Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Reduce number of thumbnails created for admin, avoid admin thumbnails for svg files #1490

Merged
merged 24 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
accd80c
Fix #1377
fsbraun Jul 7, 2023
1ec6d3b
Merge branch 'master' of github.com:django-cms/django-filer
fsbraun Jul 10, 2023
50bb9fb
Merge branch 'django-cms:master' into master
fsbraun Jul 12, 2023
f046125
Merge branch 'django-cms:master' into master
fsbraun Jul 14, 2023
d7809dd
Merge branch 'django-cms:master' into master
fsbraun Aug 1, 2023
32770ea
Merge branch 'django-cms:master' into master
fsbraun Aug 4, 2023
01c9f35
Merge branch 'django-cms:master' into master
fsbraun Aug 20, 2023
a625d80
Merge branch 'django-cms:master' into master
fsbraun Sep 6, 2023
556dacb
Merge branch 'django-cms:master' into master
fsbraun Sep 18, 2023
9ed4a06
Merge branch 'django-cms:master' into master
fsbraun Sep 27, 2023
0a534de
Merge branch 'django-cms:master' into master
fsbraun Mar 20, 2024
7f4646b
Merge branch 'django-cms:master' into master
fsbraun Apr 29, 2024
f6cadb9
Merge branch 'django-cms:master' into master
fsbraun May 17, 2024
48e16a6
Merge branch 'django-cms:master' into master
fsbraun Jul 10, 2024
7ab5565
Merge branch 'django-cms:master' into master
fsbraun Aug 9, 2024
4e209c5
Merge branch 'django-cms:master' into master
fsbraun Aug 20, 2024
96c94e4
Simplify admin thumbnails sizes to 40, 80, 160. No thumbnails for svg…
fsbraun Aug 20, 2024
793a74c
Update settings
fsbraun Aug 20, 2024
8ecdae1
Fix undefined variable add_attrs
fsbraun Aug 20, 2024
9d309e6
Dynamic css for new setting `FILER_THUMBNAIL_ICON_SIZE`
fsbraun Aug 20, 2024
2cb3d06
Replace SVG by icon if larger than 1MB (can be changed by setting)
fsbraun Aug 20, 2024
ddcbca9
Dynamic folder icon size
fsbraun Aug 21, 2024
bf1f69c
Default sizes 40, and 120px
fsbraun Aug 21, 2024
facfb94
Update folderadmin.py for better naming
fsbraun Aug 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions filer/admin/clipboardadmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
from django.core.exceptions import ValidationError
from django.forms.models import modelform_factory
from django.http import JsonResponse
from django.urls import path
from django.urls import path, reverse
from django.utils.translation import gettext_lazy as _
from django.views.decorators.csrf import csrf_exempt

from .. import settings as filer_settings
from ..models import Clipboard, ClipboardItem, Folder
from ..settings import FILER_THUMBNAIL_ICON_SIZE
from ..utils.files import handle_request_files_upload, handle_upload
from ..utils.loader import load_model
from ..validation import validate_upload
Expand Down Expand Up @@ -141,14 +142,10 @@ def ajax_upload(request, folder_id=None):
}
# prepare preview thumbnail
if isinstance(file_obj, Image):
thumbnail_180_options = {
'size': (180, 180),
'crop': True,
'upscale': True,
}
thumbnail_180 = file_obj.file.get_thumbnail(
thumbnail_180_options)
data['thumbnail_180'] = thumbnail_180.url
data['thumbnail_180'] = reverse(
f"admin:filer_{file_obj._meta.model_name}_fileicon",
args=(file_obj.pk, FILER_THUMBNAIL_ICON_SIZE),
)
data['original_image'] = file_obj.url
return JsonResponse(data)
except Exception as error:
Expand Down
13 changes: 8 additions & 5 deletions filer/admin/folderadmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
from .. import settings
from ..cache import clear_folder_permission_cache
from ..models import File, Folder, FolderPermission, FolderRoot, ImagesWithMissingData, UnsortedImages, tools
from ..settings import FILER_IMAGE_MODEL, FILER_PAGINATE_BY, TABLE_LIST_TYPE
from ..settings import FILER_IMAGE_MODEL, FILER_PAGINATE_BY, TABLE_LIST_TYPE, FILER_TABLE_ICON_SIZE, \
FILER_THUMBNAIL_ICON_SIZE
from ..thumbnail_processors import normalize_subject_location
from ..utils.compatibility import get_delete_permission
from ..utils.filer_easy_thumbnails import FilerActionThumbnailer
Expand Down Expand Up @@ -271,11 +272,13 @@ def directory_listing(self, request, folder_id=None, viewtype=None):

