From ae675487890e002c90e25b25741707dadea4580b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Tue, 25 Jun 2019 17:41:10 +0200
Subject: [PATCH] misc: allow page picture to be svg (#34334)

---
 combo/data/models.py                         |  5 +++
 combo/manager/fields.py                      | 38 ++++++++++++++++++++
 combo/manager/forms.py                       |  5 +++
 combo/manager/static/css/combo.manager.css   |  1 +
 combo/manager/templates/combo/page_view.html | 10 ++++--
 tests/test_manager.py                        | 11 ++++++
 6 files changed, 67 insertions(+), 3 deletions(-)
 create mode 100644 combo/manager/fields.py

diff --git a/combo/data/models.py b/combo/data/models.py
index 298dca46..968cd567 100644
--- a/combo/data/models.py
+++ b/combo/data/models.py
@@ -162,6 +162,11 @@ class Page(models.Model):
     def natural_key(self):
         return (self.get_online_url().strip('/'), )
 
+    def picture_extension(self):
+        if not self.picture:
+            return None
+        return os.path.splitext(self.picture.name)[-1]
+
     def save(self, *args, **kwargs):
         if not self.id:
             self.related_cells = {'cell_types': []}
diff --git a/combo/manager/fields.py b/combo/manager/fields.py
new file mode 100644
index 00000000..384bd721
--- /dev/null
+++ b/combo/manager/fields.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+#
+# combo - content management system
+# Copyright (C) 2015-2019  Entr'ouvert
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+from django.core import validators
+from django import forms
+
+
+def get_available_image_extensions():
+    return validators.get_available_image_extensions() + ['svg']
+
+validate_image_file_extension = validators.FileExtensionValidator(
+    allowed_extensions=get_available_image_extensions(),
+)
+
+class ImageIncludingSvgField(forms.ImageField):
+    default_validators = [validate_image_file_extension]
+
+    def to_python(self, data):
+        if data.name and data.name.endswith('.svg'):
+            # bypass image field Pillow check
+            return super(forms.ImageField, self).to_python(data)
+        return super(ImageIncludingSvgField, self).to_python(data)
diff --git a/combo/manager/forms.py b/combo/manager/forms.py
index bbfd4820..1767e659 100644
--- a/combo/manager/forms.py
+++ b/combo/manager/forms.py
@@ -23,6 +23,8 @@ from django.utils.translation import ugettext_lazy as _
 
 from combo.data.models import Page
 
+from .fields import ImageIncludingSvgField
+
 
 class PageEditTitleForm(forms.ModelForm):
     class Meta:
@@ -53,6 +55,9 @@ class PageEditPictureForm(forms.ModelForm):
     class Meta:
         model = Page
         fields = ('picture',)
+        field_classes = {
+            'picture': ImageIncludingSvgField
+        }
 
 
 class PageVisibilityForm(forms.ModelForm):
diff --git a/combo/manager/static/css/combo.manager.css b/combo/manager/static/css/combo.manager.css
index e54884a5..99d49e7d 100644
--- a/combo/manager/static/css/combo.manager.css
+++ b/combo/manager/static/css/combo.manager.css
@@ -374,6 +374,7 @@ span.extra-info {
 
 img.page-picture {
 	max-width: 100%;
+	max-height: 250px;
 }
 
 span.error {
diff --git a/combo/manager/templates/combo/page_view.html b/combo/manager/templates/combo/page_view.html
index ec3585fe..83646bce 100644
--- a/combo/manager/templates/combo/page_view.html
+++ b/combo/manager/templates/combo/page_view.html
@@ -70,10 +70,14 @@
 <p>
 <label>{% trans 'Picture:' %}</label>
 {% if object.picture %}
-{% thumbnail object.picture "320x240" crop="50% 25%" as im %}
-<img class="page-picture" src="{{im.url}}"/>
+  {% if object.picture_extension != '.svg' %}
+    {% thumbnail object.picture "320x240" crop="50% 25%" as im %}
+    <img class="page-picture" src="{{im.url}}"/>
+    {% endthumbnail %}
+  {% else %}
+    <img class="page-picture" src="{{page.picture.url}}"/>
+  {% endif %}
 (<a href="{% url 'combo-manager-page-remove-picture' pk=object.id %}">{% trans 'remove' %}</a>)
-{% endthumbnail %}
 {% else %}<i>{% trans 'none' %}</i>{% endif %}
 (<a rel="popup" href="{% url 'combo-manager-page-edit-picture' pk=object.id %}">{% trans 'change' %}</a>)
 </p>
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 8aafd611..e9748e63 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -170,11 +170,22 @@ def test_page_edit_picture(app, admin_user):
     resp = resp.follow()
     assert '<h2>Page - One</h2>' in resp.text
     assert '<img' in resp.text
+    assert 'src="/media/cache/' in resp.text
     resp = resp.click(href='.*/remove-picture/')
     resp = resp.follow()
     assert '<h2>Page - One</h2>' in resp.text
     assert not '<img' in resp.text
 
+    resp = resp.click(href='.*/picture/')
+    resp.form['picture'] = Upload('black.svg',
+            '<svg xmlns="http://www.w3.org/2000/svg"/>', 'image/svg+xml')
+    resp = resp.form.submit()
+    assert resp.location.endswith('/manage/pages/%s/' % page.id)
+    resp = resp.follow()
+    assert '<h2>Page - One</h2>' in resp.text
+    assert '<img' in resp.text
+    assert Page.objects.get(id=page.id).picture.url in resp.text
+
 def test_delete_page(app, admin_user):
     Page.objects.all().delete()
     page = Page(title='One', slug='one', template_name='standard')
-- 
2.20.1

