diff --git a/wagtail/wagtailadmin/static_src/wagtailadmin/scss/components/_listing.scss b/wagtail/wagtailadmin/static_src/wagtailadmin/scss/components/_listing.scss index 674ebad79e..d8eee1d5b2 100644 --- a/wagtail/wagtailadmin/static_src/wagtailadmin/scss/components/_listing.scss +++ b/wagtail/wagtailadmin/static_src/wagtailadmin/scss/components/_listing.scss @@ -689,3 +689,28 @@ td.ord { margin-top: -28px; } } + +table.listing { + th.ordered { + color: $color-teal; + + &.ascending { + &:before { + content: '\25B2'; // up arrow + display: inline-block; + height: 100%; + vertical-align: middle; + } + } + + &.descending { + &:before { + content: '\25BC'; // down arrow + display: inline-block; + height: 100%; + vertical-align: middle; + } + } + + } +} diff --git a/wagtail/wagtailforms/models.py b/wagtail/wagtailforms/models.py index b0c6d9bf87..364781df3d 100644 --- a/wagtail/wagtailforms/models.py +++ b/wagtail/wagtailforms/models.py @@ -212,6 +212,33 @@ class AbstractForm(Page): def get_landing_page_template(self, request, *args, **kwargs): return self.landing_page_template + def get_field_ordering(self, ordering_list, default=('submit_time', 'descending')): + """ + Accepts a list of strings ['-submit_time', 'id'] + Returns a list of tuples [(field_name, 'ascending'/'descending'), ...] + Intented to be used to process ordering via request.GET.getlist('order_by') + Checks if the field options are valid, only returns valid, de-duplicated options + invalid options are simply ignored - no error created + """ + valid_fields = ['id', 'submit_time'] + field_ordering = [] + if len(ordering_list) == 0: + return [default] + for ordering in ordering_list: + try: + none, prefix, field_name = ordering.rpartition('-') + if field_name not in valid_fields: + continue # Invalid field_name, skip it + # only add to ordering if the field is not already set + if field_name not in [order[0] for order in field_ordering]: + asc_desc = 'ascending' + if prefix == '-': + asc_desc = 'descending' + field_ordering.append((field_name, asc_desc)) + except (IndexError, ValueError): + continue # Invalid ordering specified, skip it + return field_ordering + def get_submission_class(self): """ Returns submission class. diff --git a/wagtail/wagtailforms/templates/wagtailforms/list_submissions.html b/wagtail/wagtailforms/templates/wagtailforms/list_submissions.html index d534964e08..854b170cfb 100644 --- a/wagtail/wagtailforms/templates/wagtailforms/list_submissions.html +++ b/wagtail/wagtailforms/templates/wagtailforms/list_submissions.html @@ -6,14 +6,16 @@ - + - {% for heading in data_headings %} - {{ heading }} + {% for heading in data_fields_with_ordering %} + + {% if heading.order %}{{ heading.label }}{% else %}{{ heading.label }}{% endif %} + {% endfor %} diff --git a/wagtail/wagtailforms/tests/test_views.py b/wagtail/wagtailforms/tests/test_views.py index 6cac4edf4d..386f0f5c57 100644 --- a/wagtail/wagtailforms/tests/test_views.py +++ b/wagtail/wagtailforms/tests/test_views.py @@ -300,6 +300,33 @@ class TestFormsSubmissionsList(TestCase, WagtailTestUtils): # Check that we got the last page self.assertEqual(response.context['submissions'].number, response.context['submissions'].paginator.num_pages) + def test_list_submissions_default_order(self): + response = self.client.get(reverse( + 'wagtailforms:list_submissions', args=(self.form_page.id,))) + # check default ordering, most recent responses first + first_row_values = response.context['data_rows'][0]['fields'] + self.assertTrue('this is a fairly new message' in first_row_values) + + def test_list_submissions_url_params_ordering_recent_first(self): + response = self.client.get(reverse( + 'wagtailforms:list_submissions', + args=(self.form_page.id,)), + {'order_by': '-submit_time'} + ) + # check ordering matches '-submit_time' (most recent first) + first_row_values = response.context['data_rows'][0]['fields'] + self.assertTrue('this is a fairly new message' in first_row_values) + + def test_list_submissions_url_params_ordering_oldest_first(self): + response = self.client.get(reverse( + 'wagtailforms:list_submissions', + args=(self.form_page.id,)), + {'order_by': 'submit_time'} + ) + # check ordering matches 'submit_time' (oldest first) + first_row_values = response.context['data_rows'][0]['fields'] + self.assertTrue('this is a really old message' in first_row_values) + class TestFormsSubmissionsExport(TestCase, WagtailTestUtils): def setUp(self): @@ -673,7 +700,7 @@ class TestCustomFormsSubmissionsList(TestCase, WagtailTestUtils): self.assertEqual(len(response.context['data_rows']), 2) # CustomFormPageSubmission have custom field. This field should appear in the listing - self.assertContains(response, 'Username', html=True) + self.assertContains(response, 'Username', html=True) self.assertContains(response, 'user-m1kola', html=True) self.assertContains(response, 'user-john', html=True) @@ -688,7 +715,7 @@ class TestCustomFormsSubmissionsList(TestCase, WagtailTestUtils): self.assertEqual(len(response.context['data_rows']), 1) # CustomFormPageSubmission have custom field. This field should appear in the listing - self.assertContains(response, 'Username', html=True) + self.assertContains(response, 'Username', html=True) self.assertContains(response, 'user-m1kola', html=True) def test_list_submissions_filtering_date_to(self): @@ -702,7 +729,7 @@ class TestCustomFormsSubmissionsList(TestCase, WagtailTestUtils): self.assertEqual(len(response.context['data_rows']), 1) # CustomFormPageSubmission have custom field. This field should appear in the listing - self.assertContains(response, 'Username', html=True) + self.assertContains(response, 'Username', html=True) self.assertContains(response, 'user-john', html=True) def test_list_submissions_filtering_range(self): @@ -717,7 +744,7 @@ class TestCustomFormsSubmissionsList(TestCase, WagtailTestUtils): self.assertEqual(len(response.context['data_rows']), 1) # CustomFormPageSubmission have custom field. This field should appear in the listing - self.assertContains(response, 'Username', html=True) + self.assertContains(response, 'Username', html=True) self.assertContains(response, 'user-m1kola', html=True) def test_list_submissions_pagination(self): @@ -733,7 +760,7 @@ class TestCustomFormsSubmissionsList(TestCase, WagtailTestUtils): self.assertEqual(response.context['submissions'].number, 2) # CustomFormPageSubmission have custom field. This field should appear in the listing - self.assertContains(response, 'Username', html=True) + self.assertContains(response, 'Username', html=True) self.assertContains(response, 'generated-username-', count=20) def test_list_submissions_pagination_invalid(self): diff --git a/wagtail/wagtailforms/views.py b/wagtail/wagtailforms/views.py index c75fdd767d..046c8a43e3 100644 --- a/wagtail/wagtailforms/views.py +++ b/wagtail/wagtailforms/views.py @@ -68,7 +68,30 @@ def list_submissions(request, page_id): data_fields = form_page.get_data_fields() - submissions = form_submission_class.objects.filter(page=form_page).order_by('submit_time') + ordering = form_page.get_field_ordering(request.GET.getlist('order_by')) + + # convert ordering tuples to a list of strings like ['-submit_time'] + ordering_strings = [ + '%s%s' % ('-' if order_str[1] == 'descending' else '', order_str[0]) + for order_str in ordering] + + if request.GET.get('action') == 'CSV': + # Revert to CSV being sorted submit_time ascending for backwards compatibility + submissions = form_submission_class.objects.filter(page=form_page).order_by('submit_time') + else: + submissions = form_submission_class.objects.filter(page=form_page).order_by(*ordering_strings) + + data_fields_with_ordering = [] + for name, label in data_fields: + order = None + for order_value in [o[1] for o in ordering if o[0] == name]: + order = order_value + data_fields_with_ordering.append({ + "name": name, + "label": label, + "order": order, + }) + data_headings = [label for name, label in data_fields] select_date_form = SelectDateForm(request.GET) @@ -127,6 +150,6 @@ def list_submissions(request, page_id): 'form_page': form_page, 'select_date_form': select_date_form, 'submissions': submissions, - 'data_headings': data_headings, + 'data_fields_with_ordering': data_fields_with_ordering, 'data_rows': data_rows })