Skip to content

Commit

Permalink
Merge pull request #49 from makinacorpus/add_setting_min_width_height
Browse files Browse the repository at this point in the history
Add settings Min attachment height/width
  • Loading branch information
LePetitTim authored Jul 6, 2022
2 parents 36ed844 + f390c44 commit 7524321
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 15 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ CHANGELOG
=======================

* Add `PAPERCLIP_MAX_BYTES_SIZE_IMAGE` max size image form
* Add `PAPERCLIP_MIN_ATTACHMENT_HEIGHT` min height image form
* Add `PAPERCLIP_MIN_ATTACHMENT_WIDTH` min width image form


2.5.1 (2022-07-05)
Expand Down
12 changes: 12 additions & 0 deletions paperclip/forms.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django import forms
from django.core.files.images import get_image_dimensions
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

Expand Down Expand Up @@ -85,8 +86,19 @@ def clean(self):

def clean_attachment_file(self):
uploaded_image = self.cleaned_data.get("attachment_file", False)
if not self.is_creation:
try:
uploaded_image.file.readline()
except FileNotFoundError:
return uploaded_image
if settings.PAPERCLIP_MAX_BYTES_SIZE_IMAGE and settings.PAPERCLIP_MAX_BYTES_SIZE_IMAGE < uploaded_image.size:
raise forms.ValidationError(_('The uploaded file is too large'))

width, height = get_image_dimensions(uploaded_image)
if settings.PAPERCLIP_MIN_ATTACHMENT_WIDTH and settings.PAPERCLIP_MIN_ATTACHMENT_WIDTH > width:
raise forms.ValidationError(_('The uploaded file is not wide enough'))
if settings.PAPERCLIP_MIN_ATTACHMENT_HEIGHT and settings.PAPERCLIP_MIN_ATTACHMENT_HEIGHT > height:
raise forms.ValidationError(_('The uploaded file is not tall enough'))
return uploaded_image

def success_url(self):
Expand Down
2 changes: 2 additions & 0 deletions paperclip/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
PAPERCLIP_LICENSE_MODEL = settings.PAPERCLIP_LICENSE_MODEL
PAPERCLIP_MAX_ATTACHMENT_WIDTH = getattr(settings, 'PAPERCLIP_MAX_ATTACHMENT_WIDTH', 1280)
PAPERCLIP_MAX_ATTACHMENT_HEIGHT = getattr(settings, 'PAPERCLIP_MAX_ATTACHMENT_HEIGHT', 1280)
PAPERCLIP_MIN_ATTACHMENT_WIDTH = getattr(settings, 'PAPERCLIP_MIN_ATTACHMENT_WIDTH', None)
PAPERCLIP_MIN_ATTACHMENT_HEIGHT = getattr(settings, 'PAPERCLIP_MIN_ATTACHMENT_HEIGHT', None)
PAPERCLIP_MAX_BYTES_SIZE_IMAGE = getattr(settings, 'PAPERCLIP_MAX_BYTES_SIZE_IMAGE', None)
PAPERCLIP_RESIZE_ATTACHMENTS_ON_UPLOAD = getattr(settings, 'PAPERCLIP_RESIZE_ATTACHMENTS_ON_UPLOAD', False)

Expand Down
2 changes: 2 additions & 0 deletions test_project/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@
PAPERCLIP_MAX_ATTACHMENT_WIDTH = 1280
PAPERCLIP_MAX_ATTACHMENT_HEIGHT = 1280
PAPERCLIP_MAX_BYTES_SIZE_IMAGE = None
PAPERCLIP_MIN_ATTACHMENT_WIDTH = None
PAPERCLIP_MIN_ATTACHMENT_HEIGHT = None
PAPERCLIP_RESIZE_ATTACHMENTS_ON_UPLOAD = False

# fix for project created django < 3.2
Expand Down
175 changes: 160 additions & 15 deletions test_project/test_app/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,20 @@ class ViewTestCase(TestCase):
def setUpTestData(cls):
cls.user = User.objects.create_user("foo_user", password="foo_password", last_name="foo lastname",
first_name="foo firstname")
object = TestObject.objects.create(name="foo object")
cls.object = TestObject.objects.create(name="foo object")
cls.filetype = get_filetype_model().objects.create(type="foo filetype")
cls.attachment = get_attachment_model().objects.create(content_object=object, filetype=cls.filetype,
attachment_file="foo_file.txt", creator=cls.user,

file = BytesIO()
file.name = 'foo_file.txt'
file.seek(0)
cls.attachment = get_attachment_model().objects.create(content_object=cls.object, filetype=cls.filetype,
attachment_file=SimpleUploadedFile(file.name,
file.read(),
content_type='text/txt'),
creator=cls.user,
author="foo author", title="foo title",
legend="foo legend", starred=True)
cls.pk = object.pk
cls.pk = cls.object.pk

def test_detail_not_logged(self):
response = self.client.get('/test_object/{pk}/'.format(pk=self.pk))
Expand All @@ -45,7 +52,7 @@ def test_detail_view(self):
response = self.client.get('/test_object/{pk}/'.format(pk=self.pk))
self.assertContains(response, "Attached files")
self.assertContains(response, "foo title")
self.assertContains(response, "foo_file.txt")
self.assertContains(response, "foo-title.txt")
self.assertContains(response, "foo legend")
self.assertContains(response, "foo author")
self.assertContains(response, "star-on.svg")
Expand All @@ -62,7 +69,8 @@ def test_add_view(self):
self.assertRedirects(response, "/foo-url/", fetch_redirect_response=False)
self.assertQuerysetEqual(get_attachment_model().objects.all(),
(f'<Attachment: foo_user attached paperclip/test_app_testobject/{self.pk}/file.txt>',
'<Attachment: foo_user attached foo_file.txt>'))
'<Attachment: foo_user attached '
f'paperclip/test_app_testobject/{self.pk}/foo-title.txt>'))

