Skip to content

Commit e939b46

Browse files
committed
fix: similar implementation for private and public files
1 parent ab02845 commit e939b46

File tree

4 files changed

+44
-86
lines changed

4 files changed

+44
-86
lines changed

filer/admin/folderadmin.py

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -376,30 +376,8 @@ def directory_listing(self, request, folder_id=None, viewtype=None):
376376
.order_by("-modified")
377377
)
378378
file_qs = file_qs.annotate(
379-
# For private files (Filer pattern)
380-
thumbnail_name_filer=Subquery(
381-
thumbnail_qs.filter(name__contains=f"__{size}_").values_list("name")[:1]
382-
),
383-
thumbnailx2_name_filer=Subquery(
384-
thumbnail_qs.filter(name__contains=f"__{size_x2}_").values_list("name")[
385-
:1
386-
]
387-
),
388-
# For public files (easy_thumbnails pattern)
389-
thumbnail_name_easy=Subquery(
390-
thumbnail_qs.filter(name__contains=f".{size}_q85_crop").values_list(
391-
"name"
392-
)[:1]
393-
),
394-
thumbnailx2_name_easy=Subquery(
395-
thumbnail_qs.filter(name__contains=f".{size_x2}_q85_crop").values_list(
396-
"name"
397-
)[:1]
398-
),
399-
thumbnail_name=Coalesce("thumbnail_name_filer", "thumbnail_name_easy"),
400-
thumbnailx2_name=Coalesce(
401-
"thumbnailx2_name_filer", "thumbnailx2_name_easy"
402-
),
379+
thumbnail_name=Subquery(thumbnail_qs.filter(name__contains=f".{size}_").values_list("name")[:1]),
380+
thumbnailx2_name=Subquery(thumbnail_qs.filter(name__contains=f".{size_x2}_").values_list("name")[:1])
403381
).select_related("owner")
404382

405383
try:

filer/utils/filer_easy_thumbnails.py

Lines changed: 27 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33

44
from easy_thumbnails.files import Thumbnailer
55

6-
7-
# match the source filename using `__` as the seperator. ``opts_and_ext`` is non
8-
# greedy so it should match the last occurence of `__`.
9-
# in ``ThumbnailerNameMixin.get_thumbnail_name`` we ensure that there is no `__`
10-
# in the opts part.
11-
RE_ORIGINAL_FILENAME = re.compile(r"^(?P<source_filename>.*)__(?P<opts_and_ext>.*?)$")
6+
# easy-thumbnails default pattern
7+
# e.g: source.jpg.100x100_q80_crop_upscale.jpg
8+
RE_ORIGINAL_FILENAME = re.compile(
9+
r"^(?P<source_filename>.*?)\.(?P<opts_and_ext>[^.]+\.[^.]+)$"
10+
)
1211

1312

1413
def thumbnail_to_original_filename(thumbnail_name):
@@ -19,69 +18,42 @@ def thumbnail_to_original_filename(thumbnail_name):
1918

2019

2120
class ThumbnailerNameMixin:
22-
thumbnail_basedir = ''
23-
thumbnail_subdir = ''
24-
thumbnail_prefix = ''
21+
thumbnail_basedir = ""
22+
thumbnail_subdir = ""
23+
thumbnail_prefix = ""
2524

2625
def get_thumbnail_name(self, thumbnail_options, transparent=False):
2726
"""
28-
A version of ``Thumbnailer.get_thumbnail_name`` that produces a
29-
reproducible thumbnail name that can be converted back to the original
30-
filename. For public files, it uses easy_thumbnails default naming.
27+
Get thumbnail name using easy-thumbnails pattern.
28+
For public files: Uses configurable naming via THUMBNAIL_NAMER
29+
For private files: Uses easy-thumbnails default naming pattern regardless of THUMBNAIL_NAMER
3130
"""
3231
is_public = False
3332
if hasattr(self, "thumbnail_storage"):
3433
is_public = "PrivateFileSystemStorage" not in str(
3534
self.thumbnail_storage.__class__
3635
)
3736

38-
if is_public:
39-
return super(ThumbnailerNameMixin, self).get_thumbnail_name(
40-
thumbnail_options, transparent
41-
)
42-
4337
path, source_filename = os.path.split(self.name)
44-
source_extension = os.path.splitext(source_filename)[1][1:].lower()
45-
preserve_extensions = self.thumbnail_preserve_extensions
46-
if preserve_extensions is True or source_extension == 'svg' or \
47-
isinstance(preserve_extensions, (list, tuple)) and source_extension in preserve_extensions:
48-
extension = source_extension
49-
elif transparent:
50-
extension = self.thumbnail_transparency_extension
51-
else:
52-
extension = self.thumbnail_extension
53-
extension = extension or 'jpg'
54-
55-
thumbnail_options = thumbnail_options.copy()
56-
size = tuple(thumbnail_options.pop('size'))
57-
initial_opts = ['{}x{}'.format(*size)]
58-
quality = thumbnail_options.pop('quality', self.thumbnail_quality)
59-
if extension == 'jpg':
60-
initial_opts.append(f'q{quality}')
61-
elif extension == 'svg':
62-
thumbnail_options.pop('subsampling', None)
63-
thumbnail_options.pop('upscale', None)
64-
65-
opts = list(thumbnail_options.items())
66-
opts.sort() # Sort the options so the file name is consistent.
67-
opts = ['{}'.format(v is not True and f'{k}-{v}' or k)
68-
for k, v in opts if v]
69-
all_opts = '_'.join(initial_opts + opts)
70-
71-
basedir = self.thumbnail_basedir
72-
subdir = self.thumbnail_subdir
73-
74-
# make sure our magic delimiter is not used in all_opts
75-
all_opts = all_opts.replace('__', '_')
76-
filename = f'{source_filename}__{all_opts}.{extension}'
38+
thumbnail_name = super(ThumbnailerNameMixin, self).get_thumbnail_name(
39+
thumbnail_options, transparent
40+
)
41+
if is_public:
42+
return thumbnail_name
7743

