Browse Source

Add ability to filter image index by a tag

- Fixed error when searching and filtering by tag at the same time
Benedikt Willi 5 years ago
parent
commit
233e7f5189

+ 1 - 0
CHANGELOG.txt

@@ -17,6 +17,7 @@ Changelog
  * Cleaned up Django docs URLs in documentation (Pete Andrew)
  * Add StreamFieldPanel to available panel types in documentation (Dan Swain)
  * Add {{ block.super }} example to ModelAdmin customisation in documentation (Dan Swain)
+ * Add ability to filter image index by a tag (Benedikt Willi)
  * Fix: Rename documents listing column 'uploaded' to 'created' (LB (Ben Johnston))
  * Fix: Submenu items longer then the page height are no longer broken by the submenu footer (Igor van Spengen)
  * Fix: Unbundle the l18n library as it was bundled to avoid installation errors which have been resolved (Matt Westcott)

+ 1 - 0
CONTRIBUTORS.rst

@@ -425,6 +425,7 @@ Contributors
 * Tim Gates
 * Timothy Bautista
 * Pete Andrew
+* Benedikt Willi
 
 Translators
 ===========

+ 5 - 1
client/scss/components/_button.scss

@@ -106,7 +106,7 @@
     }
 
     &.bicolor {
-        border: 0;
+        border: 1px solid transparent;
         padding-left: 3.5em;
 
         &:before {
@@ -123,6 +123,10 @@
             border-top-left-radius: inherit;
             border-bottom-left-radius: inherit;
         }
+
+        &.button-secondary {
+            border: 1px solid rgba(0, 0, 0, 0.2);
+        }
     }
 
     &.button-small.bicolor {

+ 27 - 3
client/scss/components/_tag.scss

@@ -29,9 +29,33 @@ a.tag:hover {
 .taglist {
     font-size: 0.9em;
     line-height: 2.4em;
+}
+
+.tagfilter {
+    legend {
+        @include visuallyvisible;
+
+        @include media-breakpoint-up(sm) {
+            @include column(2);
+            padding-left: 0;
+        }
+
+        font-weight: 700;
+        color: #333;
+        font-size: 1.1em;
+        display: block;
+        padding: 0 0 0.8em;
+    }
+
+    a {
+        font-size: 0.9em;
+    }
+
+    .button.bicolor.icon-cross {
+        padding-left: 2em;
 
-    h3 {
-        display: inline;
-        margin-right: 1em;
+        &:before {
+            background-color: transparent;
+        }
     }
 }

+ 2 - 2
client/scss/tools/_mixins.general.scss

@@ -81,10 +81,10 @@
 
 
 @mixin visuallyvisible {
-    clip: none;
+    clip: auto;
     height: auto;
     width: auto;
-    margin: auto;
+    margin: initial;
     overflow: visible;
     position: initial;
 }

+ 1 - 0
docs/releases/2.8.rst

@@ -26,6 +26,7 @@ Other features
  * Cleaned up Django docs URLs in documentation (Pete Andrew)
  * Add StreamFieldPanel to available panel types in documentation (Dan Swain)
  * Add {{ block.super }} example to ModelAdmin customisation in documentation (Dan Swain)
+ * Add ability to filter image index by a tag (Benedikt Willi)
 
 
 Bug fixes

+ 27 - 6
wagtail/images/templates/wagtailimages/images/index.html

@@ -1,4 +1,5 @@
 {% extends "wagtailadmin/base.html" %}
+{% load wagtailadmin_tags %}
 {% load wagtailimages_tags %}
 {% load i18n %}
 
@@ -31,13 +32,33 @@
     {% endif %}
 
     <div class="nice-padding">
-        {% if collections %}
-            <form class="image-search search-bar" action="{% url 'wagtailimages:index' %}" method="GET" novalidate>
-                <ul class="fields">
+        <form class="image-search search-bar" action="{% url 'wagtailimages:index' %}" method="GET" novalidate>
+            <ul class="fields">
+                {% if collections %}
                     {% include "wagtailadmin/shared/collection_chooser.html" %}
-                </ul>
-            </form>
-        {% endif %}
+                    {% if current_tag %}
+                        <input type="hidden" name="tag" value="{{ current_tag }}" />
+                    {% endif %}
+                {% endif %}
+                {% if popular_tags %}
+                <li>
+                    <fieldset class="tagfilter">
+                    <legend>{% trans 'Popular Tags:' %}</legend>
+                      {% for tag in popular_tags %}
+                          {% if tag.name != current_tag %}
+                              <a class="button button-small button-secondary bicolor icon icon-tag" href="{% url 'wagtailimages:index' %}{% querystring tag=tag.name %}">{{ tag.name }}</a>
+                          {% else %}
+                              <a class="button button-small bicolor icon icon-tag" href="{% url 'wagtailimages:index' %}{% querystring tag=tag.name %}">{{ tag.name }}</a>
+                          {% endif %}
+                      {% endfor %}
+                      {% if current_tag %}
+                          <a class="button button-small bicolor button-secondary icon icon-cross" href="{% url 'wagtailimages:index' %}{% querystring tag='' %}">{% trans 'Clear choice' %}</a>
+                      {% endif %}
+                    </fieldset>
+                </li>
+                {% endif %}
+            </ul>
+        </form>
 
         <div id="image-results">
             {% include "wagtailimages/images/results.html" %}

+ 80 - 1
wagtail/images/tests/test_admin_views.py

@@ -50,7 +50,7 @@ class TestImageIndexView(TestCase, WagtailTestUtils):
         for i in range(1, 50):
             self.image = Image.objects.create(
                 title="Test image %i" % i,
-                file=get_test_image_file(),
+                file=get_test_image_file(size=(1, 1)),
                 collection=evil_plans_collection
             )
 
@@ -87,6 +87,85 @@ class TestImageIndexView(TestCase, WagtailTestUtils):
             ['Root', 'Evil plans', 'Good plans'])
 
 
+    def test_tags(self):
+        image_two_tags = Image.objects.create(
+            title="Test image with two tags",
+            file=get_test_image_file(),
+        )
+        image_two_tags.tags.add("one", "two")
+
+        response = self.get()
+        self.assertEqual(response.status_code, 200)
+
+        current_tag = response.context['current_tag']
+        self.assertIsNone(current_tag)
+
+        tags = response.context['popular_tags']
+        self.assertTrue(
+            [tag.name for tag in tags] == ["one", "two"]
+            or [tag.name for tag in tags] == ["two", "one"]
+        )
+
+
+    def test_tag_filtering(self):
+        Image.objects.create(
+            title="Test image with no tags",
+            file=get_test_image_file(),
+        )
+
+        image_one_tag = Image.objects.create(
+            title="Test image with one tag",
+            file=get_test_image_file(),
+        )
+        image_one_tag.tags.add("one")
+
+        image_two_tags = Image.objects.create(
+            title="Test image with two tags",
+            file=get_test_image_file(),
+        )
+        image_two_tags.tags.add("one", "two")
+
+        # no filtering
+        response = self.get()
+        self.assertEqual(response.context['images'].paginator.count, 3)
+
+        # filter all images with tag 'one'
+        response = self.get({'tag': 'one'})
+        self.assertEqual(response.context['images'].paginator.count, 2)
+
+        # filter all images with tag 'two'
+        response = self.get({'tag': 'two'})
+        self.assertEqual(response.context['images'].paginator.count, 1)
+
+
+    def test_tag_filtering_preserves_other_params(self):
+        for i in range(1, 100):
+            image = Image.objects.create(
+                title="Test image %i" % i,
+                file=get_test_image_file(size=(1, 1)),
+            )
+            if (i % 2 != 0):
+                image.tags.add('even')
+                image.save()
+
+
+        response = self.get({'tag': 'even', 'p': 2})
+        self.assertEqual(response.status_code, 200)
+
+        response_body = response.content.decode('utf8')
+
+        # prev link should exist and include tag
+        self.assertTrue(
+            "?p=2&amp;tag=even" in response_body
+            or "?tag=even&amp;p=1" in response_body
+        )
+        # next link should exist and include tag
+        self.assertTrue(
+            "?p=3&amp;tag=even" in response_body
+            or "?tag=even&amp;p=3" in response_body
+        )
+
+
 class TestImageAddView(TestCase, WagtailTestUtils):
     def setUp(self):
         self.login()

+ 9 - 0
wagtail/images/views/images.py

@@ -59,6 +59,14 @@ def index(request):
         except (ValueError, Collection.DoesNotExist):
             pass
 
+    # Filter by tag
+    current_tag = request.GET.get('tag')
+    if current_tag:
+        try:
+            images = images.filter(tags__name=current_tag)
+        except (AttributeError):
+            current_tag = None
+
     paginator = Paginator(images, per_page=INDEX_PAGE_SIZE)
     images = paginator.get_page(request.GET.get('p'))
 
@@ -85,6 +93,7 @@ def index(request):
 
             'search_form': form,
             'popular_tags': popular_tags_for_model(Image),
+            'current_tag': current_tag,
             'collections': collections,
             'current_collection': current_collection,
             'user_can_add': permission_policy.user_has_permission(request.user, 'add'),