Commits

Luke Plant  committed 89d121e Merge

Merged from default

  • Participants
  • Parent commits 7ecfdbf, 68a22ae
  • Branches live

Comments (0)

Files changed (8)

File cciw/cciwmain/static/js/cciwutils.js

                 // or any other URL that isn't scheme relative or absolute i.e relative.
                 !(/^(\/\/|http:|https:).*/.test(url));
         }
-        if (sameOrigin(settings.url)) {
+        function safeMethod(method) {
+            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
+        }
+
+        if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
             xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
         }
     });

File cciw/officers/admin.py

         self._force_user_val(request)
         self._update_timestamp(request)
 
-    # Officers do not even have 'officers.add_application' permission
-    # - this is to prevent them adding things via the normal interface.
-    # So we special case things in the permission methods
-
-    def add_view(self, request):
-        if request.method == "POST":
-            self._force_post_vals(request)
-
-        return super(ApplicationAdmin, self).add_view(request)
-
     def change_view(self, request, obj_id):
         if request.method == "POST":
             self._force_post_vals(request)
 
         return super(ApplicationAdmin, self).change_view(request, obj_id)
 
-    def has_add_permission(self, request):
-        if request.user is not None and request.user.groups.filter(name='Officers').exists():
-            return True
-        else:
-            return super(ApplicationAdmin, self).has_add_permission(request)
-
     def has_change_permission(self, request, obj=None):
         # Normal users do not have change permission, unless they are editing
         # their own object.  For officers, this method will return False when
             response["Location"] = location
         return response
 
-    def response_add(self, request, new_object):
-        resp = super(ApplicationAdmin, self).response_add(request, new_object)
-        return self._redirect(request, resp)
-
     def response_change(self, request, new_object):
         resp = super(ApplicationAdmin, self).response_change(request, new_object)
         return self._redirect(request, resp)
     def save_model(self, request, obj, form, change):
         from cciw.officers import email
         super(ApplicationAdmin, self).save_model(request, obj, form, change)
+        if obj.finished and obj.officer == request.user:
+            # We clear out any unfinished application forms, as they will just
+            # confuse the officer in future.  It is possible for an admin to be
+            # editing an old form of their own, while a new form of their own is
+            # still unfinished. So we filter on date_submitted.  If
+            # date_submitted is NULL, the form has never been saved, so its fine
+            # to delete.
+            old = obj.officer.application_set.filter(finished=False)
+            old = old.filter(date_submitted__isnull=True) | old.filter(date_submitted__lt=obj.date_submitted)
+            old.delete()
         email.send_application_emails(request, obj)
 
 

File cciw/officers/tests/applicationform.py

 class ApplicationFormView(TwillMixin, TestCase):
     fixtures = ['basic.json', 'officers_users.json']
 
-    def _application_add_url(self):
-        return make_django_url('admin:officers_application_add')
-
     def _application_edit_url(self, app_id):
         return make_django_url('admin:officers_application_change', app_id)
 
     def _get_email_change_emails(self):
         return [e for e in mail.outbox if "E-mail change" in e.subject]
 
-    def test_add_application(self):
-        self._twill_login(OFFICER)
-        tc.go(self._application_add_url())
-        tc.code(200)
-        tc.find('Save and continue editing')
-        tc.notfind('Save and add another')
-        u = User.objects.get(username=OFFICER[0])
-        self.assertEqual(u.application_set.count(), 0)
-        tc.formvalue('1', 'full_name', 'Test full name')
-        tc.submit('_save')
-        tc.url(reverse("cciw.officers.views.applications"))
-        self.assertEqual(u.application_set.count(), 1)
-        self.assertEqual(u.application_set.all()[0].full_name, 'Test full name')
-
-    def test_add_application_leader(self):
-        # Test that we don't get an error if a leader is using it, and forgets
-        # to do fill out the 'officer' box.
-        u = User.objects.get(username=LEADER[0])
-        self.assertEqual(u.application_set.count(), 0)
-        self._twill_login(LEADER)
-        tc.go(self._application_add_url())
-        tc.code(200)
-        tc.formvalue('1', 'full_name', 'Test full name')
-        tc.submit('_save')
-        tc.url(reverse("cciw.officers.views.applications"))
-        self.assertEqual(u.application_set.count(), 1)
-        self.assertEqual(u.application_set.all()[0].full_name, 'Test full name')
-
     def test_change_application(self):
         self._twill_login(OFFICER)
         a = self._add_application()
         u = User.objects.get(username=OFFICER[0])
         self.assertEqual(u.application_set.count(), 0)
         self._twill_login(OFFICER)