list_type = get_directory_listing_type(request) or settings.FILER_FOLDER_ADMIN_DEFAULT_LIST_TYPE
if list_type == TABLE_LIST_TYPE:
size = "40x40" # Prefetch thumbnails for listing
size_x2 = "80x80"
s = FILER_TABLE_ICON_SIZE # Prefetch thumbnails for table view
fsbraun marked this conversation as resolved.
Show resolved Hide resolved
size = f"{s}x{s}"
size_x2 = f"{2 * s}x{2 * s}"
else:
size = "160x160" # Prefetch thumbnails for thumbnail view
size_x2 = "320x320"
s = FILER_THUMBNAIL_ICON_SIZE # Prefetch thumbnails for thumbnail view
size = f"{s}x{s}"
size_x2 = f"{2 * s}x{2 * s}"

# Check actions to see if any are available on this changelist
actions = self.get_actions(request)
Expand Down
3 changes: 1 addition & 2 deletions filer/cache.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import typing

from django.core.cache import cache

from django.contrib.auth import get_user_model
from django.core.cache import cache


User = get_user_model()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Generated by Django 3.2.25 on 2024-08-19 14:49

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion

import filer.fields.multistorage_file
import filer.models.filemodels
import filer.models.mixins
Expand Down
4 changes: 2 additions & 2 deletions filer/models/abstract.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import logging

from django.conf import settings
from django.core.checks import Warning, register as register_check
from django.core.checks import Warning
from django.core.checks import register as register_check
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _


import easy_thumbnails.utils
from easy_thumbnails.VIL import Image as VILImage
from PIL.Image import MAX_IMAGE_PIXELS
Expand Down
2 changes: 1 addition & 1 deletion filer/models/foldermodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
from django.utils.translation import gettext_lazy as _

from .. import settings as filer_settings
from . import mixins
from ..cache import get_folder_permission_cache, update_folder_permission_cache
from . import mixins


class FolderPermissionManager(models.Manager):
Expand Down
4 changes: 0 additions & 4 deletions filer/private/sass/components/_navigator.scss
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,6 @@ body {
// removes padding to make sure that column has correct height #664
padding-top: 0 !important;
padding-bottom: 0 !important;
img {
width: 40px;
height: auto;
}
}
.column-action {
text-align: center;
Expand Down
24 changes: 13 additions & 11 deletions filer/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import os

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.module_loading import import_string as get_storage_class
from django.utils.translation import gettext_lazy as _

Expand Down Expand Up @@ -38,20 +37,24 @@

FILER_PAGINATE_BY = getattr(settings, 'FILER_PAGINATE_BY', 100)

if hasattr(settings, "FILER_ADMIN_ICON_SIZES"):
logger.warning("FILER_ADMIN_ICON_SIZES is deprecated and will be removed in the future.")

_ICON_SIZES = getattr(settings, 'FILER_ADMIN_ICON_SIZES', ('16', '32', '48', '64'))
if not _ICON_SIZES:
raise ImproperlyConfigured('Please, configure FILER_ADMIN_ICON_SIZES')
# Reliably sort by integer value, but keep icon size as string.
# (There is some code in the wild that depends on this being strings.)
FILER_ADMIN_ICON_SIZES = [str(i) for i in sorted([int(s) for s in _ICON_SIZES])]

# Filer admin templates have specific icon sizes hardcoded: 32 and 48.
_ESSENTIAL_ICON_SIZES = ('32', '48')
if not all(x in FILER_ADMIN_ICON_SIZES for x in _ESSENTIAL_ICON_SIZES):
logger.warn(
"FILER_ADMIN_ICON_SIZES has not all of the essential icon sizes "
"listed: {}. Some icons might be missing in admin templates.".format(
_ESSENTIAL_ICON_SIZES))
# Currently, these two icon sizes are hard-coded into the admin and admin templates
FILER_TABLE_ICON_SIZE = getattr(settings, "_FILER_TABLE_ICON_SIZE", 40)
FILER_THUMBNAIL_ICON_SIZE = getattr(settings, "_FILER_THUMBNAIL_ICON_SIZE", 160)
DEFERRED_THUMBNAIL_SIZES = (
FILER_TABLE_ICON_SIZE,
2 * FILER_TABLE_ICON_SIZE,
FILER_THUMBNAIL_ICON_SIZE,
2 * FILER_THUMBNAIL_ICON_SIZE,
)


# This is an ordered iterable that describes a list of
# classes that I should check for when adding files
Expand Down Expand Up @@ -283,7 +286,6 @@ def update_server_settings(settings, defaults, s, t):
},
}

