Skip to content

Commit df5ddbc

Browse files
committed
feat: Add thumbnail deletion and cache busting for attachments
1 parent 7cc02d6 commit df5ddbc

3 files changed

Lines changed: 96 additions & 2 deletions

File tree

src/Filesystem/Profile.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,87 @@ public function delete($file, $force = false)
170170
if(($force || $this->getConfig('delete'))) $this->filesystem()->delete($file);
171171
}
172172

173+
/**
174+
* Delete all thumbnails for a given file path.
175+
*
176+
* @param string $filePath The original file path (e.g., "images/photo.jpg")
177+
* @return array List of deleted thumbnail paths
178+
*/
179+
public function deleteThumbnails(string $filePath): array
180+
{
181+
$thumbProfileName = $this->getConfig('thumbnails');
182+
if (empty($thumbProfileName) || $thumbProfileName === false) {
183+
return [];
184+
}
185+
186+
$thumbProfile = $this->thumbProfile();
187+
$deletedPaths = [];
188+
$profileDir = $this->name;
189+
190+
try {
191+
$contents = $thumbProfile->listContents($profileDir, true);
192+
193+
foreach ($contents as $item) {
194+
if ($item['type'] !== 'file') {
195+
continue;
196+
}
197+
198+
if ($this->matchesThumbnailPath($item['path'], $filePath, $profileDir)) {
199+
try {
200+
$thumbProfile->delete($item['path'], true);
201+
$deletedPaths[] = $item['path'];
202+
} catch (\Exception $e) {
203+
// Continue with other deletions
204+
}
205+
}
206+
}
207+
} catch (\Exception $e) {
208+
// listContents might fail on some adapters (e.g., External)
209+
}
210+
211+
return $deletedPaths;
212+
}
213+
214+
/**
215+
* Check if a thumbnail path corresponds to the given original file path.
216+
*
217+
* @param string $thumbnailPath Full thumbnail path
218+
* @param string $originalPath Original file path
219+
* @param string $profileName Profile name
220+
* @return bool
221+
*/
222+
protected function matchesThumbnailPath(string $thumbnailPath, string $originalPath, string $profileName): bool
223+
{
224+
$thumbnailPath = str_replace(['/', '\\'], DS, $thumbnailPath);
225+
$originalPath = str_replace(['/', '\\'], DS, $originalPath);
226+
227+
if (strpos($thumbnailPath, $profileName . DS) !== 0) {
228+
return false;
229+
}
230+
231+
// Direct suffix match
232+
if (substr($thumbnailPath, -strlen($originalPath)) === $originalPath) {
233+
return true;
234+
}
235+
236+
// WebP conversion match
237+
$thumbExt = strtolower(pathinfo($thumbnailPath, PATHINFO_EXTENSION));
238+
$origExt = strtolower(pathinfo($originalPath, PATHINFO_EXTENSION));
239+
240+
if ($thumbExt === 'webp' && in_array($origExt, ['jpg', 'jpeg', 'png'])) {
241+
$originalWithoutExt = pathinfo($originalPath, PATHINFO_DIRNAME) . DS .
242+
pathinfo($originalPath, PATHINFO_FILENAME);
243+
$thumbWithoutExt = pathinfo($thumbnailPath, PATHINFO_DIRNAME) . DS .
244+
pathinfo($thumbnailPath, PATHINFO_FILENAME);
245+
246+
if (substr($thumbWithoutExt, -strlen($originalWithoutExt)) === $originalWithoutExt) {
247+
return true;
248+
}
249+
}
250+
251+
return false;
252+
}
253+
173254
public function readAndDelete($path)
174255
{
175256
return $this->filesystem()->readAndDelete($path);

src/ORM/Behavior/FlyBehavior.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ public function beforeSave(Event $event, EntityInterface $entity, ArrayObject $o
133133
if(!empty($orginalValues[$field]))
134134
{
135135
$oldProfile = ProfileRegistry::retrieve(empty($orginalValues['profile'])? $conf['profile']: $orginalValues['profile']);
136+
$oldProfile->deleteThumbnails($orginalValues[$field]);
136137
$oldProfile->delete($orginalValues[$field]);
137138
$afterReplace = $oldProfile->afterReplace;
138139
}
@@ -177,7 +178,11 @@ public function afterDelete(Event $event, EntityInterface $entity, ArrayObject $
177178
{
178179
$settings = $this->getConfig();
179180
$field = $settings['file_field'];
180-
if(!empty($entity->get($field))) ProfileRegistry::retrieve($entity->get('profile'))->delete($entity->get($field));
181+
if(!empty($entity->get($field))) {
182+
$profile = ProfileRegistry::retrieve($entity->get('profile'));
183+
$profile->deleteThumbnails($entity->get($field));
184+
$profile->delete($entity->get($field));
185+
}
181186
}
182187

183188
}

src/View/Helper/AttachmentHelper.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,14 @@ public function thumbSrc($params)
220220
foreach($dims as $key => $value) if (!empty($params[$key])) $url .= $value.$params[$key];
221221
if (!empty($params['cropratio'])) $url .= 'c'.str_replace(':','-',$params['cropratio']);
222222
$url = $url.'/'.$params['image'];
223-
return ProfileRegistry::retrieve($profile)->thumbProfile()->getUrl($url);
223+
$fullUrl = ProfileRegistry::retrieve($profile)->thumbProfile()->getUrl($url);
224+
225+
// Add version query param for cache busting (use modified timestamp or md5)
226+
if (!empty($params['version'])) {
227+
$separator = strpos($fullUrl, '?') !== false ? '&' : '?';
228+
$fullUrl .= $separator . 'v=' . $params['version'];
229+
}
230+
231+
return $fullUrl;
224232
}
225233
}

0 commit comments

Comments
 (0)