-        tc.go(self._application_add_url())
+        a = self._add_application()
+        tc.go(self._application_edit_url(a.id))
         url = tc.browser.get_url()
         tc.code(200)
         tc.formvalue('1', 'finished', 'on')
         tc.url(url)
         tc.find("Please correct the errors below")
         tc.find("form-row errors address")
-        self.assertEqual(u.application_set.count(), 0) # shouldn't have been saved
+        self.assertEqual(u.application_set.exclude(date_submitted__isnull=True).count(), 0) # shouldn't have been saved
 
     def test_finish_complete(self):
         u = User.objects.get(username=OFFICER[0])
         self.assertEqual(u.application_set.count(), 0)
         self.assertEqual(len(mail.outbox), 0)
         self._twill_login(OFFICER)
-        tc.go(self._application_add_url())
+        # An old, unfinished application form
+        a_old = self._add_application()
+        a = self._add_application()
+        tc.go(self._application_edit_url(a.id))
         tc.code(200)
         self._finish_application_form()
 
         tc.submit('_save')
         tc.url(reverse("cciw.officers.views.applications"))
 
-        self.assertEqual(u.application_set.count(), 1)
+        apps = list(u.application_set.all())
+        # The old one should have been deleted.
+        self.assertEqual(len(apps), 1)
+        self.assertEqual(a.id, apps[0].id)
 
         # There should be two emails in outbox, one to officer, one to
         # leader.  This assumes that there is a leader for the camp,
         a1 = self._add_application()
         a1.date_submitted = datetime.date.today()
         a1.save()
-        tc.go(self._application_add_url())
+        a2 = self._add_application()
+        tc.go(self._application_edit_url(a2.id))
         self._finish_application_form()
         tc.submit('_save')
         tc.find("You've already submitted")
         u = User.objects.get(username=OFFICER[0])