def test_update_view(self):
perm = Permission.objects.get(codename='change_attachment')
Expand All @@ -75,9 +83,33 @@ def test_update_view(self):
'title': 'test'})
self.assertRedirects(response, "/foo-url/", fetch_redirect_response=False)
self.assertQuerysetEqual(get_attachment_model().objects.all(),
('<Attachment: foo_user attached foo_file.txt>', ))
(f'<Attachment: foo_user attached '
f'paperclip/test_app_testobject/{self.pk}/foo-title.txt>',
))
response = self.client.get('/paperclip/update/{pk}/'.format(pk=self.attachment.pk))
self.assertContains(response, b'Update foo_file.txt')
self.assertContains(response, b'Update foo-title.txt')

def test_update_view_deleted_file(self):
perm = Permission.objects.get(codename='change_attachment')
self.user.user_permissions.add(perm)
self.client.login(username="foo_user", password="foo_password")
attachment = get_attachment_model().objects.create(content_object=self.object, filetype=self.filetype,
attachment_file='foo_file.txt',
creator=self.user,
author="foo author", title="foo title",
legend="foo legend", starred=True)
response = self.client.post('/paperclip/update/{pk}/'.format(pk=attachment.pk),
{'embed': 'File',
'filetype': self.filetype.pk,
'next': '/foo-url/',
'title': 'test'})
self.assertRedirects(response, "/foo-url/", fetch_redirect_response=False)
self.assertQuerysetEqual(get_attachment_model().objects.all(),
('<Attachment: foo_user attached foo_file.txt>',
'<Attachment: foo_user attached '
f'paperclip/test_app_testobject/{self.pk}/foo-title.txt>'
)
)

def test_delete_view(self):
object_attachment = TestObject.objects.create(name="foo object")
Expand All @@ -90,11 +122,13 @@ def test_delete_view(self):
self.client.login(username="foo_user", password="foo_password")
self.assertQuerysetEqual(get_attachment_model().objects.all(),
('<Attachment: foo_user attached attach.txt>',
'<Attachment: foo_user attached foo_file.txt>',))
'<Attachment: foo_user attached '
f'paperclip/test_app_testobject/{self.pk}/foo-title.txt>', ))
response = self.client.post('/paperclip/delete/{pk}/'.format(pk=attachment.pk))
self.assertRedirects(response, "/", fetch_redirect_response=False)
self.assertQuerysetEqual(get_attachment_model().objects.all(),
('<Attachment: foo_user attached foo_file.txt>', ))
('<Attachment: foo_user attached '
f'paperclip/test_app_testobject/{self.pk}/foo-title.txt>', ))

def test_delete_view_no_permission_delete(self):
user_other = User.objects.create_user("other_user", password="other_password", last_name="other lastname",
Expand All @@ -109,15 +143,17 @@ def test_delete_view_no_permission_delete(self):
self.client.login(username="foo_user", password="foo_password")
self.assertQuerysetEqual(get_attachment_model().objects.all(),
('<Attachment: other_user attached attach.txt>',
'<Attachment: foo_user attached foo_file.txt>',))
'<Attachment: foo_user attached '
f'paperclip/test_app_testobject/{self.pk}/foo-title.txt>',))
response = self.client.post('/paperclip/delete/{pk}/'.format(pk=attachment.pk))
self.assertRedirects(response, "/", fetch_redirect_response=False)

messages = list(get_messages(response.wsgi_request))
self.assertEqual(str(messages[0]), 'You are not allowed to delete this attachment.')
self.assertQuerysetEqual(get_attachment_model().objects.all(),
('<Attachment: other_user attached attach.txt>',
'<Attachment: foo_user attached foo_file.txt>', ))
'<Attachment: foo_user attached '
f'paperclip/test_app_testobject/{self.pk}/foo-title.txt>', ))