DEFERRED_THUMBNAIL_SIZES = (40, 80, 160)
IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp']
IMAGE_MIME_TYPES = ['gif', 'jpeg', 'png', 'x-png', 'svg+xml', 'webp']

Expand Down
2 changes: 1 addition & 1 deletion filer/static/filer/css/admin_filer.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion filer/static/filer/css/maps/admin_filer.css.map

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
<a href="{{ file.get_admin_change_url }}{% filer_admin_context_url_params %}"
title="{% blocktrans with file.label as item_label %}Change '{{ item_label }}' details{% endblocktrans %}">
{% endif %}
{% file_icon file size="160x160" %}
{% file_icon file detail="thumbnail" %}
{% if has_change_permission or is_popup and filer_admin_context.pick_file %}
</a>
{% endif %}
Expand Down
15 changes: 13 additions & 2 deletions filer/templates/admin/filer/templatetags/file_icon.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,21 @@
{% translate 'Your browser does not support video.' %}
</video>
{% else %}
<img src="{{ icon_url }}" loading="lazy" width="{{ width }}" height="{{ height }}"{% if alt_text %} alt="{{ alt_text }}"{% endif %}{% if highres_url %} srcset="{{ icon_url }} 1x, {{ highres_url }} 2x"{% endif %}{% if sidebar_image_ratio %} data-ratio="{{ sidebar_image_ratio }}" class="js-focal-point-image"{% endif %} />
<img src="{{ icon_url }}"
loading="lazy" width="{{ width }}" height="{{ height }}"
{% if alt_text %} alt="{{ alt_text }}"{% endif %}
{% if highres_url %} srcset="{{ icon_url }} 1x, {{ highres_url }} 2x"{% endif %}
{% if sidebar_image_ratio %} data-ratio="{{ sidebar_image_ratio }}"
class="js-focal-point-image"{% endif %}
{% for key, value in add_attrs.items %} {{ key }}="{{ value }}"{% endfor %}
/>
{% endif %}
{% else %}
<img src="{{ icon_url }}" loading="lazy" width="{{ width }}" height="{{ height }}"{% if alt_text %} alt="{{ alt_text }}"{% endif %}{% if highres_url %} srcset="{{ icon_url }} 1x, {{ highres_url }} 2x"{% endif %} class="thumbnail_img" />
<img src="{{ icon_url }}" loading="lazy" width="{{ width }}" height="{{ height }}"
{% if alt_text %} alt="{{ alt_text }}"{% endif %}
{% if highres_url %} srcset="{{ icon_url }} 1x, {{ highres_url }} 2x"{% endif %} class="thumbnail_img"
{% if add_attrs %}style="{% for key, value in add_attrs.items %} {{ key }}: {{ value }};{% endfor %}"{% endif %}
/>
{% endif %}
{% if sidebar_image_ratio %}
<div class="image-preview-field">
Expand Down
2 changes: 1 addition & 1 deletion filer/templates/admin/filer/widgets/admin_file.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<span class="filerFile js-file-selector">
{% if object %}
{% if object.file.exists %}
<a href="{{ object.url }}" target="_blank">{% file_icon object size='80x80' %}</a>
<a href="{{ object.url }}" target="_blank">{% file_icon object detail=True %}</a>
&nbsp;<span class="description_text">{{ object.label }}</span>
{% else %}
{% file_icon object %}
Expand Down
21 changes: 16 additions & 5 deletions filer/templatetags/filer_admin_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
from filer import settings
from filer.admin.tools import admin_url_params, admin_url_params_encoded
from filer.models.imagemodels import BaseImage
from filer.settings import DEFERRED_THUMBNAIL_SIZES

