Peter Knight Posted 10 hours ago Posted 10 hours ago Hi all, I'm planning a new feature for an upcoming MediaHub release and wanted to run it by users before committing to an approach. This is one of those features that sounds simple on the surface, but has some real edge cases underneath. The challenge Right now, if an image asset needs updating - a product photo gets refreshed, a logo gets tweaked - you have to upload the new file as a brand new asset then manually go to every page that uses the old one and swap it out. On a large site with an asset used across dozens of pages, that could be a painful process. The feature request (tracked as #81) is: replace the file on an existing asset in place, so all existing references automatically pick up the new version without any manual relinking. This works naturally in MediaHub because fields store asset page IDs, not file paths. So, the moment the file changes on the asset page, every InputfieldMediaHub field and every RTE embed that references it picks up the new file on next render. No page resaving needed anywhere. What we've thought through so far File type restriction: Phase 1 would be like-for-like within the same mime group. Images can only be replaced with images, PDFs with PDFs, video with video. JPG and JPEG are naturally the same (image/jpeg) so that's not an issue. No cross-type replacement in Phase 1. PW disk variations (auto-generated resized/WebP thumbs): These can safely be deleted — ProcessWire regenerates them on demand on the next request. No references are broken. Crop pages: This is where it gets interesting. MediaHub crop pages store crops as a child page with their own page IDs. Fields and RTE embeds reference those crop page IDs directly. We cannot simply delete crops as they'd break every reference to them. Instead, the plan is to re-apply the stored crop coordinates against the new source file and save the result back to the same crop page. The page ID is preserved, all references remain intact. We'd show a progress indicator while this happens. Focus point: Preserved as-is. The coordinates may need reviewing if the image changes significantly, but that's the user's call and we'd encourage them to check. Dimensions and aspect ratio: We're not planning a hard block if the dimensions change. A single plain-language warning before confirming, directing the user to check their pages afterwards. Our audience is largely a mix of marketing and developers, so we want one clear message rather than tiered technical warnings. Old file: Simply overwritten. No archive or restore in Phase 1 . Users are expected to hold their own copy if they need a rollback. Post-replacement: Redirect back to the asset detail page with the Usage and Variations sections highlighted, so the user can see exactly which pages were affected and verify the crops look right. The questions I'd like your input on Real-world scenarios we haven't thought of. Have you hit situations where replacing a file in place could have caused problems you wouldn't expect? Things like template code that targets specific filenames, third-party integrations that cache file URLs, anything like that. Crops with changed dimensions. If the replacement image has a different aspect ratio to the original, the re-applied crop coordinates will cover a different visual region than intended. We're planning to warn and let the user proceed. Is that acceptable, or should we be more forceful about flagging this? Non-image assets. For PDFs and video, there are no crops or focus points to worry about so replacement is much simpler. Any edge cases specific to those types we should be aware of? Anything missing from the above? Anything in your own setups such as custom fields on assets, automation rules, API usage in templates . IE cases here you'd want replacement to behave in a specific way? Not looking to build a feature request list here . Just want to make sure our assumptions hold before we start building. Any and all input appreciated here or via DMs. Cheers P 1
David Karich Posted 9 hours ago Posted 9 hours ago I think you’ve identified all the pitfalls quite well. To me, it reads like a well-thought-out plan. One pitfall in my use cases would be ProCache: if ProCache is in use, the pages that reference the modified asset should be removed from the cache. Otherwise, on-the-fly regeneration won’t be triggered the next time the page is loaded, and the page will refer to the old, deleted file – which may even have had a different filename – resulting in a 404 error. As this replacement process can trigger many background processes, depending on the amount (e.g. clearing ProCache pages for hundreds of items), an informative loading indicator in the UI should be used to show users what is happening at any given moment. In principle, it’s also worth considering whether, when uploading the asset to be replaced, you should ask whether the filename should be retained from the original or renamed to the new one.
Peter Knight Posted 9 hours ago Author Posted 9 hours ago Good suggestions, thanks for those. I'll add them to the PRD doc. I am also wondering whether to restrict the replacement image to assets of the same dimensions. At least for the first iteration. It would sidestep a lot of the focus point issues, for example.
David Karich Posted 7 hours ago Posted 7 hours ago 1 hour ago, Peter Knight said: I am also wondering whether to restrict the replacement image to assets of the same dimensions. At least for the first iteration. It would sidestep a lot of the focus point issues, for example. To be honest, I wouldn’t do that. Sometimes, for example when replacing a logo, the graphic simply has different dimensions (because it’s a different logo or partner, for instance, but is to be placed in the same spot). The same applies, for example, when the styles of images change as part of a design overhaul. For instance, square images used to be used for contact persons, but now the client wants portrait format. In that case, a simple swap wouldn’t be possible if there’s a restriction on dimensions.
Peter Knight Posted 7 hours ago Author Posted 7 hours ago 4 minutes ago, David Karich said: To be honest, I wouldn’t do that. Sometimes, for example when replacing a logo, the graphic simply has different dimensions (because it’s a different logo or partner, for instance, but is to be placed in the same spot). The same applies, for example, when the styles of images change as part of a design overhaul. For instance, square images used to be used for contact persons, but now the client wants portrait format. In that case, a simple swap wouldn’t be possible if there’s a restriction on dimensions. Yep. Already discarded the idea. 😆
Peter Knight Posted 3 hours ago Author Posted 3 hours ago Hey everyone Some good progress today. IE I've been hunched over my AI app, watching my LLM credits circle the drain. 😁 But jokes aside, this is a serious feature with many edge cases, and it has to be perfect even if the initial UI still needs a pass. Here's what we've done...if you really hate your eyeballs and want to read this... What we built The feature is fully implemented across five commits on a dedicated branch. Here's what it does: The replacement runs as a staged job — four sequential steps that can survive shared hosting timeouts and give the user live progress feedback: File swap — the new file is installed atomically using a park-and-rename rollback. If anything goes wrong, the original file is restored automatically. Nothing is deleted until the new file is confirmed on disk. Crop regeneration — all existing crop pages are regenerated in place from stored geometry. Crop page IDs are preserved, so any field or RTE reference to a specific crop continues to work. Variation re-warm — old size variants are removed and the most-used sizes are regenerated proactively so the next page visitor doesn't trigger a generation storm. Cache clearing — referencing pages are cleared from both ProcessWire's core template cache and ProCache (targeted per-page, not a full wipe unless you opt in). Metadata survives the swap: focus point, alt text, description, collections, labels, custom fields. All InputfieldMediaHub field references and RTE embeds are rewritten automatically. Tests completed We ran 171 test cases across 19 sections. Here's a summary of each: File accepted, wrong type rejected Uploading a file of the wrong type (e.g. a PDF onto an image asset) is rejected immediately in the browser with a plain-language message before anything reaches the server. Core file swap The new file installs correctly — filename stem is preserved by default (keeping any hardcoded URLs working), extension updates to match the new format. Dimensions, MIME type and filesize all update on the asset detail page. Metadata preservation Focus point, alt text, description, collections, labels and custom asset fields all survive the swap unchanged. ProcessWire disk variations Old size variants on disk are removed before the swap. New variants are regenerated from the new master file. Crop page regeneration Existing crop pages are regenerated in place from stored crop geometry. Page IDs are preserved — no references break. PNG transparency and alpha channel are handled correctly. Variation re-warm The most-used size variants are regenerated proactively after replacement so the next public page load doesn't trigger heavy server work. InputfieldMediaHub field references Pages that reference the asset via an InputfieldMediaHub field pick up the new file automatically — no re-saving of those pages required. TinyMCE / RTE embed rewriting The src attribute on embedded images is rewritten in the database when the filename or format changes. This includes both master file URLs and variation URLs baked in when users resize images inside TinyMCE (e.g. image.500x0-is.jpg). Preview thumbnail The asset card in the library grid shows the new thumbnail immediately after replacement. Collections and labels Collection membership and labels are unchanged after replacement. Programmatic and template access $asset->image()->url returns the new file URL after replacement. No re-indexing or cache warming needed on the template side. Staged progress UI The modal shows live progress for each stage with counts (e.g. "Crops — 2 of 3", "Clearing page cache — 3 pages"). Stages that don't apply (e.g. no crops, ProCache not installed) are omitted from the display. Incomplete job — Run Again If the browser tab is closed mid-run (after the file swap but before crops/variations/cache complete), a warning banner appears on the next visit to the asset detail page. A Run Again button completes the remaining stages. Only the user who started the job (or a superuser) can resume it. CSRF security API requests without a valid ProcessWire security token are rejected with {"success":false,"error":"Missing security token."}. The replacement does not run. Edge cases Trashed assets are rejected. A double-submit while a job is in progress returns a clear error rather than starting a second replacement. Same-filename replacement works correctly. ProCache integration After replacement, ProCache cache files for referencing pages are deleted. Confirmed via incognito page load (which writes the cache file) followed by replacement — the cache file was removed correctly. Filename handling By default the original filename stem is kept and only the extension updates. An opt-in checkbox allows using the new file's name instead. RTE embeds are rewritten in both cases. Audit logging Every replacement writes to the MediaHub log: start, file-swapped, crops, variations, cache-cleared, hook-fired, complete. Logging is forced on for replacements regardless of the module's logging setting — this is an irreversible operation and always needs an audit trail. Atomicity and job locking The file swap is atomic at the filesystem level. A job lock prevents two simultaneous replacements of the same asset. Rollback is exercised when the incoming file cannot be installed. Honest bugs found and fixed during testing RTE variation URL not rewritten (fixed) When a user had manually resized an image inside TinyMCE and then replaced the asset with a file of a different format (e.g. PNG → JPEG), the variation URL baked into the RTE field (image.155x0-is.png) was not being updated. The original code only rewrote the master file URL. Fixed by switching from a SQL REPLACE to a PHP preg_replace that matches the filename stem plus any variation suffix — covering master URLs, .500x0-is, .155x0-is and any other PW variation pattern. TypeError on partial job records (fixed) If a job record was incomplete (e.g. created before all keys were initialised), the publicJob() helper would throw a TypeError from count() on a null value. Fixed with null-safe defaults on all keys. This surfaced during the Run Again test. Known behaviour — RTE resized embeds and aspect ratio If you replace an asset with an image of a different aspect ratio, any images that were manually resized inside TinyMCE (by dragging the corner) will appear distorted — the src updates correctly but the hardcoded width and height attributes that TinyMCE wrote remain, as they represent an editorial layout choice. This is standard behaviour across CMSes. The fix is to re-open the page in the editor and resize the image. We'll document this clearly in the user docs. If you'd like to get your paws on this and help me test it, please DM me. I have a new feature in the account section of your account where I can provide access to early Beta version. Anywho, 12 hours of this means it's now time for a glass of wine and a flop on the sofa. Im on-site tomorrow with a client but I can answer questions on Wednesday. Cheers P
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now