Leaderboard
Popular Content
Showing content with the highest reputation on 09/27/2025 in Posts
-
Based on driving myself completely insane with noHooks for the last 2 years, and based on what Ryan specifically said here: ... I completely agree with Ryan. noHooks option should be absolutely avoided. Seriously, if you use it in advanced cases like I have been doing, you will hit every WTF issue known to man. It is not made for developer use, even though it gives off that vibe. I think that is a mistake. I will write more about this in depth soon, but at least in my situation, my goal was to ultimately update a page and all of its descendant repeaters (repeaters within repeaters) only after the page is its descendant repeaters have been saved completely first without hook interference. Using noHooks basically fucks up everything up (saying it's been frustrating dealing with it is an understatement) and there are unintended consequences everywhere! The correct way to do what I described is to do something like this, which took forever to figure out (every line has a specific reasoning behind it): // // /site/classes/OrderPage.php // class OrderPage extends Page { public function getAggregateRootPage() { return $this; } public function finalize() { // your code to finalize the page, such as populating an order_total field, etc. } } // // /site/classes/OrderLineItemsRepeaterPage.php // class OrderLineItemsRepeaterPage extends RepeaterPage { public function getAggregateRootPage() { return $this->getForPage(); } } // // /site/init.php or /site/ready.php (doesn't matter) // wire()->set('finishedPages', new PageArray()); // hook 1: use Pages::saved only to build list of pages to finalize wire()->set('finishedPagesHook', wire()->addHookAfter('Pages::saved', function(HookEvent $event) { $page = $event->arguments('page'); if(!method_exists($page, 'getAggregateRootPage')) return; wire('finishedPages')->add($page->getAggregateRootPage()); // duplicated pages won't get stored }, [ 'priority' => 1000 ]); // hook 2: use ProcessWire::finished to finalize the pages 🤌 wire()->addHookBefore('ProcessWire::finished', function(HookEvent $event) { wire()->removeHook(wire('finishedPagesHook')); foreach(wire('finishedPages') as $finishedPage) { $finishedPage->finalize(); } }, [ 'priority' => 1001 ]); When I demo my system one day which is way more complicated than the example code above, it will become clear. // TLDR: don't use noHooks when saving a page. DON'T! Instead, create a log of what pages need to be finalized, and act on those pages in ProcessWire::finished hook, which is when you can be absolutely sure the coast is clear. I wish I knew about that hook earlier. If you follow those simple rules, you don't have to think about CLI vs non-cli, ajax vs. non-ajax, whether the current page process implements WirePageEditor, uncache, getFresh, saved vs. saveReady, before vs. after hook, hook priority, editing a repeater on a page vs. editing the repeater "directly", where the hook should go (it should be in init.php/ready.php or in the init()/ready() method of a module), etc. Note: there's a whole other aspect to this in terms of locking the a page to prevent multiple saves (like if a page was being saved by an automated script and the same page was being saved by an editor in the GUI).3 points
-
Hi, everyone! We recently built a small add-on module for our StripePaymentLinks setup and thought it might be useful to share here. We’ve been working with StripePaymentLinks quite a lot lately, and one thing the clients always wanted was a simple way to get purchases synced into Mailchimp without relying on external paid add-ons. So we built a small ProcessWire module to handle exactly that. The idea is simple: every time a customer makes a purchase via StripePaymentLinks, their details (name, email) plus the purchased products (as tags) get synced directly into Mailchimp. That means you can instantly segment, automate, and follow up with buyers without any manual exports. We built this because we didn’t want to rely on a separate paid Stripe → Mailchimp connector. With this add-on, it’s all handled natively inside ProcessWire — lightweight, minimal, and no extra subscription fees. What it does right now: Hooks into the creation of purchase repeater items (repeater_spl_purchases) Pulls customer name + email from the User created by StripePaymentLinks Extracts product names either from the expanded Stripe session line_items or as fallback from the purchase_lines field Pushes everything to Mailchimp, creating the subscriber if you allow it in the config Assigns the product titles as Mailchimp tags Config is super simple: just drop in your Mailchimp API key, Audience ID, and decide if you want to auto-create subscribers or only update existing ones. We’re keeping this intentionally minimal — one module, no extra steps, no fuss. Install, configure, and you’re done. We’ve been running it in production for some clients and it’s working reliably. If you’re already using StripePaymentLinks, this could save you the cost of external integrations while keeping everything in one place. Get it here: ProcessWire: https://processwire.com/modules/stripe-pl-mailchimp-sync/ Github: https://github.com/frameless-at/StripePlMailchimpSync Happy to hear your feedback or ideas for tweaks. Cheers, Mike from frameless Media3 points
-
In case you are using this module and want to sync customers/purchases to Mailchimp – we just released an add-on for this module:2 points
-
We've long wanted a way to utilise a CDN to offload images/files/videos from ProcessWire sites without losing all the native greatness of ProcessWire image and file field types. Having read various discussions on here about ways to approach this that never seemed to reach conclusion, I've thrown myself into creating a module that allows offloading of files to Bunny.net CDN as we need a solution for a specific project. I think this would be easily adaptable to any S3 compatible CDN but I've only tested on Bunny. ⚠️ This is still very beta! Use at your own risk! I've been conducting basic testing and so far, so good but there's bound to be holes or things that others may suggest better ways of doing. But I'm now at a stage where the insight/experience of the PW community might add value to the project - so I'm sharing now! Full disclosure: Once past the initial project scaffolding I've been using AI/careful prompting to write some of the code so that I can arrive at a prototype as quickly as possible. This seems to have worked well, although some of the code looks a little verbose and could probably be refactored later on. Also not security/pen-tested yet. https://github.com/warp-design/WireBunnyCdn/ Features: Automatically uploads images to Bunny storage on page save, including all variants and mirrors assets folder structure for simple merging back to local at a later date if needed. Automatically cleanses deleted files (or files from deleted pages) from your CDN. Option to mirror files to CDN or delete local copies (this is the main aim for me, otherwise we could just use ProCache). Handles (basic currently) image sizing - either using standard ProcessWire `$image->size(X,X)` methods or by implementing Bunny Optimizer for sizing using URL params. Rewrites image paths via CDN so that you can use standard `$page->imageField->url` calls with the output being a Bunny path rather than local PW path. Also handles the image previews in admin view. Roadmap: Support for video uploads (with optional separate CDN endpoint for Bunny Stream buckets). Support for front-end video output to templates using Bunny stream players/optimisation etc. Implement chunked/background uploads for large files. Support for other size() method options, like cropping etc and mapping to Bunny Optimizer equivalents. Anyway - look forward to hearing any advice/feedback/bug reports... I'm sure there's many!1 point
-
I think having seen Kirby CMS' blocks editor last week that that sort of UI with the JSON storage behind the scenes could be the way to go. I think some mashup of the JSON storage from one of those editors, Kirby's UI and the way the Pro Custom Fields module has its fields configured would be the perfect blend and then get rid of the need for Repeater Matrix for a lot of things. Some limitations would be that you have to store images/files somewhere but it's easy enough I guess to just have a field for each that acts as a bucket and the block editor references them. If the UI was advanced enough they could be there but hidden or something. I'll see if I can track down that other discussion - sounds interesting, thanks Brendon!1 point
-
I think experimenting with a block-style editor would be great. In the very large discussion over WYSIWYG editors (I don't recall of this was a 2025 wishlist, or a discussion of the future of TinyMCE/CKEditor, but it had a LOT of paginated responses), a few people even experimented in creating a proof-of-concept with editor.js, showing their results. I gave it a shot but determined the end result (interface) was too cumbersome for most of my users (and I usually try to aim for a very low bar as a standard; less complaints / support needed). Even in the demo (tried today) I was able to cause a rendering bug in both of the editors mentioned (TipTap, Editor.js) above. That said, if a true module is attempted to be released, since they render and use JSON as the underlying structure, I would recommend using a similar database structure to TinyMCE and/or CKEditor, and save the HTML-rendered output in a field just in case someone wished to switch back to a standard text editor for a particular field. (Unfortunately that means only one-way compatibility, however - and increased storage costs/size...so maybe an option for that in the module.)1 point