-        self.assertEqual(u.application_set.count(), 1)
-
-    def test_initial_form(self):
-        """
-        Ensure that name fields are filled out initially when we can do so.
-        """
-        u = User.objects.get(username=OFFICER[0])
-
-        self._twill_login(OFFICER)
-        tc.go(self._application_add_url())
-        tc.find("%s %s" % (u.first_name, u.last_name))
-        tc.find(u.email)
+        self.assertEqual(u.application_set.exclude(date_submitted__isnull=True).count(), 1)
 
     def test_application_differences_email(self):
         """
         app0.save()
 
         # Create another application
-        tc.go(self._application_add_url())
+        app1 = self._add_application()
+        tc.go(self._application_edit_url(app1.id))
         self._finish_application_form()
         # Now change some values
         tc.formvalue('1', 'full_name', 'New Full Name')

File cciw/officers/tests/applications.py

     fixtures = ['basic.json', 'officers_users.json']
 
     _create_button = """<input type="submit" name="new" value="Create" """
-    _edit_button = """<input type="submit" name="edit" value="Edit" """
+    _edit_button = """<input type="submit" name="edit" value="Continue" """
 
     def setUp(self):
         self.client.login(username=OFFICER_USERNAME, password=OFFICER_PASSWORD)

File cciw/officers/views.py

     """Displays a list of tasks related to applications."""
     user = request.user
     c = {}
+
     finished_applications = user.application_set\
         .filter(finished=True)\
         .order_by('-date_submitted')
+    # A NULL date_submitted means they never pressed save, so there is no point
+    # re-editing, so we ignore them.
     unfinished_applications = user.application_set\
         .filter(finished=False)\
+        .exclude(date_submitted__isnull=True)\
         .order_by('-date_submitted')
     has_thisyears_app = thisyears_applications(user).exists()
     has_completed_app = thisyears_applications(user).filter(finished=True).exists()
     c['has_thisyears_app'] = has_thisyears_app
     c['has_completed_app'] = has_completed_app
 
-    if request.POST.has_key('edit'):
-        # Edit existing application
-        id = request.POST.get('edit_application', None)
-        if id is not None:
-            return HttpResponseRedirect('/admin/officers/application/%s/' % id)
+    if not has_completed_app and unfinished_applications and request.POST.has_key('edit'):
+        # Edit existing application.
+        # It should now only be possible for there to be one unfinished
+        # application, so we just continue with the most recent.
+        return HttpResponseRedirect(
+            reverse("admin:officers_application_change",
+                    args=(unfinished_applications[0].id,)))
     elif not has_thisyears_app and request.POST.has_key('new'):
         # Create new application based on old one
-        obj = None
-        try:
-            obj = finished_applications[0]
-        except IndexError:
-            # should never get here
-            obj = None
-        if obj is not None:
-            # Create a copy
-            new_obj = _copy_application(obj)
+        if finished_applications:
+            new_obj = _copy_application(finished_applications[0])
             new_obj.save()
-            return HttpResponseRedirect('/admin/officers/application/%s/' % \
-                                            new_obj.id)
+        else:
+            new_obj = Application.objects.create(officer=user,
+                                                 full_name=u"%s %s" % (user.first_name, user.last_name))
 
-    elif request.POST.has_key('delete'):
-        # Delete an unfinished application
-        pass
+        return HttpResponseRedirect('/admin/officers/application/%s/' %
+                                    new_obj.id)
 
     return render(request, 'cciw/officers/applications.html', c)
 
 @camp_admin_required
 @json_response
 def officer_details(request):
-    user = User.objects.get(pk=int(request.GET['officer_id']))
+    # We use POST here, to avoid information leaks associated with JSON over GET
+    # by 3rd party <script> tags.
+    user = User.objects.get(pk=int(request.POST['officer_id']))
     return {'username': user.username,
             'first_name': user.first_name,
             'last_name': user.last_name,

File templates/cciw/officers/applications.html

 
 <form action="" method="post">{% csrf_token %}
 
-<ul>
+
 {% if unfinished_applications %}
-<li>Continue filling in an application:
-<p>The following applications are unfinished and you may continue to edit them.</p>
-<div>
-<label for="edit_application">Choose application:</label>&nbsp;
-	<select name="edit_application" id="edit_application">
-	{% for app in unfinished_applications %}
-	        <option value="{{ app.id }}">Submitted {{ app.date_submitted|date:"Y-m-d" }}</option>
-	{% endfor %}
-	</select>
-	<input type="submit" name="edit" value="Edit" />
-</div>
-<br/><br/>
-</li>
-{% endif %}
-
-{% if finished_applications and not has_thisyears_app %}
-<li>Create new application based on your previous one:<br/>
-  <input type="submit" name="new" value="Create" />
-<br/><br/>
-<p>Once you have pressed 'Create', you will need to update any information necessary, including
-your address, and re-check the declarations.</p>
-</li>
+<p>Continue filling in an application:<br/>
+  <input type="submit" name="edit" value="Continue" />
+</p>
 
 {% endif %}
 
 {% if not has_thisyears_app %}
-<li><a href="/admin/officers/application/add/">Create new application
-    (blank)</a></li>
+  {% if finished_applications %}
+<p>Create new application based on your previous one:<br/>
+  <input type="submit" name="new" value="Create" />
+</p>
+
+<p>Once you have pressed 'Create', you will need to update any
+  information necessary, including your address, and re-check the
+  declarations.</p>
+
+  {% else %}
+<p>Start application form:<br/>
+  <input type="submit" name="new" value="Start" />
+</p>
+  {% endif %}
 {% endif %}
 
-</ul>
-
 
 <h2>Help filling in the form</h2>
 <p>You can save your work at any time (using the 'Save' button at the bottom)

File templates/cciw/officers/create_officer.html

     <p>If you are sure that your officer is a different person
       from the above, press 'Confirm' to add them to the system.
      </p>
+
+    <p class="actionnote"><b>If the person has just changed their e-mail address, DO
+        NOT press confirm and add a new officer to the system.
+        Instead you should add the officer to your list, then edit
+        their e-mail address.</b></p>
+
     <p><input type="submit" name="confirm" value="Confirm" /></p>
 
   {% endif %}

File templates/cciw/officers/officer_list.html

             var officerId = parseFloat(ev.target.id.substring(8), 10);
             // Need to retrieve details (we don't know first name and last name)
             $.ajax({
-                type: "GET",
-                url: "{% url 'cciw.officers.views.officer_details' %}?officer_id=" + officerId.toString() + "&" + Math.random().toString(),
+                type: "POST",
+                url: "{% url 'cciw.officers.views.officer_details' %}",
+                data: {officer_id: officerId.toString()},
                 dataType: 'json',
                 success: function(officer) {
                     var escape = function(t) {