from filer.settings import DEFERRED_THUMBNAIL_SIZES, FILER_TABLE_ICON_SIZE, FILER_THUMBNAIL_ICON_SIZE

register = Library()

Expand Down Expand Up @@ -126,6 +125,7 @@
else:
opts = {'size': (width, height), 'crop': True}
thumbnail_options = ThumbnailOptions(opts)
add_attrs = {}
# Optimize directory listing:
if width == height and width in DEFERRED_THUMBNAIL_SIZES and hasattr(file, "thumbnail_name"):
# Get name of thumbnail from easy-thumbnail
Expand All @@ -135,6 +135,11 @@
icon_url = file.file.thumbnail_storage.url(configured_name)
if mime_subtype != 'svg+xml' and file.thumbnailx2_name:
context['highres_url'] = file.file.thumbnail_storage.url(file.thumbnailx2_name)
elif mime_subtype == 'svg+xml':
icon_url = file.url
add_attrs = {
"object-fit": "cover",
}
else: # Probably does not exist, defer creation
icon_url = reverse("admin:filer_file_fileicon", args=(file.pk, width))
context['alt_text'] = file.default_alt_text
Expand All @@ -161,6 +166,8 @@
icon_url = staticfiles_storage.url('filer/icons/file-unknown.svg')
height = width # icon is a square
context.update(width=width, height=height, icon_url=icon_url)
if add_attrs:
Fixed Show fixed Hide fixed
context.update(add_attrs=add_attrs)
return context


Expand All @@ -187,16 +194,20 @@
"""
if size:
width, height = (int(s) for s in size.split('x'))
elif detail == "thumbnail":
width, height = FILER_THUMBNAIL_ICON_SIZE, FILER_THUMBNAIL_ICON_SIZE
elif detail is True:
width, height = 2 * FILER_TABLE_ICON_SIZE, 2 * FILER_TABLE_ICON_SIZE
else:
width, height = (75, 75) if detail else (40, 40)
return file_icon_context(file, detail, width, height)
width, height = FILER_TABLE_ICON_SIZE, FILER_TABLE_ICON_SIZE
return file_icon_context(file, detail is True, width, height)


@register.simple_tag
def file_icon_url(file):
# Cache since it is called repeatedly by templates
if not hasattr(file, "_file_icon_url_cache"):
context = file_icon_context(file, False, 80, 80)
context = file_icon_context(file, False, 2 * FILER_TABLE_ICON_SIZE, 2 * FILER_TABLE_ICON_SIZE)
file._file_icon_url_cache = escapejs(context.get('highres_url', context['icon_url']))
return file._file_icon_url_cache

Expand Down
2 changes: 1 addition & 1 deletion tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def tearDown(self):
def test_icon_view_sizes(self):
"""Redirects are issued for accepted thumbnail sizes and 404 otherwise"""
test_set = tuple((size, 302) for size in DEFERRED_THUMBNAIL_SIZES)
test_set += (50, 404), (90, 404), (320, 404)
test_set += (50, 404), (90, 404), (640, 404)
for size, expected_status in test_set:
url = reverse('admin:filer_file_fileicon', kwargs={
'file_id': self.file_object.pk,
Expand Down
Loading