78-
return os.path.join(basedir, path, subdir, filename)
44+
base_thumb_name = os.path.basename(thumbnail_name)
45+
return os.path.join(
46+
self.thumbnail_basedir,
47+
path,
48+
self.thumbnail_subdir,
49+
f"{source_filename}.{base_thumb_name}",
50+
)
7951

8052

8153
class ActionThumbnailerMixin:
82-
thumbnail_basedir = ''
83-
thumbnail_subdir = ''
84-
thumbnail_prefix = ''
54+
thumbnail_basedir = ""
55+
thumbnail_subdir = ""
56+
thumbnail_prefix = ""
8557

8658
def get_thumbnail_name(self, thumbnail_options, transparent=False):
8759
"""
@@ -101,7 +73,7 @@ def thumbnail_exists(self, thumbnail_name):
10173

10274
class FilerThumbnailer(ThumbnailerNameMixin, Thumbnailer):
10375
def __init__(self, *args, **kwargs):
104-
self.thumbnail_basedir = kwargs.pop('thumbnail_basedir', '')
76+
self.thumbnail_basedir = kwargs.pop("thumbnail_basedir", "")
10577
super().__init__(*args, **kwargs)
10678

10779

tests/test_models.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,13 @@ def tearDown(self):
3535
for f in File.objects.all():
3636
f.delete()
3737

38-
def create_filer_image(self, owner=None, is_public=True):
38+
def create_filer_image(self, owner=None):
3939
if owner is None:
4040
owner = self.superuser
4141
file_obj = DjangoFile(open(self.filename, 'rb'), name=self.image_name)
4242
image = Image.objects.create(owner=owner,
4343
original_filename=self.image_name,
44-
file=file_obj,
45-
is_public=is_public)
44+
file=file_obj)
4645
return image
4746

4847
def test_create_folder_structure(self):
@@ -81,14 +80,14 @@ def test_create_clipboard_item(self):
8180
self.assertEqual(Clipboard.objects.count(), 1)
8281

8382
def test_create_icons(self):
84-
image = self.create_filer_image(is_public=False)
83+
image = self.create_filer_image()
8584
image.save()
8685
icons = image.icons
8786
file_basename = os.path.basename(image.file.path)
8887
self.assertEqual(len(icons), len(filer_settings.FILER_ADMIN_ICON_SIZES))
8988
for size in filer_settings.FILER_ADMIN_ICON_SIZES:
9089
self.assertEqual(os.path.basename(icons[size]),
91-
file_basename + '__{}x{}_q85_crop_subsampling-2_upscale.jpg'.format(size, size))
90+
file_basename + '.{}x{}_q85_crop_upscale.jpg'.format(size, size))
9291

9392
def test_access_icons_property(self):
9493
"""Test IconsMixin that calls static on a non-existent file"""

tests/test_thumbnails.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@ def test_thumbnailer_class_for_public_files(self):
4545
image = self.create_filer_image(is_public=True)
4646
thumbnailer = image.easy_thumbnails_thumbnailer
4747
name = thumbnailer.get_thumbnail_name({"size": (100, 100)})
48-
self.assertNotIn("__", name)
48+
self.assertRegex(name, r"^.*\..*\.[^.]+$")
4949

5050
def test_thumbnailer_class_for_private_files(self):
5151
image = self.create_filer_image(is_public=False)
5252
thumbnailer = image.easy_thumbnails_thumbnailer
5353
name = thumbnailer.get_thumbnail_name({"size": (100, 100)})
54-
self.assertIn("__", name)
54+
self.assertRegex(name, r"^.*\..*\.[^.]+$")
5555

5656
@override_settings(THUMBNAIL_NAMER="tests.test_thumbnails.custom_namer")
5757
def test_thumbnail_custom_namer(self):
@@ -60,3 +60,12 @@ def test_thumbnail_custom_namer(self):
6060
name = thumbnailer.get_thumbnail_name({"size": (100, 100)})
6161
filename = os.path.basename(name)
6262
self.assertTrue(filename.startswith("custom_prefix_"))
63+
64+
@override_settings(THUMBNAIL_NAMER="tests.test_thumbnails.custom_namer")
65+
def test_private_thumbnail_ignores_custom_namer(self):
66+
image = self.create_filer_image(is_public=False)
67+
thumbnailer = image.easy_thumbnails_thumbnailer
68+
name = thumbnailer.get_thumbnail_name({"size": (100, 100)})
69+
filename = os.path.basename(name)
70+
self.assertFalse(filename.startswith("custom_prefix_"))
71+
self.assertRegex(name, r"^.*\..*\.[^.]+$")

0 commit comments

Comments
 (0)