From 2c0af0e554c3a2e94e47af2ae3d652e2a9081e77 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 12:54:58 +0000 Subject: [PATCH 1/8] ci: pre-commit autoupdate (#1435) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.4.0 → v4.5.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.4.0...v4.5.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 89ba6d406..9fbfeca42 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -31,7 +31,7 @@ repos: - id: yesqa - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-merge-conflict - id: mixed-line-ending From 8039f5517a6933135b15ab073a0658f6bd164e33 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Wed, 8 Nov 2023 08:33:45 +0100 Subject: [PATCH 2/8] fix: Linting issue in test_admin.py --- tests/test_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_admin.py b/tests/test_admin.py index 5647d098f..ce5a23ad1 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -348,7 +348,7 @@ def test_image_expand_view(self): self.assertContains( response, - f"""""" + f"""""" ) From 2e37d581f1507a0c02e5f2067046d6203ddc4cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20St=C3=A4hli?= Date: Wed, 8 Nov 2023 08:34:56 +0100 Subject: [PATCH 3/8] feat: Image dimensions update management command (#1434) * first draft: image dimensions update managemenet command * fix: import corrected * fix: enable for bitmap images * fix: self>image * test ok * catch file not fonund and corrupt images * fix flake8 and silly error * add svg test * add svg test --- filer/management/commands/filer_check.py | 49 +++++++++++++ tests/test_filer_check.py | 91 +++++++++++++++++++++++- 2 files changed, 139 insertions(+), 1 deletion(-) diff --git a/filer/management/commands/filer_check.py b/filer/management/commands/filer_check.py index 3a2de61b3..3eb77d12b 100644 --- a/filer/management/commands/filer_check.py +++ b/filer/management/commands/filer_check.py @@ -4,6 +4,8 @@ from django.core.management.base import BaseCommand from django.utils.module_loading import import_string +from PIL import UnidentifiedImageError + from filer import settings as filer_settings @@ -41,6 +43,13 @@ def add_arguments(self, parser): default=False, help="Delete references in database if files are missing in media folder.", ) + parser.add_argument( + '--image-dimensions', + action='store_true', + dest='image_dimensions', + default=False, + help="Look for images without dimensions set, set them accordingly.", + ) parser.add_argument( '--noinput', '--no-input', @@ -72,6 +81,8 @@ def handle(self, *args, **options): self.stdout.write("Aborted: Delete orphaned files from storage.") return self.verify_storages(options) + if options['image_dimensions']: + self.image_dimensions(options) def verify_references(self, options): from filer.models.filemodels import File @@ -112,3 +123,41 @@ def walk(prefix): filer_public = filer_settings.FILER_STORAGES['public']['main'] storage = import_string(filer_public['ENGINE'])() walk(filer_public['UPLOAD_TO_PREFIX']) + + def image_dimensions(self, options): + from django.db.models import Q + + import easy_thumbnails + from easy_thumbnails.VIL import Image as VILImage + + from filer.models.imagemodels import Image + from filer.utils.compatibility import PILImage + + no_dimensions = Image.objects.filter( + Q(_width=0) | Q(_width__isnull=True) + ) + self.stdout.write(f"trying to set dimensions on {no_dimensions.count()} files") + for image in no_dimensions: + if image.file_ptr: + file_holder = image.file_ptr + else: + file_holder = image + try: + imgfile = file_holder.file + imgfile.seek(0) + except (FileNotFoundError): + pass + else: + if image.file.name.lower().endswith('.svg'): + with VILImage.load(imgfile) as vil_image: + # invalid svg doesnt throw errors + image._width, image._height = vil_image.size + else: + try: + with PILImage.open(imgfile) as pil_image: + image._width, image._height = pil_image.size + image._transparent = easy_thumbnails.utils.is_transparent(pil_image) + except UnidentifiedImageError: + continue + image.save() + return diff --git a/tests/test_filer_check.py b/tests/test_filer_check.py index 01b951ca0..f18bd389c 100644 --- a/tests/test_filer_check.py +++ b/tests/test_filer_check.py @@ -1,6 +1,6 @@ import os import shutil -from io import StringIO +from io import BytesIO, StringIO from django.core.files.uploadedfile import SimpleUploadedFile from django.core.management import call_command @@ -9,10 +9,21 @@ from filer import settings as filer_settings from filer.models.filemodels import File +from filer.models.imagemodels import Image from tests.helpers import create_image class FilerCheckTestCase(TestCase): + + svg_file_string = """ + + + + {} + """ + def setUp(self): # ensure that filer_public directory is empty from previous tests storage = import_string(filer_settings.FILER_STORAGES['public']['main']['ENGINE'])() @@ -67,3 +78,81 @@ def test_delete_orphans(self): call_command('filer_check', delete_orphans=True, interactive=False, verbosity=0) self.assertFalse(os.path.exists(orphan_file)) + + def test_image_dimensions_corrupted_file(self): + original_filename = 'testimage.jpg' + file_obj = SimpleUploadedFile( + name=original_filename, + # corrupted! + content=create_image().tobytes(), + content_type='image/jpeg') + self.filer_image = Image.objects.create( + file=file_obj, + original_filename=original_filename) + + self.filer_image._width = 0 + self.filer_image.save() + call_command('filer_check', image_dimensions=True) + + def test_image_dimensions_file_not_found(self): + self.filer_image = Image.objects.create( + file="123.jpg", + original_filename="123.jpg") + call_command('filer_check', image_dimensions=True) + self.filer_image.refresh_from_db() + + def test_image_dimensions(self): + + original_filename = 'testimage.jpg' + with BytesIO() as jpg: + create_image().save(jpg, format='JPEG') + jpg.seek(0) + file_obj = SimpleUploadedFile( + name=original_filename, + content=jpg.read(), + content_type='image/jpeg') + self.filer_image = Image.objects.create( + file=file_obj, + original_filename=original_filename) + + self.filer_image._width = 0 + self.filer_image.save() + + call_command('filer_check', image_dimensions=True) + self.filer_image.refresh_from_db() + self.assertGreater(self.filer_image._width, 0) + + def test_image_dimensions_invalid_svg(self): + + original_filename = 'test.svg' + svg_file = bytes("" + self.svg_file_string, "utf-8") + file_obj = SimpleUploadedFile( + name=original_filename, + content=svg_file, + content_type='image/svg+xml') + self.filer_image = Image.objects.create( + file=file_obj, + original_filename=original_filename) + + self.filer_image._width = 0 + self.filer_image.save() + call_command('filer_check', image_dimensions=True) + + def test_image_dimensions_svg(self): + + original_filename = 'test.svg' + svg_file = bytes(self.svg_file_string, "utf-8") + file_obj = SimpleUploadedFile( + name=original_filename, + content=svg_file, + content_type='image/svg+xml') + self.filer_image = Image.objects.create( + file=file_obj, + original_filename=original_filename) + + self.filer_image._width = 0 + self.filer_image.save() + + call_command('filer_check', image_dimensions=True) + self.filer_image.refresh_from_db() + self.assertGreater(self.filer_image._width, 0) From 41b969d5493a8739bfdd919f387d9da04bf52ca8 Mon Sep 17 00:00:00 2001 From: Marco Bonetti Date: Fri, 10 Nov 2023 10:42:41 +0100 Subject: [PATCH 4/8] fix: address failing gulp ci jobs. Fixes #1436 (#1437) --- filer/static/filer/js/addons/dropzone.init.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/filer/static/filer/js/addons/dropzone.init.js b/filer/static/filer/js/addons/dropzone.init.js index d3c7ff67c..fefe90105 100644 --- a/filer/static/filer/js/addons/dropzone.init.js +++ b/filer/static/filer/js/addons/dropzone.init.js @@ -163,7 +163,8 @@ djQuery(function ($) { // Handle initialization of the dropzone on dynamic formsets (i.e. Django admin inlines) $(document).on('formset:added', function (ev, row) { - if(ev.detail && ev.detail.formsetName) { + var dropzones, rowIdx, row_; + if (ev.detail && ev.detail.formsetName) { /* Django 4.1 changed the event type being fired when adding a new formset from a jQuery to a vanilla JavaScript event. @@ -172,16 +173,17 @@ djQuery(function ($) { In this case we find the newly added row and initialize the dropzone on any dropzoneSelector on that row. */ - let rowIdx = parseInt( + + rowIdx = parseInt( document.getElementById( 'id_' + event.detail.formsetName + '-TOTAL_FORMS' ).value, 10 ) - 1; - let row_ = document.getElementById(event.detail.formsetName + '-' + rowIdx); - var dropzones = $(row_).find(dropzoneSelector) + row_ = document.getElementById(event.detail.formsetName + '-' + rowIdx); + dropzones = $(row_).find(dropzoneSelector); } else { - var dropzones = $(row).find(dropzoneSelector); + dropzones = $(row).find(dropzoneSelector); } dropzones.each(createDropzone); From 032c5959631bcde21b85dded71c6e1d8e4f8bcc7 Mon Sep 17 00:00:00 2001 From: Dennis Schwertel Date: Fri, 17 Nov 2023 13:03:19 +0100 Subject: [PATCH 5/8] fix: Added compatibility code in aldryn_config go support setting THUMBNAIL_DEFAULT_STORAGE in django 4.2 (#1440) --- aldryn_config.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/aldryn_config.py b/aldryn_config.py index 145fe1ded..3572d4ec2 100644 --- a/aldryn_config.py +++ b/aldryn_config.py @@ -47,7 +47,12 @@ def to_settings(self, data, settings): # If the DEFAULT_FILE_STORAGE has been set to a value known by # aldryn-django, then use that as THUMBNAIL_DEFAULT_STORAGE as well. for storage_backend in storage.SCHEMES.values(): - if storage_backend == settings['DEFAULT_FILE_STORAGE']: + # Process before django 4.2 + if storage_backend == settings.get('DEFAULT_FILE_STORAGE', None): + settings['THUMBNAIL_DEFAULT_STORAGE'] = storage_backend + break + # Process django 4.2 and after + if storage_backend == settings.get('STORAGES', {}).get('default', {}).get('BACKEND', None): settings['THUMBNAIL_DEFAULT_STORAGE'] = storage_backend break return settings From 1c6a6b420daad99ea9493b28ea0e0ca4a1ec6644 Mon Sep 17 00:00:00 2001 From: Mark Walker Date: Sat, 18 Nov 2023 00:37:22 +0000 Subject: [PATCH 6/8] ci: Switch to pypi trusted publisher --- .github/workflows/publish-to-live-pypi.yml | 18 ++++++++++-------- .github/workflows/publish-to-test-pypi.yml | 21 ++++++++++++--------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/.github/workflows/publish-to-live-pypi.yml b/.github/workflows/publish-to-live-pypi.yml index 1607f7724..928076bef 100644 --- a/.github/workflows/publish-to-live-pypi.yml +++ b/.github/workflows/publish-to-live-pypi.yml @@ -9,12 +9,17 @@ jobs: build-n-publish: name: Build and publish Python 🐍 distributions 📦 to pypi runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/django-filer + permissions: + id-token: write steps: - - uses: actions/checkout@master - - name: Set up Python 3.9 - uses: actions/setup-python@v1 + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: '3.12' - name: Install pypa/build run: >- @@ -33,7 +38,4 @@ jobs: - name: Publish distribution 📦 to PyPI if: startsWith(github.ref, 'refs/tags') - uses: pypa/gh-action-pypi-publish@master - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/publish-to-test-pypi.yml b/.github/workflows/publish-to-test-pypi.yml index d590f480e..bf43b73e6 100644 --- a/.github/workflows/publish-to-test-pypi.yml +++ b/.github/workflows/publish-to-test-pypi.yml @@ -9,12 +9,17 @@ jobs: build-n-publish: name: Build and publish Python 🐍 distributions 📦 to TestPyPI runs-on: ubuntu-latest + environment: + name: test + url: https://test.pypi.org/p/django-filer + permissions: + id-token: write steps: - - uses: actions/checkout@master - - name: Set up Python 3.9 - uses: actions/setup-python@v1 + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: '3.12' - name: Install pypa/build run: >- @@ -32,9 +37,7 @@ jobs: . - name: Publish distribution 📦 to Test PyPI - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@release/v1 with: - user: __token__ - password: ${{ secrets.TEST_PYPI_API_TOKEN }} - repository_url: https://test.pypi.org/legacy/ - skip_existing: true + repository-url: https://test.pypi.org/legacy/ + skip-existing: true From 7aa5c07ad78a0bc1df399b48ba756f6555a2720f Mon Sep 17 00:00:00 2001 From: Mark Walker Date: Sat, 18 Nov 2023 00:38:06 +0000 Subject: [PATCH 7/8] release: 3.1.1 --- CHANGELOG.rst | 8 ++++++++ filer/__init__.py | 2 +- setup.py | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ce4ec4140..cb2f3909e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,14 @@ CHANGELOG ========= +3.1.1 (2023-11-18) +================== + +* fix: Added compatibility code in aldryn_config go support setting THUMBNAIL_DEFAULT_STORAGE in django 4.2 +* fix: address failing gulp ci jobs +* feat: Image dimensions update management command +* ci: pre-commit autoupdate + 3.1.0 (2023-10-01) ================== diff --git a/filer/__init__.py b/filer/__init__.py index 839815895..badb62cbd 100644 --- a/filer/__init__.py +++ b/filer/__init__.py @@ -13,4 +13,4 @@ 8. Publish the release and it will automatically release to pypi """ -__version__ = '3.1.0' +__version__ = '3.1.1' diff --git a/setup.py b/setup.py index 69deb6952..d7142e61e 100644 --- a/setup.py +++ b/setup.py @@ -42,6 +42,7 @@ 'Framework :: Django CMS :: 3.11', 'Framework :: Django CMS :: 4.0', 'Framework :: Django CMS :: 4.1', + 'Framework :: Django CMS :: 4.2', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development', From b0f39ab39da19bd382cf9975e733dd0b1abe9299 Mon Sep 17 00:00:00 2001 From: Mark Walker Date: Sat, 18 Nov 2023 00:41:42 +0000 Subject: [PATCH 8/8] fix: Remove classifier --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index d7142e61e..69deb6952 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,6 @@ 'Framework :: Django CMS :: 3.11', 'Framework :: Django CMS :: 4.0', 'Framework :: Django CMS :: 4.1', - 'Framework :: Django CMS :: 4.2', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development',