def test_star_view(self):
object_attachment = TestObject.objects.create(name="foo object")
Expand Down Expand Up @@ -146,10 +182,10 @@ def test_get_view(self):
self.assertEqual(response.json(), [{"id": 1,
"title": "foo title",
"legend": "foo legend",
"url": "/foo_file.txt",
"url": f"/paperclip/test_app_testobject/{self.pk}/foo-title.txt",
"type": "foo filetype",
"author": "foo author",
"filename": "foo_file.txt",
"filename": "foo-title.txt",
"mimetype": ["text", "plain"],
"is_image": False,
"starred": True}])
Expand Down Expand Up @@ -201,7 +237,7 @@ def test_attachment_is_resized_per_height(self):
attachment = get_attachment_model().objects.create(content_object=self.object, filetype=self.filetype,
attachment_file=self.get_big_dummy_uploaded_image(), creator=self.user, author="foo author",
title="foo title", legend="foo legend", starred=True)
attachment = get_attachment_model().objects.get(title="foo title")
attachment.refresh_from_db()
self.assertEqual((50, 100), get_image_dimensions(attachment.attachment_file))

@patch("paperclip.models.PAPERCLIP_RESIZE_ATTACHMENTS_ON_UPLOAD", True)
Expand Down Expand Up @@ -295,6 +331,115 @@ def test_attachment_is_larger_max_size(self):
)
self.assertEqual(response.status_code, 200)
self.assertEqual(get_attachment_model().objects.count(), 1)
self.assertIn(b'The uploaded file is too large', response.content)

@patch("paperclip.forms.settings.PAPERCLIP_MIN_ATTACHMENT_WIDTH", 50)
def test_attachment_is_not_wide_enough(self):
# Create attachment with small image
permission = Permission.objects.get(codename="add_attachment")
self.user.user_permissions.add(permission)
self.client.force_login(self.user)

file = BytesIO()
image = Image.new('RGBA', size=(51, 400), color=(155, 0, 0))
image.save(file, 'png')
file.name = 'big.png'
file.seek(0)
response = self.client.post(
reverse('add_attachment', kwargs={
'app_label': self.object._meta.app_label,
'model_name': self.object._meta.model_name,
'pk': self.object.pk
}),
data={
'creator': self.user,
'attachment_file': SimpleUploadedFile(file.name, file.read(), content_type='image/png'),
'filetype': self.filetype.pk,
'author': "newauthor",
'embed': 'File',
'next': f"/test_object/{self.object.pk}",
}
)
self.assertEqual(response.status_code, 302)
self.assertEqual(get_attachment_model().objects.count(), 1)

small_file = BytesIO()
small_image = Image.new('RGBA', size=(49, 400), color=(155, 0, 0))
small_image.save(small_file, 'png')
small_file.name = 'small.png'
small_file.seek(0)
response = self.client.post(
reverse('add_attachment', kwargs={
'app_label': self.object._meta.app_label,
'model_name': self.object._meta.model_name,
'pk': self.object.pk
}),
data={
'creator': self.user,
'attachment_file': SimpleUploadedFile(small_file.name, small_file.read(), content_type='image/png'),
'filetype': self.filetype.pk,
'author': "newauthor",
'embed': 'File',
'next': f"/test_object/{self.object.pk}",
}
)
self.assertEqual(response.status_code, 200)
self.assertEqual(get_attachment_model().objects.count(), 1)
self.assertIn(b'The uploaded file is not wide enough', response.content)

@patch("paperclip.forms.settings.PAPERCLIP_MIN_ATTACHMENT_HEIGHT", 50)
def test_attachment_is_not_tall_enough(self):
# Create attachment with small image
permission = Permission.objects.get(codename="add_attachment")
self.user.user_permissions.add(permission)
self.client.force_login(self.user)

file = BytesIO()
image = Image.new('RGBA', size=(400, 51), color=(155, 0, 0))
image.save(file, 'png')
file.name = 'big.png'
file.seek(0)
response = self.client.post(
reverse('add_attachment', kwargs={
'app_label': self.object._meta.app_label,
'model_name': self.object._meta.model_name,
'pk': self.object.pk
}),
data={
'creator': self.user,
'attachment_file': SimpleUploadedFile(file.name, file.read(), content_type='image/png'),
'filetype': self.filetype.pk,
'author': "newauthor",
'embed': 'File',
'next': f"/test_object/{self.object.pk}",
}
)
self.assertEqual(response.status_code, 302)
self.assertEqual(get_attachment_model().objects.count(), 1)

small_file = BytesIO()
small_image = Image.new('RGBA', size=(400, 49), color=(155, 0, 0))
small_image.save(small_file, 'png')
small_file.name = 'small.png'
small_file.seek(0)
response = self.client.post(
reverse('add_attachment', kwargs={
'app_label': self.object._meta.app_label,
'model_name': self.object._meta.model_name,
'pk': self.object.pk
}),
data={
'creator': self.user,
'attachment_file': SimpleUploadedFile(small_file.name, small_file.read(), content_type='image/png'),
'filetype': self.filetype.pk,
'author': "newauthor",
'embed': 'File',
'next': f"/test_object/{self.object.pk}",
}
)
self.assertEqual(response.status_code, 200)
self.assertEqual(get_attachment_model().objects.count(), 1)
self.assertIn(b'The uploaded file is not tall enough', response.content)


class LicenseModelTestCase(TestCase):
Expand Down

0 comments on commit 7524321

Please sign in to comment.