Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 02/08/2026 in all areas

  1. Hi everyone, As promised, the Beta version of WireBooking is now available on GitHub! You can find the repository here: https://github.com/markusthomas/WireBooking I would love to get your feedback on the installation process and general usability. If you find any bugs or have suggestions for improvements, please open an issue on GitHub. Happy testing!
    3 points
  2. Hey, I've started to create a Media Hub for Processwire. [Edit...newest updates to UI are in later posts] Screenshots attached. Obviously a few UI improvements are needed 🙂 One of my clients requested a centralised media manager. I thought it'd be fun to give it a go and learn some stuff. I know that with a self-built Module, it'll always be maintained, and I have an active interest in evolving it. Shout out to @markus-th who just announced he is doing similar with WireMedia.
    2 points
  3. Today I updated the information about Processwire on the wiki. If you have anything to add, you know where to find the page. https://en.wikipedia.org/wiki/ProcessWire
    2 points
  4. Hi everyone, I’m currently working on a module called WireMedia Library and wanted to share the current state of development to get some feedback on the concept. The main goal is to provide a central interface for managing media across the entire ProcessWire instance while staying 100% compatible with the native storage system. The Concept: The module acts as a central hub. You can upload files to the library and then select them from any page. When a file is selected, it is copied into the local page’s file/image field. This ensures that the files remain "native" to the page they are used on, keeping your API calls and templates exactly as they are. Key Features of the current prototype: Central Media Overview: A unified view of all media assets in the system. Usage Tracking: The module indexes where files are used. Even if a file is used within a Repeater, it identifies the "leading" parent page so you know exactly where your assets live. Database Indexing: A custom database table keeps track of file locations for fast performance. Rebuild Index: A tool to rescan the system and ensure the database stays in sync with the file system. Native Workflow: Since files are copied to local fields, you can still use all native PW features (like cropping or focus points) directly on the target page. Planned: Permission System: Granular access control for different user roles/folders. I am still undecided about the final licensing or if/how I will release the module, but I wanted to show the UI and the logic behind it to see if this approach resonates with the community. I’d love to hear your thoughts on the "copy-to-field" approach and the general UI!
    1 point
  5. @maximus Thanks for updating it. I had tried to do so a week or two ago and there were various variables involved in it that I couldn't figure out the source of, so ended up adding it to some version history log, thinking maybe it was pulling from that, but apparently it wasn't. It looks like you replaced the variables with the actual version, which is much simpler.
    1 point
  6. Everything you need to know about custom page classes, from beginner to advanced. You'll find time saving tips and tricks, pitfalls, best practices, and plenty of examples too— https://processwire.com/blog/posts/custom-page-classes/
    1 point
  7. @gebeer That's an excellent summary, thanks! On the core-patterns one, the 'getExcerpt' example probably isn't ideal because it's only returning an excerpt if output formatting is on, otherwise it's returning the entire 'body' field with tags stripped out, which isn't an 'excerpt'. Usually a 'body' field is HTML (TinyMCE, CKE, etc.), so the formatted version would be the same as the unformatted version, unless there's some other formatters being applied on top of it, like TextformatterVideoEmbed, etc. There's an example of a getExcerpt() in the blog post that I think might work better, though I'm sure there are even better examples possible. In the same file, under API wire access, it says that pages() would not work in a Page class. But actually it would work just fine, so long as functions API is enabled. But what's preferable is $this->wire()->pages because it would be guaranteed to be tied to the correct instance (just in case multi-instance, even if rare). For calls like $this->wire('sanitizer') (where API var is in quotes) I'd suggest $this->wire()->sanitizer instead, just because the IDE will know that it's referring to the Sanitizer class, whereas if 'sanitizer' is in quotes then the IDE won't know, or at least it will have to do a lot more work to know. The same goes for any API variable. Lastly, do you think it could link to the blog post also, since that's the source for some of it -- It might help for folks looking for additional info? Thanks!
    1 point
  8. @Ivan Gretsky I'm always a little reluctant to make a blanket statement like "avoid markup in page classes", but I'm referring to what I think works best with the projects I work on. The files in /site/templates/ are the view layer, as nearly all code in there is aimed at generating markup/output. Even if something isn't directly generating markup, it's still finding and preparing things for output. Most markup comes from "partials", which are files that I put in /site/templates/parts/, or if exclusive to a particular template, then /site/templates/[template]/. And then I either include() them, or files()->render() them from the site's template files. I primarily use Markup Regions. The _main.php establishes the base markup: <?php namespace ProcessWire; /** @var Page $page **/ ?><!DOCTYPE html> <html> <head id="html-head"> <?php include('./parts/html-head.php');?> </head> <body id="html-body"> <header id="header"> <?php include('./parts/header.php');?> </header> <h1 id="headline"><?=$page->title?></h1> <main id="content"> <?=$page->body?> </main> <aside id="sidebar" pw-optional> </aside> <footer id="footer"> <?php include('./parts/footer.php');?> </footer> </body> </html> Below is a template file for the /products/ page which lists product pages, supports pagination, and uses URL segments for sorting: <?php namespace ProcessWire; // products.php /** @var ProductsPage|CategoryPage $page */ $products = findProducts($page); $body = input()->pageNum === 1 ? $page->body : ''; $headline = $page->get('headline|title'); ?> <h1 id="headline"><?=$headline?></h1> <main id="content"> <?=$body?> <?php include('./parts/sorts.php'); // no expects ?> <?php include('./parts/products-list.php'); // expects $products ?> </main> <aside id="sidebar"> <?php include('./parts/categories-list.php'); // no expects ?> </aside> The category template file works exactly the same way, except that it lists products for the category rather than listing all products. The same code works either way, so "category.php" just includes "products.php": <?php namespace ProcessWire; // category.php include('./products.php'); There's that findProducts() function above in the products.php template file -- I usually have helper functions in a /site/templates/_func.php, /site/templates/_products.php, or /site/templates/products/func.php (assuming exclusive for "products"). Another place would be for the ProductsPage and CategoryPage to have findProducts() methods, but usually I don't want the page classes getting involved with detecting stuff about the current request (sort, pageNum, etc.) so like these in a simple function library file: <?php namespace ProcessWire; // file site/templates/_func.php included by _init.php function getSorts(): array { return [ 'name' => 'A-Z', '-name' => 'Z-A', 'price' => 'Price (low-high)', '-price' => 'Price (high-low)', 'created' => 'Date added (oldest)', '-created' => 'Date added (newest)' ]; } function getSort(): string { $sorts = getSorts(); $sort = input()->urlSegment('sort-(*)'); if(empty($sort)) $sort = 'name'; if(!isset($sorts[$sort])) wire404('Invalid sort'); return $sort; } function findProducts($page, $limit = 20): PageArray { $sort = getSort(); $find = "template=product, sort=$sort, limit=$limit"; if($page instanceof CategoryPage) $find .= ", categories=$page"; return pages()->find($find); } Here's an example of a ./parts/products-list.php file: <?php namespace ProcessWire; // file: parts/products-list.php /** @var PageArray|ProductPage[] $products */ $subhead = $products->getPaginationStr('Products'); $pagination = files()->render('parts/pagination.php', [ 'items' => $products ]); ?> <h3><?=$subhead?></h3> <?=$pagination?> <ul class="products-list"> <?php foreach($products as $product): ? <?php include('./parts/products-item.php'); // expects $product ?> <?php endforeach; ?> </ul> And the parts/products-item.php, though in reality there would likely be more to it: <?php namespace ProcessWire; // file: parts/products-item.php /** @var ProductPage $product */ ?> <li class="products-item"> <h3><?=$product->title?></h3> <p><?=$product->summary?></p> <p><a href="<?=$product->url?>">View Details</a></p> </li> To complete it, here's the parts/sorts.php file: <?php namespace ProcessWire; // file: parts/sorts.php $currentSort = getSort(); $url = page()->url; $sorts = []; foreach(getSorts() as $sort => $label) { if($sort != $currentSort) $label = "<a href='{$url}sort-$sort/'>$label</a>"; $sorts[] = $label; } echo "<p class='sorts'>Sort by: " . implode(' / ', $sorts) . "</p>"; If I start needing to output products in more places in the site, then I'll usually do fewer include()'s and move the rendering to dedicated functions. That way these things render in their own variable namespace and don't bleed variables or overwrite variables in the main rendering. So this would also go in that _func.php (or _products.php or ./products/func.php) mentioned above, and the include() calls in template fiels would be replaced with render...() calls: function renderProducts(PageArray $products): string { return files()->render('parts/products-list.php', [ 'products' => $products ]); } function renderCategories(): string { return files()->render('parts/categories-list.php'); } function renderPagination(PageArray $items) { return files()->render('parts/pagination.php', [ 'items' => $items ]); } So if using render() functions then the <main> with the include('./parts/products-list.php'); would get replaced with this: <main id="content"> <?=$body?> <?=renderProducts($products)?> </main> Ah yes, I hadn't thought about that in a long time. I can't remember if that was implemented yet or not. I'll find out. If not yet implemented I'll have to implement it, it should be fairly simple.
    1 point
  9. Hey @androbey, can you please provide some more data about your empty responses? Do they have something in common that could have triggered the strange behaviour? Which PHP and ProcessWire versions do you run? I have tested the new release with different PHP versions and several route definitions and did not run into those problems. You are welcome to share the code for your API routes here, or send me code snippets via private message. I really want to find out why this is happening for you. v1.4.2 includes several bugfixes to assure PHP 8.4 compatibility. The part in the code which you mentioned is one of the newly implemented features. Clearing the output buffer should assure that no plain PHP warnings or errors can be output when an apicall is active, because that would lead to invalid json responses.
    1 point
  10. These are very good questions. My honest take: PW is still very much a niche product. People who've been working with it for years learned to appreciate it. But it's very hard to convince anyone to jump in and dive deep until you discover the many advantages PW offers. And yeah, most devs have their specific workflows and it is just inconvenient to adapt new ones that might actually work better. Time/energy constraints may contribute to that. I can only say for me personally, I'd always buy and support proprietary modules developed by experienced PW devs although I am a FOSS enthusiast.
    1 point
  11. @bernhard I want to express my gratitude for all you have contributed to the PW community over the years. And I commend you on your decision to open source your modules. Many of your modules have become part of my workflows for most projects over the years. I was happily paying for the bundle. It was definitely worth it. I hope that the community will pick up on your move and contribute through PRs for further refinement and new features where needed. Thank you again, Bernhard. You and your work are both much appreciated :-)
    1 point
  12. I made this into a skill, following agent skill conventions. Available here: https://github.com/gebeer/processwire-ai-docs/tree/main/skills/pw-ddev-cli There's also a custom page classes skill in that repo, based on Ryans great blog article that just came out. Let your agents stay informed :-)
    1 point
  13. Awesome article that sums it all up neatly. Thanks for this comprehensive guide, Ryan! I converted the content of this article into a reusable AI agent skill. Available here: https://github.com/gebeer/processwire-ai-docs/tree/main/skills/pw-page-classes
    1 point
  14. In line with this topic, I recently expanded AutoTemplateStubs to include ProFields Stubs. There should be stubs for FieldtypeTable, FieldtypeTextareas, FieldtypeCustom and RepeaterMatrix (including stubs for all types). I haven't created a PR yet because I wanted to test it myself first, but since it fits so well with the blog post, if anyone would like to help with testing: https://github.com/phlppschrr/AutoTemplateStubs
    1 point
  15. Awesome! Want more of those! ‐-- Custom page classes really aren't a "view" layer in any form, and I suggest keeping all markup generation code within /site/templates/ and subdirectories within it. Would love to know more about how you implement the view layer. Components with several views.. Controllers for url segments and stuff like that...
    1 point
  16. Beautiful. +1 for the Star Trek reference.
    1 point
  17. Also added a check for ProCache so if maintenance mode is enabled and it detects ProCache is installed and enabled then the warning also tells the user to turn off ProCache - warning only visible in the admin. Also made strings translatable for the warning messages. I've not checked through this topic sorry for other suggestions but can do if I get time and there are still important things to look at just I needed a few quick fixes for myself for now (selfish I know). I will check out the other suggested module I saw too. @ryan perhaps we need a way to mark modules in the database as abandoned or as having issues? Because this one of mine hadn't been touched in a decade and wasn't working and made me feel bad as a result because people were trying to use it and the way my brain works (or rather doesn't) I must have seen issue reports coming in via Github but then got distracted so didn't realise there were that many when I checked. Perhaps we could have a system where folks with a certain reputation can mark a module on the site here as having issues so they get looked at/removed just not sure if anyone would volunteer for that.
    1 point
×
×
  • Create New...