From fcf52a0e445a9702b97cb22cf0878b4f3fb71c99 Mon Sep 17 00:00:00 2001 From: Mischa Date: Sun, 5 Jul 2026 18:56:59 +0200 Subject: [PATCH] refactor(photos): extract shared zero-padded PhotoRenumberer helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor cache-on-save's renumberPhotos into a shared PhotoRenumberer class (Grav\Plugin\Shared), the single owner of the photo-NN naming invariant used by both the create/edit reconcile and the upcoming live reorder route, so their numbering can't diverge. Changes vs the old private method: - Zero-pads to photo-01..NN (pad width grows with the set) so lexicographic media order equals numeric order past 9 photos — cover = images|first stays correct for 10+ photos. Normalises pre-existing un-padded photo-N on first pass. - Image-extension guard moved into the helper: only real image files on disk are renamed, so a crafted manifest naming the entry .md, a .gpx or a .meta.yaml is skipped by every caller, not just cache-on-save. Create-mode entries now also emit photo-01..NN — an intentional, accepted side effect of sharing one helper. Co-Authored-By: Claude Opus 4.8 --- plugins/cache-on-save/cache-on-save.php | 38 +-------- .../cache-on-save/classes/PhotoRenumberer.php | 84 +++++++++++++++++++ 2 files changed, 88 insertions(+), 34 deletions(-) create mode 100644 plugins/cache-on-save/classes/PhotoRenumberer.php diff --git a/plugins/cache-on-save/cache-on-save.php b/plugins/cache-on-save/cache-on-save.php index c474f78..6756326 100644 --- a/plugins/cache-on-save/cache-on-save.php +++ b/plugins/cache-on-save/cache-on-save.php @@ -6,15 +6,17 @@ use Grav\Common\Plugin; use RocketTheme\Toolbox\Event\Event; require_once __DIR__ . '/classes/EntryScopeGuard.php'; +require_once __DIR__ . '/classes/PhotoRenumberer.php'; use Grav\Plugin\Shared\EntryScopeGuard; +use Grav\Plugin\Shared\PhotoRenumberer; class CacheOnSavePlugin extends Plugin { /** * onFormProcessed fires once per `process:` action (add_page, upload, message, * reset — 4x for the post form). Photo reconciliation must run exactly once: - * the first pass renames the kept photos to photo-1..N, so a second pass with + * the first pass renames the kept photos to photo-01..NN, so a second pass with * the same manifest would see those renamed files as "unlisted" and delete * them. This latches after the first run (the plugin instance persists for the * request); the first fire is the `add_page` action, after add-page-by-form @@ -244,7 +246,7 @@ class CacheOnSavePlugin extends Plugin } } - $this->renumberPhotos($dir, $names); + PhotoRenumberer::renumber($dir, $names); } /** @@ -276,38 +278,6 @@ class CacheOnSavePlugin extends Plugin } } - /** - * Rename the files named in $names to photo-1..N in that order, in $dir. - * Two-phase via temp names so a target (photo-2.jpg) can't clobber a - * not-yet-moved source of the same name. - */ - private function renumberPhotos(string $dir, array $names): void - { - $planned = []; - $i = 1; - foreach ($names as $name) { - $src = $dir . DIRECTORY_SEPARATOR . $name; - if (!is_file($src)) { - continue; // skip anything not actually on disk - } - $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION)) ?: 'jpg'; - $tmp = $dir . DIRECTORY_SEPARATOR . '.reorder-tmp-' . $i . '.' . $ext; - $final = $dir . DIRECTORY_SEPARATOR . 'photo-' . $i . '.' . $ext; - if ($src === $final) { - $i++; - continue; // already correctly named - } - @rename($src, $tmp); - $planned[] = [$tmp, $final]; - $i++; - } - foreach ($planned as [$tmp, $final]) { - if (is_file($tmp)) { - @rename($tmp, $final); - } - } - } - /** * Locate the freshly-created entry folder: the child of the active trip's * dailies directory that contains all of the uploaded files. Matching by the diff --git a/plugins/cache-on-save/classes/PhotoRenumberer.php b/plugins/cache-on-save/classes/PhotoRenumberer.php new file mode 100644 index 0000000..ebf0bcd --- /dev/null +++ b/plugins/cache-on-save/classes/PhotoRenumberer.php @@ -0,0 +1,84 @@ +