diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 022670216..30cb3b650 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -15,13 +15,16 @@ jobs:
django-3.1.txt,
django-3.2.txt,
django-4.0.txt,
- django-4.1.txt
+ django-4.1.txt,
+ django-4.2.txt,
]
exclude:
- python-version: 3.7
requirements-file: django-4.0.txt
- python-version: 3.7
requirements-file: django-4.1.txt
+ - python-version: 3.7
+ requirements-file: django-4.2.txt
- python-version: 3.9
requirements-file: django-2.2.txt
- python-version: 3.10
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 4f3da7048..9d64545b8 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -2,6 +2,11 @@
CHANGELOG
=========
+Unreleased
+==========
+* Add Django 4.2 support
+* fix tests
+
2.2.6 (2023-07-03)
==================
diff --git a/README.rst b/README.rst
index 8e22ed370..7b495328d 100644
--- a/README.rst
+++ b/README.rst
@@ -7,6 +7,9 @@ Django Filer
**django Filer** is a file management application for django that makes
handling of files and images a breeze.
+.. warning::
+ django-filer 2.x is susceptible to SVG XSS attacks and we strongly recommend upgrading it to 3.x.
+
.. note::
This project is endorsed by the `django CMS Association `_.
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
diff --git a/filer/__init__.py b/filer/__init__.py
index 0401c529e..ed363ac3b 100644
--- a/filer/__init__.py
+++ b/filer/__init__.py
@@ -14,5 +14,3 @@
"""
__version__ = '2.2.6'
-
-default_app_config = 'filer.apps.FilerConfig'
diff --git a/filer/admin/clipboardadmin.py b/filer/admin/clipboardadmin.py
index 3a727a4a5..9c2d23bb3 100644
--- a/filer/admin/clipboardadmin.py
+++ b/filer/admin/clipboardadmin.py
@@ -1,7 +1,7 @@
from django.contrib import admin
from django.forms.models import modelform_factory
from django.http import JsonResponse
-from django.urls import re_path
+from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from .. import settings as filer_settings
@@ -35,21 +35,21 @@ class ClipboardAdmin(admin.ModelAdmin):
def get_urls(self):
return [
- re_path(r'^operations/paste_clipboard_to_folder/$',
- self.admin_site.admin_view(views.paste_clipboard_to_folder),
- name='filer-paste_clipboard_to_folder'),
- re_path(r'^operations/discard_clipboard/$',
- self.admin_site.admin_view(views.discard_clipboard),
- name='filer-discard_clipboard'),
- re_path(r'^operations/delete_clipboard/$',
- self.admin_site.admin_view(views.delete_clipboard),
- name='filer-delete_clipboard'),
- re_path(r'^operations/upload/(?P[0-9]+)/$',
- ajax_upload,
- name='filer-ajax_upload'),
- re_path(r'^operations/upload/no_folder/$',
- ajax_upload,
- name='filer-ajax_upload'),
+ path('operations/paste_clipboard_to_folder/',
+ self.admin_site.admin_view(views.paste_clipboard_to_folder),
+ name='filer-paste_clipboard_to_folder'),
+ path('operations/discard_clipboard/',
+ self.admin_site.admin_view(views.discard_clipboard),
+ name='filer-discard_clipboard'),
+ path('operations/delete_clipboard/',
+ self.admin_site.admin_view(views.delete_clipboard),
+ name='filer-delete_clipboard'),
+ path('operations/upload//',
+ ajax_upload,
+ name='filer-ajax_upload'),
+ path('operations/upload/no_folder/',
+ ajax_upload,
+ name='filer-ajax_upload'),
] + super().get_urls()
def get_model_perms(self, *args, **kwargs):
@@ -123,7 +123,7 @@ def ajax_upload(request, folder_id=None):
'file_id': file_obj.pk,
}
# prepare preview thumbnail
- if type(file_obj) == Image:
+ if isinstance(file_obj, Image):
thumbnail_180_options = {
'size': (180, 180),
'crop': True,
diff --git a/filer/admin/folderadmin.py b/filer/admin/folderadmin.py
index 5e93cc62b..08dfba06a 100644
--- a/filer/admin/folderadmin.py
+++ b/filer/admin/folderadmin.py
@@ -15,7 +15,7 @@
from django.db import models, router
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
-from django.urls import re_path, reverse
+from django.urls import path, reverse
from django.utils.encoding import force_str
from django.utils.html import escape, format_html
from django.utils.safestring import mark_safe
@@ -200,35 +200,35 @@ def get_urls(self):
return [
# we override the default list view with our own directory listing
# of the root directories
- re_path(r'^$',
- self.admin_site.admin_view(self.directory_listing),
- name='filer-directory_listing-root'),
-
- re_path(r'^last/$',
- self.admin_site.admin_view(self.directory_listing),
- {'viewtype': 'last'},
- name='filer-directory_listing-last'),
-
- re_path(r'^(?P\d+)/list/$',
- self.admin_site.admin_view(self.directory_listing),
- name='filer-directory_listing'),
-
- re_path(r'^(?P\d+)/make_folder/$',
- self.admin_site.admin_view(views.make_folder),
- name='filer-directory_listing-make_folder'),
- re_path(r'^make_folder/$',
- self.admin_site.admin_view(views.make_folder),
- name='filer-directory_listing-make_root_folder'),
-
- re_path(r'^images_with_missing_data/$',
- self.admin_site.admin_view(self.directory_listing),
- {'viewtype': 'images_with_missing_data'},
- name='filer-directory_listing-images_with_missing_data'),
-
- re_path(r'^unfiled_images/$',
- self.admin_site.admin_view(self.directory_listing),
- {'viewtype': 'unfiled_images'},
- name='filer-directory_listing-unfiled_images'),
+ path('',
+ self.admin_site.admin_view(self.directory_listing),
+ name='filer-directory_listing-root'),
+
+ path('last/',
+ self.admin_site.admin_view(self.directory_listing),
+ {'viewtype': 'last'},
+ name='filer-directory_listing-last'),
+
+ path('/list/',
+ self.admin_site.admin_view(self.directory_listing),
+ name='filer-directory_listing'),
+
+ path('/make_folder/',
+ self.admin_site.admin_view(views.make_folder),
+ name='filer-directory_listing-make_folder'),
+ path('make_folder/',
+ self.admin_site.admin_view(views.make_folder),
+ name='filer-directory_listing-make_root_folder'),
+
+ path('images_with_missing_data/',
+ self.admin_site.admin_view(self.directory_listing),
+ {'viewtype': 'images_with_missing_data'},
+ name='filer-directory_listing-images_with_missing_data'),
+
+ path('unfiled_images/',
+ self.admin_site.admin_view(self.directory_listing),
+ {'viewtype': 'unfiled_images'},
+ name='filer-directory_listing-unfiled_images'),
] + super().get_urls()
# custom views
diff --git a/filer/utils/files.py b/filer/utils/files.py
index 8bbffbf3d..fe360d7f1 100644
--- a/filer/utils/files.py
+++ b/filer/utils/files.py
@@ -22,7 +22,7 @@ def handle_upload(request):
filename = request.GET.get('qqfile', False) or request.GET.get('filename', False) or ''
try:
- content_length = int(request.META['CONTENT_LENGTH'])
+ content_length = int(request.headers['content-length'])
except (IndexError, TypeError, ValueError):
content_length = None
diff --git a/setup.py b/setup.py
index 2601510d8..0fee49cd2 100644
--- a/setup.py
+++ b/setup.py
@@ -32,6 +32,7 @@
'Framework :: Django :: 3.2',
'Framework :: Django :: 4.0',
'Framework :: Django :: 4.1',
+ 'Framework :: Django :: 4.2',
'Framework :: Django CMS',
'Framework :: Django CMS :: 3.6',
'Framework :: Django CMS :: 3.7',
diff --git a/tests/requirements/base.txt b/tests/requirements/base.txt
index 9a0be2f19..2ff68a7dc 100644
--- a/tests/requirements/base.txt
+++ b/tests/requirements/base.txt
@@ -6,3 +6,4 @@ django-mptt
coverage
isort
flake8
+packaging
\ No newline at end of file
diff --git a/tests/requirements/django-4.2.txt b/tests/requirements/django-4.2.txt
new file mode 100644
index 000000000..192e81265
--- /dev/null
+++ b/tests/requirements/django-4.2.txt
@@ -0,0 +1,8 @@
+-r base.txt
+
+django-app-helper>=3.3.1
+
+django>=4.2,<5
+django_polymorphic>=3.1
+django-app-helper
+#https://github.com/jrief/django-app-helper/archive/refs/heads/develop.zip
\ No newline at end of file
diff --git a/tests/utils/custom_image/migrations/0004_alter_image_file_ptr.py b/tests/utils/custom_image/migrations/0004_alter_image_file_ptr.py
new file mode 100644
index 000000000..eca5e335a
--- /dev/null
+++ b/tests/utils/custom_image/migrations/0004_alter_image_file_ptr.py
@@ -0,0 +1,20 @@
+# Generated by Django 4.1.9 on 2023-06-13 20:22
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('filer', '0015_alter_file_owner_alter_file_polymorphic_ctype_and_more'),
+ ('custom_image', '0003_auto_20180414_2059'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='image',
+ name='file_ptr',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='%(app_label)s_%(class)s_file', serialize=False, to='filer.file'),
+ ),
+ ]
diff --git a/tests/utils/test_app/admin.py b/tests/utils/test_app/admin.py
index f1c73ed51..2b6b6b44d 100644
--- a/tests/utils/test_app/admin.py
+++ b/tests/utils/test_app/admin.py
@@ -3,8 +3,6 @@
from .models import MyModel
+@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
model = MyModel
-
-
-admin.site.register(MyModel, MyModelAdmin)
diff --git a/tests/utils/urls.py b/tests/utils/urls.py
index 882eca68d..888e0b671 100644
--- a/tests/utils/urls.py
+++ b/tests/utils/urls.py
@@ -1,6 +1,6 @@
from django.conf import settings
from django.contrib import admin
-from django.urls import include, re_path
+from django.urls import include, path, re_path
from django.views.static import serve
@@ -11,6 +11,6 @@
re_path(r'^media/(?P.*)$', serve,
{'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
re_path(r'^admin/', admin_urls),
- re_path(r'^', include('filer.server.urls')),
- re_path(r'^filer/', include('filer.urls')),
+ path('', include('filer.server.urls')),
+ path('filer/', include('filer.urls')),
]
diff --git a/tox.ini b/tox.ini
index 27f4bc2ff..1d868b739 100644
--- a/tox.ini
+++ b/tox.ini
@@ -10,6 +10,7 @@ envlist =
py{36,37,38,39}-dj{30,31}-noswap
py{36,37,38,39,310}-dj32-swap
py{36,37,38,39,310}-dj32-noswap
+ py{38,39,310,311}-dj{40,41,42}-{swap,noswap}
[gh-actions]
python =
@@ -18,20 +19,26 @@ python =
3.8: py38
3.9: py39
3.10: py310
+ 3.11: py311
skip_missing_interpreters=True
[testenv]
deps =
- dj22: -r tests/requirements/django-2.2.txt
+dj22: -r tests/requirements/django-2.2.txt
dj30: -r tests/requirements/django-3.0.txt
dj31: -r tests/requirements/django-3.1.txt
dj32: -r tests/requirements/django-3.2.txt
+ dj40: -r tests/requirements/django-4.0.txt
+ dj41: -r tests/requirements/django-4.1.txt
+ dj42: -r tests/requirements/django-4.2.txt
commands =
{envpython} --version
{env:COMMAND:coverage} erase
{env:COMMAND:coverage} run setup.py test
{env:COMMAND:coverage} report
+allowlist_externals =
+ {env:COMMAND:coverage}
setenv =
swap: CUSTOM_IMAGE=custom_image.Image