From 507192f5be15b517f8c2d9719841d4b4bb0b56d1 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Fri, 4 May 2018 16:20:48 +0100 Subject: [PATCH] Added file_hash field to Image --- .../images/migrations/0021_image_file_hash.py | 18 +++++++++++++++ wagtail/images/models.py | 23 +++++++++++++++++-- wagtail/images/tests/test_admin_views.py | 15 +++++++++--- wagtail/images/views/chooser.py | 7 ++++++ wagtail/images/views/images.py | 8 +++++++ .../migrations/0032_auto_20180505_0008.py | 23 +++++++++++++++++++ 6 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 wagtail/images/migrations/0021_image_file_hash.py create mode 100644 wagtail/tests/testapp/migrations/0032_auto_20180505_0008.py diff --git a/wagtail/images/migrations/0021_image_file_hash.py b/wagtail/images/migrations/0021_image_file_hash.py new file mode 100644 index 000000000..900fcb90e --- /dev/null +++ b/wagtail/images/migrations/0021_image_file_hash.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.4 on 2018-05-04 15:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wagtailimages', '0020_add-verbose-name'), + ] + + operations = [ + migrations.AddField( + model_name='image', + name='file_hash', + field=models.CharField(blank=True, editable=False, max_length=40), + ), + ] diff --git a/wagtail/images/models.py b/wagtail/images/models.py index 5f71c3abc..c0301f75a 100644 --- a/wagtail/images/models.py +++ b/wagtail/images/models.py @@ -80,6 +80,8 @@ class AbstractImage(CollectionMember, index.Indexed, models.Model): focal_point_height = models.PositiveIntegerField(null=True, blank=True) file_size = models.PositiveIntegerField(null=True, editable=False) + # A SHA-1 hash of the file contents + file_hash = models.CharField(max_length=40, blank=True, editable=False) objects = ImageQuerySet.as_manager() @@ -110,6 +112,18 @@ class AbstractImage(CollectionMember, index.Indexed, models.Model): return self.file_size + def _set_file_hash(self, file_contents): + self.file_hash = hashlib.sha1(file_contents).hexdigest() + + def get_file_hash(self): + if self.file_hash == '': + with self.open_file() as f: + self._set_file_hash(f.read()) + + self.save(update_fields=['file_hash']) + + return self.file_hash + def get_upload_to(self, filename): folder_name = 'original_images' filename = self.file.field.storage.get_valid_name(filename) @@ -150,7 +164,7 @@ class AbstractImage(CollectionMember, index.Indexed, models.Model): return self.title @contextmanager - def get_willow_image(self): + def open_file(self): # Open file if it is closed close_file = False try: @@ -176,11 +190,16 @@ class AbstractImage(CollectionMember, index.Indexed, models.Model): image_file.seek(0) try: - yield WillowImage.open(image_file) + yield image_file finally: if close_file: image_file.close() + @contextmanager + def get_willow_image(self): + with self.open_file() as image_file: + yield WillowImage.open(image_file) + def get_rect(self): return Rect(0, 0, self.width, self.height) diff --git a/wagtail/images/tests/test_admin_views.py b/wagtail/images/tests/test_admin_views.py index a87b6a57b..bc8f7a69f 100644 --- a/wagtail/images/tests/test_admin_views.py +++ b/wagtail/images/tests/test_admin_views.py @@ -128,8 +128,9 @@ class TestImageAddView(TestCase, WagtailTestUtils): self.assertEqual(image.width, 640) self.assertEqual(image.height, 480) - # Test that the file_size field was set + # Test that the file_size/hash fields were set self.assertTrue(image.file_size) + self.assertTrue(image.file_hash) # Test that it was placed in the root collection root_collection = Collection.get_first_root_node() @@ -332,8 +333,9 @@ class TestImageEditView(TestCase, WagtailTestUtils): def test_edit_with_new_image_file(self): file_content = get_test_image_file().file.getvalue() - # Change the file size of the image + # Change the file size/hash of the image self.image.file_size = 100000 + self.image.file_hash = 'abcedf' self.image.save() response = self.post({ @@ -346,13 +348,15 @@ class TestImageEditView(TestCase, WagtailTestUtils): self.update_from_db() self.assertNotEqual(self.image.file_size, 100000) + self.assertNotEqual(self.image.file_hash, 'abcedf') @override_settings(DEFAULT_FILE_STORAGE='wagtail.tests.dummy_external_storage.DummyExternalStorage') def test_edit_with_new_image_file_and_external_storage(self): file_content = get_test_image_file().file.getvalue() - # Change the file size of the image + # Change the file size/hash of the image self.image.file_size = 100000 + self.image.file_hash = 'abcedf' self.image.save() response = self.post({ @@ -365,6 +369,7 @@ class TestImageEditView(TestCase, WagtailTestUtils): self.update_from_db() self.assertNotEqual(self.image.file_size, 100000) + self.assertNotEqual(self.image.file_hash, 'abcedf') def test_with_missing_image_file(self): self.image.file.delete(False) @@ -704,6 +709,10 @@ class TestImageChooserUploadView(TestCase, WagtailTestUtils): self.assertEqual(image.width, 640) self.assertEqual(image.height, 480) + # Test that the file_size/hash fields were set + self.assertTrue(image.file_size) + self.assertTrue(image.file_hash) + def test_upload_no_file_selected(self): response = self.client.post(reverse('wagtailimages:chooser_upload'), { 'title': "Test image", diff --git a/wagtail/images/views/chooser.py b/wagtail/images/views/chooser.py index 050606fc8..e68e79ee8 100644 --- a/wagtail/images/views/chooser.py +++ b/wagtail/images/views/chooser.py @@ -127,6 +127,13 @@ def chooser_upload(request): form = ImageForm(request.POST, request.FILES, instance=image, user=request.user) if form.is_valid(): + # Set image file size + image.file_size = image.file.size + + # Set image file hash + image.file.seek(0) + image._set_file_hash(image.file.read()) + form.save() # Reindex the image to make sure all tags are indexed diff --git a/wagtail/images/views/images.py b/wagtail/images/views/images.py index 5fcc6b5fd..76de6dd3e 100644 --- a/wagtail/images/views/images.py +++ b/wagtail/images/views/images.py @@ -101,6 +101,10 @@ def edit(request, image_id): # Set new image file size image.file_size = image.file.size + # Set new image file hash + image.file.seek(0) + image._set_file_hash(image.file.read()) + form.save() if 'file' in form.changed_data: @@ -253,6 +257,10 @@ def add(request): # Set image file size image.file_size = image.file.size + # Set image file hash + image.file.seek(0) + image._set_file_hash(image.file.read()) + form.save() # Reindex the image to make sure all tags are indexed diff --git a/wagtail/tests/testapp/migrations/0032_auto_20180505_0008.py b/wagtail/tests/testapp/migrations/0032_auto_20180505_0008.py new file mode 100644 index 000000000..2afe12ade --- /dev/null +++ b/wagtail/tests/testapp/migrations/0032_auto_20180505_0008.py @@ -0,0 +1,23 @@ +# Generated by Django 2.0.4 on 2018-05-04 15:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tests', '0031_customdocument_file_size'), + ] + + operations = [ + migrations.AddField( + model_name='customimage', + name='file_hash', + field=models.CharField(blank=True, editable=False, max_length=40), + ), + migrations.AddField( + model_name='customimagefilepath', + name='file_hash', + field=models.CharField(blank=True, editable=False, max_length=40), + ), + ]