Leaderboard
Popular Content
Showing content with the highest reputation on 04/18/2023 in all areas
-
Extends Pagefile to use Cloudflare Images, Stream and R2 storage to serve files. https://github.com/nbcommunication/CloudflareAssets The main purpose of this module is to allow ProcessWire to be used in an auto-scaling multi-instance environment. By serving file assets from Cloudflare, it is not necessary to have all file assets copied to all instances and we also get the benefits of serving assets from a CDN. How it works When a Pagefile is added in the admin or via the API, it is uploaded to Cloudflare's R2 storage service. Additionally, if the file is an image, it is uploaded to Cloudflare Images, and if the file is a video it is uploaded to Cloudflare Stream. When a URL for the Pagefile is requested e.g. $pagefile->url(), the appropriate Cloudflare URL is returned. As ProcessWire's admin still requires the file to be available locally, in a multi-instance setup if a file is not available it is downloaded from the 'master' copy in R2. This module is not yet being used in production. There may be changes to how Images work in the coming months as features are still being rolled out to this Cloudflare service. Cheers, Chris4 points
-
Hello, We've recently been researching how to use ProcessWire in a horizontal scaling environment (multiple server instances using a load balanced, read replica databases), and ran an experiment using AWS Elastic Beanstalk. Getting read replica databases up and running was easy - it's built in to the core: https://processwire.com/blog/posts/pw-3.0.175/#how-to-use-it-in-processwire Using multiple server instances throws up one big problem: how to keep the filesystem on multiple instances in sync given that ProcessWire doesn't currently support using an external service (like s3 or EFS) as the filesystem. The solution that we came up with is to use various Cloudflare services (R2, Stream, Images) to serve file assets, and we've built a module to facilitate this: We're not using this in production yet, but our tests on EB were successful, and we're confident this will solve the main part of this problem. However the Cloudflare Images service is still quite new and there's still features to be rolled out (e.g. webP for flexible variants) so it can't be considered a complete solution yet. Additionally, we use ProCache and this presents an additional multi-instance problem - if the cache is cleared on one, how can we clear it on all? Our solution is to log clears in the database and use this to sync up clearing. We built another module: Again this worked well in our test, but isn't yet being used in production. The main purpose of this thread, aside from sharing these potential solutions, is to ask for and discuss other experiences of hosting ProcessWire in a horizontal scaling environment. What solutions did you come up with (if you want to share them) and are there other potential issues we maybe haven't thought about? Cheers, Chris3 points
-
At the time of import of new pages you should use the same sanitizer that you use later for the exist check and assign the page name manually when creating. That should give you consistency.3 points
-
Purge the StackPath CDN cache when ProCache clears. https://github.com/nbcommunication/ProCacheStackPath If you use StackPath's CDN to deliver your website, you need a way to clear its cache when ProCache clears. This module connects with your StackPath stack via the StackPath API and when ProCache clears it requests StackPath clear the cache, attempting to respect the specific rules (Parents, Family, Children etc) set up in ProCache. This module is being using in production but only for a couple of sites that just clear the whole cache when a page is saved. The implementation of the Parents / Family / Children rules haven't been fully tested, and it isn't clear from StackPath's documentation whether their 'recursive' option only works from the root URL or from internal pages too. Hopefully it does and I'll hopefully get it tested soon. Cheers, Chris2 points
-
Synchronises ProCache clearing across a multi-instance environment. https://github.com/nbcommunication/ProCacheSync The main purpose of this module is to allow ProCache to be used in an auto-scaling multi-instance environment. How it works When a page is saved and cache clearing is triggered, the page id(s) associated with that clear are saved to the database. ProCacheSync runs a check every minute via LazyCron to look for records in the database since ProCache was last cleared. If it finds any it processes them as ProCache would. Any records it finds will have been generated by a clear on another server instance, thus enabling multiple instances of ProCache to stay approximately synchronous. This module is not yet being used in production. Cheers, Chris2 points
-
Just released v1.1.0 with support for automatically switching to the tab that contains the first error. This way editors won’t be confused if the error is on a different tab from the one they’re currently on.2 points
-
Freaking awesome!! I've been thinking about doing the same for bunny.net these last couple weeks and the approach I had outlined in my mind would have been very similar to yours. Very well done, I'm super excited for this ?2 points
-
@artfulrobot There's different ways you could go about it, but what you described should work. The way I built the comments form (here) for the example I linked earlier was to use a FormBuilder form for the comments/reviews form and just used the comments API to add comments. (In that example the "Rate more details" link at the bottom opens a bunch more fields). I mainly used FormBuilder because there were so many different fields and photo uploads, etc., that it went beyond what I wanted to do with extending a CommentForm class. Though a manually written regular HTML form would have also worked fine. I use CommentForm more often when it's more typical blog-type comments with the built-in optional stars, votes, etc., as it can save a lot of time since it's nearly turn-key. If you only need to add a field or two, that's probably the quickest route. Btw, I also see no harm with using pages for comments either, but just that you'd be building a lot from scratch with regards to spam prevention, comment approval, etc., and also just as a personal preference, I like to keep anonymous user generated content out of the page tree.2 points
-
@artfulrobot Comments are a kind of turn-key fieldtype focused just on comments (like you might use in a blog) or reviews, and their purpose is pretty specific and different from that of a page. So the point is more to be focused on solving a specific thing than to be flexible in the way that pages are. And actually, this is the purpose of most Fieldtypes. If what you are needing is the ability to build your own custom type then that's what pages, templates and fields are for, and maybe that's what you need, I'm not sure. But if you are needing specifically comments, then FieldtypeComments is also quite flexible for comment-specific needs. When it comes to custom data that you want to store along with the comment, there is the meta() method which you might find useful: https://processwire.com/api/ref/comment/meta/. This is what I use for storing photos and other Q&A with comments/reviews, like you see here: https://www.biketours.com/reviews/2 points
-
Wow... is all I can say right now for the moment. What amount of traffic or hits/second are you awaiting for that kind of setup? I built and ran pretty cheap and simple setups that handled up to about 30-50k hits*/day without noticable issues - ok, those sites were ProCached and running behind Cloudflare CDN (free tier), yet... it worked out. They probably could have handled even more. Nothing of my projects here are scaling horizontally, vertically or in any other direction ? compared to your setup. It's not within your league of setups by any measure - but here is how I built something that scaled back in my days very well: JS files came from sub[1-3].domain.tld super necessary parts were inlined file_get_contents of custom JS came from external sources CSS files came from sub[1-3].domain.tld almost all (critical) CSS was inlined file_get_contents of custom CSS came from external sources IMGs came from assets[1-3].domain.tld Cloudflare took care of GZIP and compressing and caching the output (not sure about brotli) ProCache took care of the heavy load prior to everything else as 95% of the whole site/s were cached (pre-cached by using a Sitescraper after each release) with a very long lifetime Asset and file handling were kind of static and strict without much options for custom solutions (wasn't really necessary for those sites) as the overall page setups were kind of minimal and simple (blog style - minimal differences) files like JS, CSS, IMGs came from other services and not my host, actually everything from a subdomain came from other services as the hosting was too cheap to handle lots of requests - I used Github, Zeitgeist (which is Vercel now - I guess), and some other services I can't remember, for that It was a bl**dy hell to make that work back then (BUT I had to save money I didn't have then) - but those were also one of my very first real projects with ProcessWire then (one of my first public 10 projects ever, and most of them were my own projects) - nowadays that setup would probably be still annoying in some parts, yet more feasible and easier to handle with way better results. My issues back then were limited database and webserver connections (those were over limit pretty fast) at my hosting companies (HostN*n, Dream***, Host***, Blue***, A2***, and such - super cheap) so I split all assets to other services and made them work via subdomains. In the very early days I only paid something between 0,99 USD/month for those sites. Later on 2,99 USD and even later 8,99 USD. It only became faster and faster. About a year before selling/shutting down those projects I paid about 60 USD/month/project. STEEP! Still the almost same setups could easily handle more than double/triple the hits*/day nowadays but with far better pagespeed results than ever before. Till today I'm happy with these kind of setups for my projects. The moment I reach at least 50k+ hits*/day with a project I return to that but with methods and services from today. What I use nowadays (for whatever reason - you will find out ?? webgo IONOS Hetzner Plusline Server Netlify Vercel Cloudflare Pages Cloudflare CDN Cloudinary Planetscale Runway Superbase * real hits/users/sessions - no fake requests ** paid plans for super high traffic sites, otherwise free tiers1 point
-
Could you provide a sample image? And... could you please export the field so I can import it here and compare it? My testing worked with a 1kb JPG pretty fine in PW 3.0.210. Additional note: Please open that image you try to upload and save/export it again as JPG or PNG. I bet there is something wrong with the file itself.1 point
-
Github: https://github.com/thetuningspoon/AdminPreSaveValidation This module prevents admin editors from saving changes to a page that has one or more invalid fields. It uses an ajax call to check for errors and javascript to populate the page edit form with any error messages that were returned. This way the user can correct the issues and resubmit the form without invalid data getting saved to the database or the editor losing changes. I've wanted a way to implement this common workflow in ProcessWire for some time now and this relatively simple ajax-based approach makes it possible without getting too much in the way of how ProcessWire normally works. For example, all of the post-save actions (Save + Exit, Save + View, etc.) still work as usual. The only downside is that a successful save will take a bit longer as it involves two sequential http requests (the initial ajax request that checks for errors and the normal page submit after no errors are returned). This module has also been tested successfully with repeaters and other nested inputs. By hooking after Inputfield::processInput, you can add your own custom validations to inputfields and this module will pick up on and display them as well. /* * Example of hook that adds an error to an inputfield. This will add an error to every input. */ $this->wire()->addHookAfter("Inputfield::processInput", function(HookEvent $event) { $event->object->error('Invalid input!'); }); I hope others will find this module useful!1 point
-
It didn't take long for the changes to the Images implementation - it turns out that image options are available for named variants, just not documented. The module now uses named variants instead of flexible variants. More notes etc to be added to the README in the next month or so.1 point
-
Yeah, apologies on my part for being a bit wishy washy there bro. So... whilst I know there will be a future release with in-built discount codes, that I know is on your roadmap, in the meantime, I have a client who requires some very rudimentary discount code magic for one discount code. With this in mind, I'm trying to implement a discount code input field on my shop-cart.php page/template which calls $padloper->cartRender->editCart() and renders my custom site/templates/padloper/cart-edit.php file. I've added a field called "discount_code" within the form area which I was hoping would get posted on "update cart" submission so that I can then hook 'Padloper::updateCart' to simply grab the posted value from the 'discount_code' field perhaps like so within the hook. if (input()->post->discount_code) { $discountCode = sanitizer()->text(input()->post->discount_code); if ($discountCode == "T/O-FF") { wire('session')->set('orderDiscountCode', true); } } Then I know that in respect to this given session the customer/users discount code is valid I could then do something like: $this->addHookAfter('PadloperCart::getProductPrice', null, 'discountCodePrice'); function discountCodePrice(HookEvent $event) { $product = $event->arguments('product'); // get the product field with the price $stock = $product->padloper_product_stock; // grab the price $price = (float) $stock->price; # amend price if condition is met if (wire('session')->orderDiscountCode === true) { $price = 0.01 * $price; } $event->return = $price; } And it would apply the discount. I could be way off-piste, but I hopefully that gives you an idea of what I'm trying to achieve ??♂️1 point
-
Thanks @ryan I'm glad to hear that you hit this problem too. The client in mind uses comments as ... well ... just blog comments! ... and their need of asking for an Organisation field does not seem to take that use-case away from it primarily being comments. I'm glad also that you implemented a solution 10 months ago, too! So you implement a custom comment form class, and use `getMeta()` in the template output, as you describe in your July 2022 update (and when it comes to me implementing this, I'll figure out where to hook to call `setMeta()`) [EDIT: Ah, looks like I'd just override processInput()] That sounds like the way to go. Thanks for your time and support.1 point
-
tl;dr - see below As @flydev pointed out... a page is more than just only a page. The confusion is real. Totally real for... at least me back in the days when I started using ProcessWire, coming from b2cafelog (nowadays WordPress), Drupal, Nucleus, Textpattern and many more "solutions". Yet... I never ever tried to explain a client what a "page" is in ProcessWire. For my clients a page is the last point/instance, in terms of hierarchy in delivering output, aka a real page. I never tried (after the first 10 clients or so) to explain the steps in-between. Even most designers and developers (outside of PW) get this part - and I don't even try to explain it most of the time anymore. For reasons. While everything is a page... you might want to explain it to your client in different terms. I most often use terms like unit, container, wrapper, entity, part of, fracture... or else depending on whatever I talk about. While I use a page as reference in a recipe, it's still an "ingredient, I can reference" for a client. Makes life easier. This "page concept" is NOT about communicating it to the client (for me!), it's for understanding the concept. When you get this... PW is not only your friend, but your best friend, wingman, best man, godfather of... you get the idea. These are/could be all the same - depending on the context and your setup. $page->title $ingredient->title $city->title $user->title Even though one is a page (endpoint) and all others are a page reference, comment, repeater... whatever. You seem to be very new to PW and came from Drupal, which is totally fine, and therefore you might JUST need to explore more parts of how the API and page handling works. This is not a newbie tip at all! It's what I did and I enjoyed it! You worked with Drupal so... you know more complex setups and concepts - I guess. I came from super limited CMS (see above) back then and learned a lot about PHP and whatever you could achieve with ProcessWire. For me it was freedom to do whatever I wanted to do. Till today. tl;dr/Conclusion: Think about 'page' as a concept and not a page. Or... let us know. We are here to assist and help to find a way.1 point
-
Short answer as I'm slumped on the couch. It seem you already read the structure documentation page and got the idea. About explaining the concept to the client, just use a real analogy. For example, think of a website as a book. In a book, you have chapters, sections, and pages. Similarly, a website is organized into sections, and each section contains individual pages that hold different pieces of content. In ProcessWire, a "page" is a container that holds all the data and content for a specific part of your website. It's not limited to what you see on the front-end as a traditional webpage. Instead, a page in ProcessWire can represent various types of content or data, such as blog posts, products, images, or contact forms. Once that said, and once you got the « Wow this is flexible! » then you can go further and say that using this tool (some of us call it a framework more than a simple CMS) allow you to build other types of applications than a website. I mean no frontend, no colors, no (web)Pages. You can find somes use case threads arround the forum. Ask for more. Just adding that once your clients become familiar with the page-based concept and the benefits of it, they will find it easier to manage their website content. Enjoy ?1 point
-
bern hard, https://processwire.com/api/ref/page/meta/ $bern_hards_datas = $page->meta()->getArray(); love $willy1 point
-
art fulbot, usea that select-options field type.if you not want pages fer select opts. thats blog tut.very olden days, they are was not select-options type then {{ but I still like to uses pages most times }} for comment donut use pages 4 comments install ProcessCommentsManger module in Modules>Core>Process edit.comment field .. clicka Input tab .. choose "yes" for "use comment manger rather then in-page editor" for findning comment use comments find() methoda n.joy youwill or ask more love, $willy1 point
-
Hey @gebeer working with fieldsets in migrations has always been a bit of a pain for me. That's why I'm using them only at runtime in all of my projects. I don't see any drawback with this. Using MagicPages it is as simple as doing this: <?php ... public function editForm($form) { ... $rm->wrapFields( // current form wrapper $form, // fields inside the fieldset [ 'foo' => ['columnWidth'=>33], 'bar' => ['columnWidth'=>33], 'baz' => ['columnWidth'=>33], ], // fieldset settings [ 'label' => 'Your Runtime Fieldset', 'icon' => 'bolt', ] ); } This is a lot quicker than messing around with fields, field order, moving the ending field to the correct place etc. I only place the fields on the correct template, which is necessary for the fields to be queryable by selectors etc and all the rest is done in code in the pageclass. That also makes it possible to switch layouts based on the user's role for example. So the approach is very powerful. I don't see that as a limitation. Or are there any things that I'm missing?1 point
-
ChatGPT helped me to improve RockMigrations - it's a little scary but it really saved me time today. First, I wanted to support single paths (not json) in the workflow file definition: That worked after I adjusted the variable defaults of input.PATHS to "{}" so that the parsing does not throw an error. No idea why it actually seems to parse the json even if the if-condition is not met, but it works now, so I'm happy with that. Then, as that worked quite well, I tried to fix a warning that I've been getting for quite some time and that worked even better: All updates are on the dev branch.1 point
-
Hey @ryan thx for the additions! And thx @Jan Romero for the PR! I think a download button is a must have on image fields, so for my taste it should be default, but until then I've added a tweak to RockMigrations (v2.15.0) so the download buttons are just a checkbox away ?1 point
-
Consider this example, where I want to print a list of teasers (linked title + summary) for news articles: $content = ""; foreach ($pages->find("template=article") as $article) { $content .= wireRenderFile('teaser', array("item" => $article)); } It works, but I find the code quite ugly. I would much prefer to be able to switch the call to wireRenderFile to something more like this: $article->renderWith("teaster"); Or even better, replace the whole thing with something like this: $pages->find("template=article")->renderAllWith("teaser"); Are there any functions like that? I searched the API, but couldn't find anything. Or is there any other way to make my code look better? Or is my quest completely misquided here? I just started out with PW today, so I have no clue what I am doing yet. I already love it though!1 point
-
This may be another one of those ideas that are answered mostly along the lines of "you can already do that by..." - but I'm going to post it anyway, just to see what you make of it. I wonder if it would make sense to support more than one view (php file) for the same template? Currently, php views/files are tied 1:1 to Templates - I realize you can tie more than one Template to the same file, but what I have in mind here is the opposite: tie more than one file to the same Template, and make it optional which one to render. So with this change, $page->render() would render the default view, and other named views could be rendered e.g. using $page->render('summary') which would be useful for example when you're rendering related pages (e.g. from a page selector field) from within another view, as these are typically not displayed in the exact same way as when they render on a dedicated page. Thoughts?1 point
-
I kind of like the current possibility to change template file on the fly. If that is common need then it might be good to have it as a argument for render. Makes nice and clean code: $news->render("snippets/list.php") $page->render("themes/mobile.php") Etc.1 point