Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation since 06/02/2026 in all areas

  1. This week we've got a big batch of issue fixes on the core dev branch (about 18 fixes), most related to GitHub issue reports. I've also been catching up on some client work this week, so not as many updates as in the last few weeks. As I use more AI in my work, I've been building up a real desire to understand how it works. To me it seems like magic. So recently I started building a language model in PHP. Admittedly PHP isn't the ideal language for building such a thing, but this is really about learning, and so I thought I should use the language I know best. Using something like PyTorch in Python would certainly make things much easier, but would also abstract away a lot of the understanding I want to get out of the project. The new model (and future ProcessWire module) is called Rambler. Currently it rambles on, often incoherently, hence the name Rambler. Though it'll get more coherent as time goes on, no doubt (hopefully!). Rambler uses zero machine learning libraries, no black boxes, and in fact has no dependencies at all. It's just pure PHP implementing the same mathematical foundations that power modern AI systems like GPT, at least that's the goal. I'm writing all the code myself, but I do have Claude as my teacher, describing each step, teaching me the concepts and terminology, and telling me what I need to code. After I code each part, he looks at it and tells me what I did right and wrong. It's a slow process but I learn by doing, and so it's also a lot of fun. It certainly helps to have an infinitely patient teacher. Currently the project runs two models side by side (for comparison): A Markov n-gram model, which is a classical statistical approach that predicts the next word based on how often word sequences appeared in training data. A neural network model that learns distributed word representations called embeddings. The neural model passes those embeddings through a hidden layer with ReLU activation, then predicts the next word using a softmax output. As far as I understand it, these are the same core building blocks used in early neural language models. The most time consuming part of doing this in PHP is the training. Python has libraries and functions that handle a lot of the hardcore math in ways that can take advantage of the hardware, like GPUs. But this is not the case with PHP, so training uses the CPU, and a lot of it! As far as the training system goes, it uses mini-batch gradient descent with backpropagation. The model makes predictions, measures how wrong it was (which is the "loss"), and then works backwards through the network, computing gradients to adjust every weight in the right direction. Rambler also includes two tokenizers: A word-level tokenizer, and a BPE (byte pair encoding) tokenizer. BPE is the subword strategy that is used by GPT, Claude and other modern LLMs. But for the small scale that I'm working at, the word-level tokenizer works faster, so far. The next milestone is adding an attention mechanism in a RamblerTransformer subclass. This attention mechanism (a transformer) is the core innovation behind a lot of modern LLMs. I'm hoping to get started on that part this weekend. Beyond being a learning exercise, the longer-term goal is to train it on all of the ProcessWire documentation (which is what they call a "corpus" in this context) and release it on GitHub as a learning resource, and a PW module. Perhaps someday it'll be a tool in ProcessWire, or at least a really smart search engine for ProcessWire, we'll see. As far as I could tell, there aren't any other PHP-based language models that use the same technologies used by modern LLMs, so I figured, why not. I want to understand how they work under the hood without wading through Python frameworks, and I'm sure others do too. Once I get a little farther along with it, I look forward to getting it up on GitHub as a standalone project, but also as a ProcessWire module. The slowness of the training process (the model, not me... well okay, probably me too) is the hard part. I'm currently running a 30 hour training on all the text from a book. When the project is finished, I'm likely going to have Claude or Codex do a translation of the training code into C, that takes advantage of the much faster math capabilities available there. From what I understand, a 30 hour training in PHP will take about 30 minutes in C. I don't know if that's accurate or not, but it sounds good enough that I'm going to find out. 🙂 Thanks for reading and have a great weekend!
    27 points
  2. This week on the core dev branch, the biggest addition was the upgrade from FontAwesome v4 to FontAwesome v6. This greatly increases the number of icons available in the admin. And while the new icons are in the same family as the old ones (still FontAwesome), they have a little bit different personality too. You can still use the v4 icons if you want by setting $config->adminIcons('version', '4'); though I recommend leaving it at the default, which will use v6 icons for AdminThemeUikit and v4 icons for older admin themes. There were several other updates this week to the core, which can be found on the dev branch commit log. The AgentTools module also received some significant updates this week: New scheduled tasks feature (this one is my favorite). Enables AgentTools to run tasks automatically at scheduled times or intervals. Improved Agents/models management screen (see screenshot). New tabs navigation makes it easier to get around in AgentTools. New debug and traces feature to track what agents are doing behind the scenes. New persistent memory feature, enabling agents to save a permanent memory across all sessions, when you ask them to. New guards on agent behavior, in case one goes rogue and decides to go places where it shouldn't. Note that SiteEngineer and PageEngineer each have their own persistent memory. SiteEngineer's persistent memory can be modified in the module configuration, while PageEngineer's persistent memory can be modified in your engineer field(s) settings. @wbmnfktr let me know about OpenCode Go last week, which I think is amazing. It provides you access to a whole bunch of AI models and a ton of usage for $10/month ($5 for first month). I have found most of the models to have very good ProcessWire knowledge. I think it's a perfect match with AgentTools because OpenCode provides you with as many API keys as you want. Whereas Anthropic and OpenAI don't include API keys with their monthly plans (Anthropic and OpenAI make you pay by the token with API keys). If you want an extra $5 credit for it use this link and it'll give both of us $5. I'm only posting that because it gives you extra credit, I don't need the extra credit so if we can get @wbmnfktr to post his link, use his instead since he's the one that told me about it. OpenCode also comes with a terminal app that is basically identical to the Claude Code and Codex terminal apps. They apparently have a desktop app too, but I've not yet tried it. If any of you do end up getting OpenCode I'd be curious what models you enjoy the most. So far I'm having a hard time deciding, as they've all impressed me in different ways. Yesterday I setup a scheduled task to "write blog posts" as just an AgentTools test, and do a round robin between the agents, writing blog posts and having different agent proofread them, every 15 minutes. I went to lunch. Then I started getting emails from the agents that they'd finished blog posts. Look at what they came up with (screenshot below). I started reading them, and they are actually fantastic posts. They are so good in fact that I'm tempted to post some of them on the site here, but I've never used AI generated content for this stuff before so am still debating that. What would you do? Thanks for reading and have a great weekend! Blog posts that agents came up with: AgentTools tasks: New AgentTools agents configuration screen: Background scheduled jobs in AgentTools: New icon selection in the core (InputfieldIcon, like used in the Field edit screen) replaces the "show all icons" grid of icons with a search box. FA6 comes with far too many icons to show a grid of them all at once, plus the search is a lot more useful and easy to use.
    17 points
  3. This week we've ProcessWire 3.0.267 on the dev branch, here are some highlights (commits): The entire WireTests module has been moved into the core. All of the tests now live alongside the classes that they test. The WireTests module is used from the command line. Once installed, you can type "php index.php test" for a list of WireTest commands. If you already have the non-core WireTests module installed, I recommend uninstalling it and installing the core version. More than a dozen new WireTests and API.md documentation files have been added this week (this is where the biggest additions of the week are). Many improvements to the CLI "docs" command, especially with regard to chapter retrieval. New faster $modules->refresh() method from @matjazp New HOWTO-API.md documentation that outlines how to write API.md files for your own modules. New getFresh() and getRaw() methods added to $fields and $templates. These do the same as their $pages method equivalents (getFresh and getRaw), except for fields and templates. This was requested by an agent in AgentTools. This week we also have a new guest blog post! Page's best kept secret: the meta() method Thanks for reading and have a great weekend!
    15 points
  4. Hi everyone, I’m happy to share the first public testing release of Mercato, a ProcessWire-native commerce toolkit. Mercato is not just a “shop template” or a simple cart module. The goal is to provide a flexible commerce layer that works the ProcessWire way: pages, templates, fields, permissions, hooks, and site-level customization. The basic idea is: Your website should not have to become “the shop system”. The shop should become part of your ProcessWire site. Mercato can be used as a full storefront, but it is also designed for custom ProcessWire websites that need commerce features without being forced into a rigid shop structure. What Mercato includes Products as ProcessWire pages Cart and checkout Orders stored as ProcessWire pages Stripe, Mollie, PayPal, bank transfer, and demo payment support Discounts and coupons Inventory handling Preorder and backorder support Digital products and downloads Fulfilment: carrier delivery, store pickup, local delivery Customer records Abandoned checkout recovery tools Refunds and payment operation tracking Webhook/event logs Reports and CSV exports Launch-readiness checklist Public order status and receipt pages Headless/read API surfaces A complete installable demo storefront Demo storefront A fresh install creates a real demo storefront called Arlberg Ceramics. It is not just placeholder content. The demo includes: physical products digital products gift cards product collections product images discounts low-stock products sold-out states preorder/backorder examples checkout policy pages order confirmation pages delivery, pickup, and local delivery scenarios The demo is meant to help developers test the full commerce flow immediately after installation. Existing sites Mercato is designed to add commerce functionality to an existing ProcessWire site without taking over the whole website. It creates Mercato-specific fields, templates, pages, permissions, products, collections, and order storage. It does not remove existing site content. Bundled storefront template files are copied into /site/templates/ only when the target files are missing. Existing template files are not replaced unless template overwrite is explicitly enabled in the module settings. That said, this is a commerce module and it changes the site schema, so please test on a staging copy first and make a backup before installing it on an existing/production site. Current status This is the first public release and should be treated as a testing release. I have tested the installer, demo storefront, checkout flow, admin screens, and basic payment workflows, but I would really appreciate feedback from real ProcessWire projects and different hosting setups. Things I’m especially interested in: installation issues gateway setup issues checkout edge cases admin UX feedback missing hooks or extension points template customization feedback real-world commerce scenarios I have not covered yet Repository GitHub: https://github.com/mxmsmnv/Mercato Requirements ProcessWire 3.0.200+ PHP 8.1+ Final note This is a big personal milestone for me. I have wanted ProcessWire to have a serious, modern commerce foundation for a long time. Mercato is my attempt to build that foundation in a way that respects how ProcessWire developers actually build websites. Feedback, testing, issues, and ideas are very welcome.
    14 points
  5. Hi all — we're putting this one up as a public beta and looking for feedback before we tag a stable release. How it started. Over the past months we've been moving an old blog into a fresh PW site using our own SiteSync module and a Claude Code agent doing most of the migration grunt work. At some point the blog owner mentioned, in that very offhand way clients do, "hey, an image search would be nice." It was Saturday afternoon, so we let the agent build a prototype, pushed it through SiteSync, tested it on the phone an hour later. Worked great. Search results were… not great. But the search wasn't the problem – the underlying data was. Thousands of imported images, almost no descriptions, no tags, no nothing. So we needed a way to retroactively caption and tag a few thousand images without clicking through hundreds of page edits one by one. Since PW (rightly) attaches images to the pages they belong to, we needed a tool that reaches across the whole install at once and – crucially – can edit metadata in bulk. Why not the existing modules? We looked at the two obvious candidates: Media Manager by @kongondo – great if you're starting fresh and want a central media hub. But it's its own storage layer: you upload INTO Media Manager, editors pick FROM Media Manager. Images already sitting on per-page image fields stay invisible to it. Also commercial. MediaLibrary by @BitPoet – adds a MediaLibrary template with its own MediaImages / MediaFiles fields plus a CKEditor picker. Same pattern: a separate page hierarchy you migrate media into. Both are well-designed for "we want a central media model from day one." Neither helps you when the media is already scattered across lead_image, body_images, gallery, images_in_some_repeater etc. Migrating that into a different storage layer would have broken the original page model the blog depends on. So we built Image Library: a Process module that does nothing to your data – it just surfaces a cross-site table view of everything that's already there, with serious bulk editing on top. The bulk-edit part – the reason this module exists. Selection as a paintbrush. You tick N rows across any pages, templates and image fields. Then you edit a cell on ANY of those rows — the popup gains an Add / Replace mode picker (tags additionally offer Remove) and the value gets broadcast to the entire selection in one server round. Same row applies to description, tags, every custom subfield, AND the filename (with placeholders: (n), (n2)..(n5) padded counters, (N) total, (t) page title, (d) date, (p) page name, (f) field name → e.g. rename 200 selected files to event-2025-(n3).jpg). Same row applies to delete (one trash click, whole selection gone behind one confirm dialog with a where-used preflight – see below). Edits that push a row OUT of the active filter ("missing tags" → tag assigned → row no longer matches) fade out and drop from the table; counters auto-decrement. Other highlights: One sortable, paginated, bookmarkable table across every FieldtypeImage field on every page on every template (with config-side blacklists). Inline edit per cell – multilang-aware: language tabs in the popup, all languages committed in one save. Typed widgets per custom subfield: checkbox, date, integer, options (single + multi), and FieldtypePage rendered through PW's actually configured Inputfield (PageAutocomplete / PageListSelect / ASMSelect / whatever the field uses) — no re-implementation. Replace image in place (drag-drop or upload icon) – basename stays, variations regen. Renaming an image in the library instantly rewrites every CKEditor/TinyMCE embed of that file across the site — original and all variations, in every language — so links never break, and a summary dialog shows which pages were updated. Delete with where-used preflight: dialog scans every Textarea via $pages->findIDs("field%='/pid/stem.', include=all") and lists the pages where the image is still embedded in rich text – CKEditor + TinyMCE both, multilang-aware, with direct edit links so you can fix embeds before deleting. JSON / CSV export + import for offline metadata work – hand a CSV to a copywriter or feed it to your agent, get it back, import it. View prefs (columns, page size, bookmarks) live in $user->meta, cross-device. Status. v0.54.x – public beta. Module + docs (EN + DE concept) at GitHub or the Modules Directory Feedback welcome – especially edge cases we haven't seen yet (weird Fieldtype combos in custom-field templates, ProFields, Repeaters / RepeaterMatrix nesting). And if you've got a use case the current feature set doesn't cover, let us know. Cheers, Mike
    14 points
  6. This week we have ProcessWire 3.0.266 on the dev branch. The focus of this version has primarily been on expanding our documentation tools and API.md documentation files. As part of the process, WireTests files are added for each documented class. In addition, the WireApiDocs class got several major upgrades, plus its own documentation file: https://processwire.com/api/ref/wire-api-docs/ Other classes that gained custom API.md documentation and tests files this week were: Fieldtype: https://processwire.com/api/ref/fieldtype/ Selectors and Selector: https://processwire.com/api/ref/selectors/ Pages Versions: https://processwire.com/api/ref/pages-versions/ Wire Markup Regions: https://processwire.com/api/ref/wire-markup-regions/ Wire Text Tools: https://processwire.com/api/ref/wire-text-tools/ Wire Random: https://processwire.com/api/ref/wire-random/ Wire API Docs: https://processwire.com/api/ref/wire-api-docs/ The Fieldtype API.md was written by Claude Sonnet, Selectors/Selector was written by Claude Opus, PagesVersions was written by Kimi K2.7 and the rest were written by MiMo Pro. Each goes through multiple rounds of proofreading and testing. Proofreading is either done by GPT 5.5 or Claude Sonnet. Following that, GPT 5.5 builds tests for the class using the WireTests framework. After running tests, we find items to fix either in the API.md or the core. Then I do a round of proofreading and edits. Finally, both the class API.md file and the tests are committed to the core. At this point, most of the major classes in ProcessWire have their own API.md documentation files and tests, so now we're focused on some of the more specific tools and classes. I also wanted to mention that the Modules class got some major CLI improvements. Here's the full CLI command set available for the $modules API now: Modules ======= php index.php modules list [site|core] List installed modules, optionally limited to site or core modules php index.php modules unlist [site|core] List uninstalled modules, optionally limited to site or core modules php index.php modules info <name> [property] Get JSON of all info for module or optionally info property php index.php modules install <name> Install module php index.php modules uninstall <name> Uninstall module php index.php modules exists <name> Does given class name resolve to a module? (Yes/No) php index.php modules installed <name> Is module installed? (Yes/No) php index.php modules config <name> Get configuration data for module as JSON php index.php modules config <name> <property> Get value for property in module config php index.php modules dir <name> Query ProcessWire modules directory for module info php index.php modules updates [name] List available updates for installed site modules, or check one module php index.php modules download <name> [--install] Download module from PW modules directory (+ optionally install) php index.php modules download <url> [--install] Download module ZIP file from https URL (+ optionally install) php index.php modules update <name> [--force] Download and apply an available module update php index.php modules delete <name> Delete/erase uninstalled module from file system Optionally append --json to any of the above commands for more verbose JSON output While I'm posting CLI commands, here's the full command set for the updated WireApiDocs class: WireApiDocs =========== php index.php docs list List classes with API.md docs php index.php docs list 'Class*' List classes matches wildcard pattern php index.php docs list-verbose List classes with API.md docs in verbose mode php index.php docs list-verbose 'Class*' List classes matching pattern in verbose mode php index.php docs get <class> Get API docs for given class php index.php docs toc <class> Get table of contents for given class php index.php docs chapter <class> <num> Get body for given class and chapter number php index.php docs chapter <class> 'Title' Get body for given class and chapter title php index.php docs methods <class> Get public methods for given class (* prefix = hookable) php index.php docs method <class> <method> Get details for a single method (JSON only) php index.php docs classinfo <class> Get class info: parent, interfaces, traits php index.php docs constants <class> Get public constants for a class php index.php docs properties <class> Get @property annotations for a class php index.php docs groups <class> Get #pw-summary-[group] descriptions for a class php index.php docs vars List all API variables and the classes they represent WireApiDocs commands return JSON by default. To make command return plain text (not JSON), append `-text` to the command name, i.e. `list-text` See the dev branch commit log for additional core updates this week: https://github.com/processwire/processwire/commits/dev/. If using AgentTools, grab this week's new version too. More next week. Thanks for reading and have a great weekend!
    14 points
  7. Hi everyone, I’m preparing a new ProcessWire module for release: Panorama. Panorama is a media audit and maintenance toolkit for ProcessWire images and files. It is built for sites where media is spread across many templates, repeaters, galleries and file fields, and where you need one place to understand what is stored, where it is used, what can be cleaned up and what needs attention. What it does Media dashboard with totals, disk usage, average size, recent uploads and largest files. Breakdown by file type, field, page and template. Visual Explorer for browsing media by gallery, page or template. Detail drawer with owner page, template, field, dimensions, file size and image variations. Duplicate finder with background scan, visual cards and reclaimable space estimates. Alt text audit for images missing descriptions. Cleanup tools for broken references, orphaned originals and orphaned variations. Background image variation warmup. Bulk actions for selected media. CSV export. Support for Repeaters, Repeater Matrix and FieldsetPage ownership where possible. The heavier thumbnail-based sections load in the background, so the main admin page should stay responsive even on media-heavy sites. Requirements ProcessWire 3.0.227+ PHP 8.3+ Repository GitHub: https://github.com/mxmsmnv/Panorama
    13 points
  8. Thx for the loveletter @gebeer and others. I thought I've already replied, but it seems that post got lost. I'm getting a lot of 500 errors these days in the forum. I wouldn't say I've quit the community. What I've stepped away from is making significant investments in the ecosystem and trying to build a business around it. That didn't work out for me, so I had to move on. As for RockMigrations: I understand and even share some of the concerns raised here (even though I find terms like "bloated nature" a bit harsh). It started as a somewhat experimental proof of concept, and yes, I built it with my other modules in mind. At the same time, it has always been completely free because I felt it was too important to put behind a paywall, and it has always been open to PRs. 😉 Nevertheless, over time it turned out to be very reliable and became an essential tool for my work. Considering that all my ProcessWire projects from the last decade rely on it, I expect it will remain supported for quite some time. That's the status quo. What I did find a little surprising, though, was seeing migrations become part of the core years later, seemingly almost overnight, while RockMigrations and the other migration modules weren't much of a reference point in that process. Unfortunately, it's not the first time I've seen that happen. What I learned from that experience is that it's not something I can build a business on. So I really haven't quit on the community — I've simply moved on from trying to build a business around my ProcessWire contributions, and my reduced forum activity is just a consequence of that. But I check in from time to time to see what's going on and I try my best to be available and helpful if anybody needs anything related to my modules. 🙂
    13 points
  9. Today, I am happy to showcase one of Fruitcake Communications AG's latest projects powered by ProcessWire. Ausgleichskasse des Schweizerischen Gewerbes AK105 AK105 is one of Switzerland's long established trade compensation funds, serving over 10,000 companies with first and second pillar social insurance, family allowances and pension administration since 1947. The multilingual site gives employers, employees, the self employed and pensioners clear access to all services, news and the connect online portal for paperless administration. The website is only part of the whole project as we also refreshed the existing logo, created a new corporate identity from that, and reimagined all business materials. The result is a modern, professional brand identity that accurately reflects the organization’s values, also perfectly reflected on the new website. We have developed a portal for existing business customers, parties interested switching to AK105 and also the actual people to whose benefit the trade compensation funds are. The goal is to answer questions and provide further information directly on the federal government portal. We knew one of the most important aspects of such a challenge is the search function and improved accessibility. ProcessWire has ready-to-use tools for both of these goals whilst our frontend is purpose built in-house like always for Fruitcake. Search We are making heavy use of @ryan's RepeaterMatrix which allows any component to be used literally anywhere on the website. Searchability becomes a challenge just because of that though. Most of the actual content lives either inside RepeaterMatrix pages or is fetched from other meta content. Thus, a front-facing page will be stitched together from many different sources like repeaters and other pages. To actually make the website searchable, we have developed a sophisticated set of hooks and functions which will search through all repeaters, nested repeaters and then from these results, backtrack to the actual front-facing pages so that the search result shows the precise content found whilst then linking back to the correct place where that content is located. Accessibility Not only just because the target audience is people aged 50+, accessibility is another key requirement for such a project. Apart from following accessilibity standards, we also have included a text-size switcher to allow for better readability whilst keeping the rest of the content concise. Most of the work here is invisible though: Styles for prefers-reduced-motion switches, proper handling of any navigation and control element using just the keyboard and flawless screen-reader support. Actually selling this invisible work to clients is a challenge in itself. Luckily, our client is already familiar with the necessity and the benefit of the endeavour and thus there was almost no discussion needed. Admin impressions Technical details The site is powered by ProcessWire 3.0.255 of course using the new admin theme which we ❤️! Modules in use: RepeaterMatrix SeoMaestro ProcessRedirects TracyDebugger ProtectedMode WireMailSmtp FileValidatorSvgSanitizer InputfieldAssistedURL FieldtypeColorPicker Links Case study on our website: https://fruitcake.ch/cases/ak105/ Website: https://ak105.ch/ Showcase on processwire.com: coming soon
    11 points
  10. Hi everyone, Needed a way to embed polls and quizzes directly into page content — shortcode in any text field, renders as a cache-safe interactive widget. Built it, it works well, sharing it. GitHub: https://github.com/mxmsmnv/Pulse Screenshot: What it does Polls and quizzes embedded via shortcode into any text field: [[pulse:poll name="my-poll"]] [[pulse:quiz name="my-quiz"]] Polls — voting with live results, multi-select, allow-other option, duplicate protection (cookie+IP, user session, or soft). Quizzes — three modes: graded — points, pass/fail threshold, answer review personality — options award points to outcomes, winning outcome shown at end exam — graded plus timer, attempt limit, question bank, certificate Visual questions — question-level images and image-option layouts. Admin workspace — Setup → Pulse with full CRUD, analytics, CSV/JSON export, import, clone, and demo installer. Security: All scoring server-side — is_correct / outcome_points never reach the client before submission CSRF mandatory on all submissions (token from /pulse/state) Honeypot + rate limiting by hashed IP Voter cookies and IPs stored as salted hashes only Exam HMAC-signed question sets, server-enforced timeouts, signed certificates Cache-safe: renders a static shell, hydrates via JS — works with ProCache. Requirements: ProcessWire 3.0+, PHP 8.2+ MIT License.
    11 points
  11. Hi everyone, Most ProcessWire sites need some form of community interaction — reviews on product pages, Q&A on documentation, discussions on articles. Vox adds all of that without leaving your PW install. GitHub: https://github.com/mxmsmnv/Vox Demo: https://vox.smnv.org/vox-demo/ What it does Reviews — star or dot ratings, custom fields per template, photo attachments Q&A — questions with best-answer selection Discussions — open threads with nested replies Block comments — inline comment panels for specific sections on a page Guest posting — with optional email requirement Moderation — approval queues, reports, stop-word filtering Gamification — points, ranks, badges, leaderboards REST API — via VoxApi submodule Admin section — browse, filter, approve/reject/remove content; configure per template; manage ranks and badges Screenshots One or multiple widgets per page, combinable into tabs. Ships with an optional demo with restaurant, hotel, and product sample data. Requirements: ProcessWire 3.0+, PHP 8.0+ MIT License.
    11 points
  12. Hi everyone, Most project management tools are generic. Verk is built specifically around ProcessWire's data model — tasks link directly to PW pages, the calendar reads real page date fields, and content audits run PW selectors. Everything stays inside your install. GitHub: https://github.com/mxmsmnv/Verk Why Verk? Verk means "work" or "task" in Icelandic and Swedish. Short, abstract, and fitting for a tool built around getting things done inside ProcessWire. It follows the same naming approach as other modules in this series — Arbor, Ichiban, Collections — names that mean something without being literal. Screenshots: What it does Dashboard — open tasks, upcoming publications, audit alerts, and active sprint planning Tasks — create/assign tasks linked to specific PW pages; one click opens the page editor directly Calendar — month, week, and quarter views for page publications and task due dates Content Audit — run PW selectors with dot-notation field checks to find missing content Knowledge Base — rich editorial notes organized by category, searchable and exportable Sprints — sprint planning, quarter grouping, task assignment, DOCX export, and progress tracking Key details: Tasks store page_id — page data lives in PW, never duplicated Page Editor Widget injected via hookAfter('ProcessPageEdit::buildForm') — no template files modified Audit to tasks — bulk-create tasks from audit results with page context prefilled Rich text via TinyMCE when InputfieldTinyMCE is installed DOCX exports for task lists, notes, sprints, and knowledge base Return-aware forms — create/edit flows preserve filtered list URLs Requirements: ProcessWire 3.0.200+, PHP 8.0+ MIT License.
    10 points
  13. Wake up. Work with ProcessWire. Drink coffee. Repeat. Anyone else wonder how they got so lucky to be able to work with ProcessWire every day? 🤗 I often wonder if developers on other systems feel the same. What are you working on today?
    10 points
  14. Hi everyone, Accessibility overlays have a bad reputation — mostly because they're sold as SaaS, phone home to third-party servers, and charge monthly fees for something that should be built-in. Ally is different: self-hosted, MIT, no external requests at runtime. GitHub: https://github.com/mxmsmnv/Ally What it does Adds an accessibility panel to your site's frontend, powered by Sienna (MIT). The JS bundle and OpenDyslexic font ship with the module and are served from your own server — nothing loads from external CDNs at runtime. Font size adjustment Dark, light, and high contrast modes High/low saturation, monochrome Dyslexia-friendly font (OpenDyslexic, bundled locally) Highlight links and headings Letter spacing, line height, bold text Reading guide, stop animations, big cursor 53 languages with auto-detection from html[lang] or browser settings Full ProcessWire multi-language support — maps $user->language to the correct locale automatically Configurable position, offset, button size, and accent color Skips admin pages and Chrome Lighthouse by default No build step — prebuilt JS bundle included. One caveat: the widget is injected via Page::render hook. If you serve pages through ProCache static HTML, the hook doesn't run on cached pages — exclude those pages from ProCache if you need the widget there. Overlay widgets supplement, but do not replace, accessible markup. Use Ally alongside good semantic HTML, not instead of it. Requirements: ProcessWire 3.0.200+, PHP 8.1+ MIT License.
    10 points
  15. Meteo is a weather integration module for ProcessWire. It fetches current weather and forecast data, normalizes responses from different providers into one predictable array format, and can optionally render ready-made weather widgets. Providers Meteo supports: Open-Meteo OpenWeatherMap WeatherAPI.com Open-Meteo works without an API key. OpenWeatherMap and WeatherAPI.com can be used as primary or fallback providers when API keys are configured. Features Current weather, hourly forecast, and daily forecast City geocoding Provider fallback chain File cache with configurable TTL Structured array output for custom templates or frontend frameworks Bundled widget templates: card, full, minimal Widget themes: auto, light, dark One-click Material M3 demo page installer Error tracking via getLastError() Basic usage $meteo = $modules->get('Meteo'); $weather = $meteo->getWeather(40.7128, -74.0060, [ 'location_name' => 'King of Prussia', 'timezone' => 'America/New_York', ]); echo $weather['current']['temperature']; echo $weather['current']['weather_label']; Render a bundled widget: echo $modules->Meteo->styleTag(); echo $modules->Meteo->renderWidgetForCity('King of Prussia', [ 'language' => 'en', 'widget_theme' => 'auto', ], 'card'); Links GitHub: https://github.com/mxmsmnv/Meteo Documentation: https://github.com/mxmsmnv/Meteo/blob/main/docs/USAGE.md
    9 points
  16. New version (v 0.55.0) – This changes everything Barely a week on from the public beta release here's where Image Library went next. Short version: it grew a brain for duplicates, and that quietly changed what the module is for. How it continued. Barely a week after the first post, a different client site handed us the next problem – and this time it wasn't missing metadata, it was duplication, and a lot of it. ~10,000+ images on the site, of which only ~3,500 were actually unique – meaning roughly 6,500 exact-or-near-duplicate copies of the same pictures scattered across lead_image, body_images, galleries, repeaters, you name it. Years of "just re-upload it on this page too." So we went looking for a way to make duplicates stop mattering – without migrating anything. The full rationale is in the repo (docs/deduplication-design.md); the gist: Give every image a content identity – a byte hash (xxh128, md5 fallback), computed lazily and cached by path+size+mtime, with a cheap size/dimension pre-filter so we only hash real candidates. Group identical images into clusters = "this picture, in these K places." Then duplicates become manageable, not just visible: a Duplicates filter, a copy-count badge per image, an expandable cluster in the table (a cluster modal in the gallery), where-used per cluster, and the big one – edit-once-propagate: caption/tag a cluster once, written to all copies. Pure DB writes, fully reversible, zero filesystem risk. Optional, opt-in, reversible disk reclaim: byte-identical copies get hardlinked onto one inode, so 6,500 copies stop costing 6,500× the bytes. Lossless, runs in the background (on save + hourly), with Scan / Re-measure / Revert tools and a live "disk reclaimed" readout. (We tried perceptual near-dup detection too – dHash/pHash — and pulled it: it grouped unrelated photos that merely share a tonal layout. Detection-only, never destructive, and still not trustworthy enough to ship.) And then it clicked. What we'd built, almost by accident, was a DAM without the DAM. Strip a digital-asset manager down to what it actually gives you: one logical asset instead of scattered copies, metadata edited once as a single source of truth, a "where is this used," a central place to browse and pick – and storage that doesn't pay for the same file twice. Every one of those falls straight out of the content-identity + cluster technique. The difference: we get it over the images already sitting in native FieldtypeImage fields – the asset layer is synthesised from what's there, not a store you migrate into. No central library, no new page type, no migration, and crucially no template changes. That last part is the whole point: on some of these projects, re-wiring every template and every chunk of content onto a new media model would be a multi-week job and a regression-test nightmare. We just… didn't have to – and the editors get the DAM experience anyway. A word on MediaHub. If you are starting a fresh project and want a proper central media model from day one, MediaHub is – in our opinion – the best option out there. Huge thanks to @Peter Knight for letting us test it; we're genuinely impressed, it's a lovely piece of work. Different tool, different job: new site → MediaHub. Existing site full of scattered images → this. Image Library isn't trying to change the way PW image handling works; it's a layer over the files, pages, templates, methods and structures you already have. Which is exactly the rule we build by: must work with existing installations, as-is; plug & play – install, open, done; no rebuild, no migration, no new fields, no template surgery; no new workflow for editors; and – new – front-end-editor capable. BAM. 💥 That last one is the other big addition: two optional, off-by-default picker add-ons: Choose from library – a button on every image field to assign an existing library image without re-uploading (version-aware: inside a page version it lands in that version's folder, and it's deduped on the spot). Insert from library – a button in TinyMCE and CKEditor that drops a library image straight into rich text… and it works in the front-end inline editor (PageFrontEdit) too. Editor live on the page, click, pick, done. Plus, while we were in there: Collections – a hand-picked set of images no filter could reproduce (tick the ones you want, save them as a named tab). Recalled by a tiny ?coll=<id> link – the image list lives in your user profile, not the URL, so a 100-image collection is still a ~12-character link. Add/remove just by clicking a collection's tab while images are selected (the cursor tells you which way it goes), and collections are themselves filterable. Masonry gallery view – height-balanced (shortest-column) packing, natural aspect ratios, hover-revealed selection checkboxes – the same selection that drives bulk edits and collections. Status. v0.55.0 – public beta on top of the original. Module + docs (EN + DE concept, plus the dedup design doc) on GitHub / the Modules Directory. As before: feedback very welcome – especially duplication in the wild, odd Fieldtype combos in custom-field templates, ProFields, deep Repeater/RepeaterMatrix nesting, and anything the front-end picker trips over. Cheers, Mike
    9 points
  17. I’ve been doing regular ProcessWire updates for years, and honestly, they’re usually pretty painless. Ryan keeps things sane, modules mostly behave, and if you don’t wait forever between updates, compatibility drama is rare. But there’s still one part that always felt a bit… clicky. Check module updates. Download paid modules ZIP files. Upload/install. Refresh modules. Commit. Repeat. Then core. Then smoke test. Not hard, just enough tiny steps to make you think: “surely my robot coworker can do this?” So I made a reusable AI-agent skill for doing ProcessWire system updates through DDEV CLI: https://github.com/gebeer/processwire-ai-docs/tree/main/skills/processwire-system-update-cli plays nice with https://github.com/gebeer/processwire-ai-docs/tree/main/skills/processwire-ddev-cli Don't have ddev? Too bad, consider using it or tweak the skill. Or use Ryan's AgentTools module. Your agent will figure it out, eventually. What it does The workflow is basically: Ask ProcessWire what’s outdated via ProcessWireUpgradeCheck Check module requirements before touching anything Update modules first, core second, unless compatibility says otherwise Install public and Pro module ZIPs from the CLI Refresh module caches and verify versions Commit every module update separately Update ProcessWire core last Smoke test the site in a browser (given your agent has the tooling. I use cdp-cli with a skill derived from its readme). Less admin panel dance, more deterministic “do the thing, verify the thing, commit the thing”. The important bit: compatibility first The skill does not blindly follow “modules first” in all cases. Before updating, it checks module requirements from places like: public static function getModuleInfo() { return [ 'requires' => [ 'ProcessWire>=3.0.240', 'PHP>=8.1', ], ]; } …and from the module directory data returned by ProcessWireUpgradeCheck. If a module needs a newer PHP version, the agent should stop and warn you. If a module needs a newer ProcessWire version, that’s a legit exception to the usual “modules before core” rule: update core first, then come back to modules. This matters especially for folks who wait a long time between updates. If you update regularly, you may never hit this. If you update once every few years, dependency gremlins are much more likely. Pro modules too For Pro modules, the skill doesn’t pretend it can magically access your PW account. It scans first, sees which Pro modules need updates, then asks you for local ZIP paths. After that it copies the ZIP into the project cache and uses ProcessWire’s own module installer internals: ProcessModuleInstall::unzipModule() modules()->refresh() modules()->resetCache() Tiny gotcha: unzipModule() deletes the ZIP it extracts, so the skill uses a temp copy. Ask me how I know. 😄 Why I like it The end result is not fancy. That’s the point. It’s just a solid update ritual encoded as a skill: scan first check compatibility snapshot DB update one module verify commit repeat update core browser smoke test For my latest run it took care of TracyDebugger, MaintenanceMode, ProFields Table, ProFields RepeaterMatrix, ProMailer and ProcessWire dev core with clean commits for each step. ProcessWire updates were already pretty friendly. This just removes the boring click choreography. Coffee stays warm. Git history stays clean. Robots get to do robot work 🤖
    9 points
  18. Hi everyone, I've been building this for a while - a full genealogy system inside ProcessWire. Family trees, people, relationships, sources, documents, DNA kits, research workflow, and GEDCOM export. All inside the PW admin. GitHub: https://github.com/mxmsmnv/Arbor Status: Beta. Usable on a test copy. Always backup before installing. Screenshots: Why ProcessWire for genealogy? Genealogy data is complex, relational, and research-driven - not a blog. ProcessWire's flexible data model and custom DB table support make it a natural fit. Everything in Arbor lives in arbor_ prefixed tables, completely separate from the PW page tree. What it does Multiple trees - owner/public settings per tree Person profiles - names, events, notes, sources, photos, relationships Family/union management - partners, parents, children, relationship types Interactive tree viewer - main person panel, selected-person panel, agenda, legend Places & repositories - archives, sources, citations, documents, document leads Research workflow - questions, tasks, search log, proof conclusions, next actions DNA - kits, matches, segments GEDCOM 5.5.1 and GEDCOM 7 export GEDCOM import foundation (in progress) AI helper via AiWire-compatible providers - research suggestions, name analysis REST API via ArborApi submodule Three submodules: Arbor - schema, models, configuration, permissions, shared services ProcessArbor - admin UI under Setup → Arbor ArborApi - optional REST endpoints Requirements: ProcessWire 3.0.200+, PHP 8.1+, MySQL/MariaDB with InnoDB MIT License.
    9 points
  19. The posts I'm talking about are legitimately good, reminding me of features l'd forgotten or just better at communicating a part of processwire than me. Maybe it's in part because they'd gone through a round of proofing from another agent before I'd seen them. Though there was one about using CB radios that was completely off topic and also hilarious. And there was another one where DeepSeek (I think) describes how it experiences the world, which I thought was fascinating, and it somehow also made it about PW. As far as coding errors in the posts, I didn't find many. There was a hallucination in a post about markup regions by Opus 8, but it was something I read and thought "oh yeah we should support that". The hallucinations sometimes point to obvious things that should have been there already. One reason I like using Claude Sonnet so much is that its hallucinations are more often than not: good ideas that just haven't been implemented yet
    9 points
  20. I'd like to showcase the demo shop for my ProcessWire shop system. The demo lets you explore both the frontend and the administration backend. Feel free to log in, place test orders, and try out the available features. The shop automatically resets to its default state every six hours, so you can't accidentally break anything. If you'd like to receive the email notifications using your real email address, please make sure to delete your order immediately after testing, as your email address will be visible in the backend. Because this shop system is primarily developed for the German market, the demo shop and the documentation are currently available in German only. The "Stammdaten" section and the payment methods cannot be edited in the demo to prevent misuse. Demo: https://demoshop.mholte.de/ I'd be happy to hear your feedback, suggestions, and bug reports.
    8 points
  21. Following up on my earlier comment about the background colour - here's a quick side-by-side to illustrate (bottom of post). The proposed change is simple: lighten --pw-main-background from #eee to #f5f5f5 (a shift of less than 3%) Three reasons it's worth considering: #f5f5f5 is already the background colour used on processwire.com, so it brings the admin in line with the brand. It feels noticeably lighter and less industrial in practice. A few of my clients have used that exact wording. For module builders, it opens up more tonal room to work with subtle layering and contrast. I know it's not quite a single variable change. Borders and dividers calibrated against #eee might need a look too, but it's pretty close. You'll see below that it's just a whisper lighter. But in practice users get brand consistency, general lightness and approachability. In practical terms, this is involves transitioning from 93% to 96% white. Copying in @diogo in case this is useful feedback as the theme evolves and celebrates its birthday around now. And finally, in case it needs to be said, the KonKat theme is clean, considered and feels like a proper step forward for the ProcessWire admin. It's a really thoughtful piece of work by people who sweat the details, so I hope this feedback can be taken in that spirit.
    8 points
  22. New Figma to PageGrid Importer From design to production in no time, with the new Figma to PageGrid Importer. At our studio, Konkat, we do all our design work in Figma because it lets us move and iterate quickly alongside our clients. While AI is great at spinning up generic, cookie-cutter layouts, true professional value lies in creating websites genuinely custom-tailored to a client. For that level of work, pure text prompts simply don’t offer the layout precision you need—writing a description will never match the intuitive control of direct visual input. But as any developer here knows, turning a finished design into a live, client-editable website usually requires a lot of manual layout replication before you can get to the core development. Even though PageGrid is already a massive speed upgrade, manually recreating a canvas layout element-by-element is still a time-consuming assembly phase. It eats up hours better spent on deep development, custom logic, and polished execution. That’s exactly why I (with the help of Claude Code) built the new Figma Exporter plugin and a native PageGrid Importer module for ProcessWire. It compresses hours of manual layout replication into a five-second automated import. By handling the structural translation instantly, it lets you skip the repetitive setup entirely and jump straight into the fun stuff: custom animations, fine-tuning, and high-level development. Instead of just telling you what it does, I want to be completely transparent about how it was built, why we intentionally avoided a "live AI" generation solution, and how it changed my perspective on building dev tools. Why We Rejected Live AI Generation When I started working on this, my first instinct was to leverage the power of AI agents directly during the import process. I tried using Claude via a live Model Context Protocol (MCP) setup to directly read my active Figma canvas and dynamically generate the PageGrid layout on the fly. Honestly, AI is surprisingly good at understanding how to build a layout in PageGrid now, and the initial results were already incredibly close to the original Figma designs. Using MCP is a really interesting alternative, but for daily production work, relying on a live AI connection just didn't make sense for a few major reasons: It was unpredictable: Every single time I ran the generation on the exact same layout, the resulting code structure came back slightly different. It was slow: Relying on live AI cloud prompts meant waiting for the model to think, stream, and process the data. API Bottlenecks: The live Figma API connections generated far too many requests, causing frequent rate-limiting issues. Resource-heavy: Each time we want to create a new layout we have to prompt an AI, which uses extra computing power and adds up in ongoing token costs. At our agency, Konkat, we built PageGrid to power actual production sites for our clients. We plan to use these tools daily to optimize our own studio workflow. In a professional production pipeline, an unpredictable guessing game that changes its behavior on every prompt simply isn't an option. We needed something that was completely deterministic, blazing fast, and independent of live external APIs. So, we changed our strategy entirely. Changing the Strategy: AI as the Engineer, Not the Runtime Since Claude had become deeply familiar with the Figma API, and thoroughly understood the structure of ProcessWire and PageGrid, I stopped trying to use the AI to generate layouts on the fly. Instead, I partnered with Claude to write a dedicated, local compiler. Claude acted as my co-developer. Together, we built a simple, two-part ecosystem: A local Figma Exporter Plugin that reads the canvas instantly and packages the architectural structure and assets into a lightweight .ZIP file. A native ProcessWire Importer Module that unzips that package and handles the rendering locally on your server. Because the final tool runs entirely on pure, local code logic, it requires zero active AI API calls, runs instantly, and produces the exact same, predictable structure every single time. Respecting Layout Intent Without Forcing "Auto Layout" Most Figma-to-code plugins fail unless you have meticulously built your file using strict, perfectly nested Auto Layout components. If you design loosely, those plugins break or output absolute-positioned spaghetti code that is impossible to maintain. As a designer, I know that forcing strict Auto Layout workflows during the rapid, creative prototyping phase can feel incredibly restrictive. That’s why we engineered our importer to give you complete freedom to design your way. Whether you prefer nesting elements using strict Auto Layout, arranging them freely using Figma's native Column Layout Guides, or using a mix of both, the compiler adapts to your workflow. When you run the importer, PageGrid respects your structural intent and translates it into standard web architecture: Native CSS Grid & Flexbox: Elements aligned to your column guides are automatically mapped to precise, responsive CSS Grid positions, while Auto Layout frames are translated cleanly into semantic Flexbox structures. Direct Layer Mapping & Smart Flattening: The importer replicates your Figma layer structure directly in PageGrid by mapping all layer nodes straight to native PageGrid Blocks like pg_group (groups), pg_image (images), and pg_editor (text). At the same time, the parser automatically filters the tree, flattening out any unnecessary deep nesting or arbitrary wrappers when needed so your backend tree stays incredibly clean. Maps Figma API to Modern CSS: It natively extracts typography styles, colors, borders, spacing rules, and effects, turning them directly into clean PageGrid metadata or copyable CSS. 💡 The "Grouping" Pro-Tip: Because Figma group nodes map directly to pg_group container blocks, grouping items that belong together directly shapes your PageGrid backend structure. While the importer can handle ungrouped layers, grouping your elements ensures the highest layout accuracy and keeps complex structures beautifully organized under the hood. Keeping You in the Driver's Seat When the import finishes, you are not left with a static image or a fragile webpage that breaks the moment you edit a line of text. You get a native, fully editable ProcessWire page built out of organized PageGrid blocks. Because PageGrid functions like a precise web canvas, the layout remains rock-solid. Your clients can easily log in to update text and images directly on the live page, but they will never accidentally break the structural layout you designed. The mechanical translation work is completed in five seconds. From there, you take over to do the work that humans do best: adding fine-tuned interactions, implementing custom animations, and binding dynamic backend data fields. How to Try It You don't need a paid plan to use it—the plugin works perfectly on the free version of Figma. Here is how to get started: Get the Figma Plugin: Open the Figma to PageGrid Exporter in Figma. Select your main design frame and download your .ZIP package. Install the Importer Module (Self-Hosted Only): Add the PageGridFigmaImport module to your ProcessWire backend. Upload & Run: Go to Setup > Figma to PageGrid, drop in your file, and watch your layout appear natively. Documentation: Figma Import Documentation. I consider this a beta launch, not every generated layout will be perfect out of the box, but results so far have been quite impressive. I plan to improve this over time. Give it a try and let me know what you think!
    8 points
  23. Hi everyone, a client of ours had a term that shows up all over their site, and they had it registered internationally. So suddenly it needed an ® after every occurrence. The question landed in our inbox as "you probably know an easy way to do this, right?" 😉 There is already Ryan's TextformatterFindReplace, which is powerful and the right tool if you are happy writing regex. We needed something different: a textformatter our clients can edit themselves, i.e. people who do not write regular expressions. So in Annotations you just list the strings and pick options in a small per-string table, no regex anywhere. And so clients never have to touch the Modules section, the module also adds its own page under Setup, guarded by a dedicated annotations-edit permission, where they manage exactly these settings and nothing else. What it can do, for example: Append a mark, the classic case: put (R) (or ©, ™, ...) after a term wherever it appears. Wrap a part of a word, e.g. turn H2O into H2O (subscript the 2). Auto-style certain terms, e.g. make a brand name bold everywhere. Footnote markers, mark only the first occurrence of a term (first-only), great for footnotes1. The real work, though, is in the details that make it safe to hand to an editor: It only changes text, never markup. Tags and their attributes (href, alt, title, class), HTML comments, and the content of skip-tags you configure (code, pre, script, style) are left completely alone. A brand name sitting in a URL or an alt text stays untouched. E-mail addresses are protected. With a frameless to ® rule, info@frameless.at does not turn into info@frameless®.at. It never doubles a mark. It recognises a symbol that is already there, whether it was typed as the literal character (®), as an entity (&reg;, &REG;, &#174;, &#xAE;, lower or upper case, even with leading zeros), or already wrapped in a tag. No second one gets added. It normalises messy input to your setting. Same spelling, consistent form: a bare symbol gets wrapped, an existing wrapper gets unwrapped (or re-wrapped) if you change the tag later. Editors can be inconsistent and the output still comes out uniform. Longest match wins, and rules compose. Configure both "frameless" and "frameless Media" and each keeps its own treatment. Append runs first, wrap layers on top, so "frameless Media" can be bolded on the word and still get its ® at the end: frameless Media®, while a single "frameless" gets rendered as frameless®. Per-string control: whole-word matching (so cat does not hit category), case sensitivity, and first-occurrence-only, each set per string. Any inline wrap tag: sub, sup, b, strong, em, mark, abbr and more, not just superscript. Get it here https://github.com/frameless-at/TextformatterAnnotations https://processwire.com/modules/textformatter-annotations/ Feedback and ideas welcome. Cheers, Mike!
    8 points
  24. Is this the desk setup and keyboard thread? I love seeing people's desk setups. I just cleaned up my desk, so here's mine. Computer is a Macbook Air M5 mounted on a shelf under the desk, running from single thunderbolt cable. Monitors are 27" 4k Dell S2725QC (the cheapest 4k USB-C screens you can buy, but I like them). Keyboard is a Kisnt k85. An ebay thunderbolt 4 hub mounted under the desk feeds the screens and an external drive. A cable management basket under the desk keeps all the cables tidy. Monitors are mounted with arms attached to the rear of the desk. ProcessWire coffee and water bottle are my daily drivers, thanks to @Jonathan Lahijani
    7 points
  25. How small things can make life easier for your site editors One small (but big) thing we wanted on a recent RPB build: editors should only see the blocks that make sense for the page template they are editing. Project pages get the ~10 project blocks, landing pages get their own set, and nobody has to do per-page fiddling or choose from 25 blocks where they only need 10. Repeater Matrix makes this possible via per-template field overrides. RPB needs a little custom nudging here, but that can be pretty straightforward as you will see. The nice bit: RPB already has exactly the right hook point. RockPageBuilder::___getAllowedBlocks($field, $page) returns a BlocksArray, so we hook after it in our site/modules/Site.module.php and remove anything that is not in the template's allow-list. This runs with priority 1000, so RPB's own populate hooks have already done their thing. Shape of that hook: public function ready(): void { $this->addHookAfter( 'RockPageBuilder::getAllowedBlocks', $this, 'filterRockPageBuilderBlocksByTemplate', ['priority' => 1000] ); } public function filterRockPageBuilderBlocksByTemplate(HookEvent $event): void { $page = $event->arguments(1); $allowed = $page->template->get('rpb_blocks_allowed'); if (!is_array($allowed) || !$allowed) return; $allowed = array_flip($allowed); $blocks = $event->return; foreach ($blocks as $block) { if (!isset($allowed[$block->className()])) { $blocks->remove($block); } } $event->return = $blocks; } Allow-list implementation (the really smart bit) The allow-list lives on the template itself as a custom property called rpb_blocks_allowed, stored in the templates.data JSON column. Yes, templates support meta data just not exactly like pages. There's no public $template->meta() API. PW is cool nonetheless! We set that data declaratively in the template's migration file (config migrations to the rescue). RM picks that up and applies it via `setTemplateData()`. No new field needed, because a field would be per-page data, and that is the wrong tool here. At runtime it is just $page->template->get('rpb_blocks_allowed'). Templates without a list keep the full block palette and are completely unaffected. What that looks like in the template's migration file: // site/RockMigrations/templates/project.php return [ 'fields' => [ 'title' => [], // ... the template's normal fields ... 'rockpagebuilder_blocks' => [], ], // RPB block allow-list for this template, stored as a custom property // in templates.data and read by the getAllowedBlocks hook. 'rpb_blocks_allowed' => [ 'ProjektHeader', 'TextIntro', 'ImmobilienUeberblick', 'ImageModul', 'Video', 'BildText', 'Zitat', 'Awards', 'FAQList', 'NewsTeaser', ], ]; This also turned out to be nicer than expected: it is enforced in the editor add-menu and for API/programmatic block creation, it still composes with every block's existing info()['show'] selector, all template config stays in one clean migration, and there are zero core edits for RPB updates to clobber. Tiny gotcha we proved along the way: Template::meta() does not exist. Page has meta(), Template does not. That is exactly why templates.data plus a custom template property is such a neat fit here. Big thanks to @bernhard for making RPB so hookable. And @ryan for baking in meta data support into the Template class. This is one of those ProcessWire-ish solutions where the final code is boring in the best possible way: hook the right method, keep the config where it belongs, and let the rest of the system keep doing its thing. Happy blocking! BTW: RockPageBuilder is awesome and free on github: https://github.com/baumrock/RockPageBuilder. So what are you waiting for?
    7 points
  26. Happy Friday, everyone. I'm starting this Friday with something I meant to do last Friday! The latest version of MediaHub (1.19.25) is now available, and thanks to our testers, we have improvements across the UI, module performance and some new features too. Existing Media hubbers: Download here New to hubbing: Read here Full changelog: Read here What's new and improved in 1.19.25... Upload screen refresh Uploading is one of the first things a new user will do, so the first impression should be positive, distraction-free and simple. But behind a screen that seemingly has 'just one job', people asked if they could organise their uploads from here. While organising uploaded assets within the Library is simple, being able to associate your uploads with your Collections and Labels from the upload screen will save you a lot of time and asset admin. An "Organise uploads" bar above the dropzone lets you assign Collections and Labels before the batch starts. If one doesn't exist yet, you can create it from here. A scrollable card queue replaces the old list. Each file shows a thumbnail or file-type icon, inline status, and a per-file progress bar. Oversized or unsupported files stage with a clear warning and are excluded from the upload. Inline filename editing on each card is possible before you upload. We'll introduce a table view shortly if you're uploading at scale and want to sort and filter at this point. Once your upload starts, you can see the progress of each file and a master progress bar. Custom fields on assets Adding custom fields to a MediaHub input field works the same way as the existing workflow. Should you wish, you can also assign custom fields to a master asset depending on your use case. You can assign custom fields to your master asset or just the MediaHub field on your page. They work indenpendently when you want, but also have a relationship with and inherit-with-override model. Inherit-with-override works the same way Title and Alt already do: The asset detail page (left) holds the canonical (library-level) value. Every reference inherits it by default. On a page, custom fields appear below Title, Filename, Description, and Tags. An editor can override the value for that one reference only. The override saves when the page saves, no separate save button A small reset control next to any overridden field lets you revert to the library value in one click On a MediaHub input field, a developer can choose to display custom fields in a different order than on the asset detail page Two tiers Some metadata belongs to the asset everywhere (a photographer credit, a licence URL). Other metadata belongs to how the asset is used in a specific field. Both are supported: Asset-level fields go on pkd-mediahub-asset. Master value plus per-reference override and reset Field-specific fields go on a template named mediahub-field-{fieldName} (mirrors ProcessWire's native pattern). These appear only in that field's drawer and are per-reference only, no library master I hadn't planned on allowing custom fields on the asset detail page, but it was straightforward once I added some of the standard text-based fields. It works in the same way you'd add a field to any template and doesn't even require the extra custom field to input field step (creating an extra fileless template). Anything on that asset template that isn’t one of MediaHub’s built-in fields: title, image, alt, labels, collections, and so on - is treated as yours. Should you wish to uninstall MediaHub, we have safeguards in place to keep your custom fields on your ProcessWire install, which is what I think you might want. The only real downside I have found so far is that custom fields on the master asset template show on every asset detail page, so a name like Photographer fits a photo but feels odd on a PDF or spreadsheet. What has worked for me is broader labels from the start (Source, Description). We plan to make this more asset-aware in a future release, but for now, name fields as if every asset type will see them, because they will. But, this is just a side note and doesn't affect input fields, which was the core feature and of the 1.19.25 release. Library thumbnails 1.19.25 takes a leaner approach to thumbnail generation and makes more effort to reuse thumbnails across the UI vs generating the full scope on upload and import. One preview at upload. Each image gets a single small proportional preview. That's 75% fewer auto-generated files at upload time. One library thumb per asset. Grid, Masonry, and List share one canonical thumbnail. CSS handles grid cropping. Built on first browse. The full library size is generated the first time you scroll an asset into view, not during upload. The upload preview is shown immediately as a placeholder so nothing looks broken while it's being prepared Import Existing Images and the asset picker use the same on-demand model. Bulk imports of existing assets from existing fields should feel noticeably lighter on disk and CPU. Library bulk actions and toolbar The search and filter area of the Library was cleaned up for consistency and clarity in advance of some architectural changes (more below) Selecting assets now swaps the breadcrumb row for a compact bulk action bar with actions for Collections, Labels, and Delete. There's a useful workflow change here: you can select assets first and then create and assign a collection in one step, rather than having to create the collection first. Library and picker consistency MediaHub looks like it has one central Library, but under the hood there are actually three slightly different versions of it: the main Library, the InputfieldMediaHub picker, and the TinyMCE picker. Over time I found that improvements made to the main Library were easy to overlook on the other two, since they didn't share any underlying code. To reduce that drift, 1.19.25 moves the toolbar, sidebar, filters, and tiles onto shared partials so all three surfaces stay aligned going forward. This isn't just a tidy-up under the hood either. It lightens the module overall, and it's what made it possible to introduce the Collections and Labels sidebar into Library screens that didn't have it before. Import page images The ability to import images from your existing fields was an early feature and has been in MediaHub since v1 1.19.25 gives it an overhaul. BTW the import Page Images button is optionally enabled in the field config so you can enable it on a field-by-field basis. Repeater and RepeaterMatrix support The scan now walks Repeater and RepeaterMatrix fields up to three levels deep. Results are grouped under breadcrumb headings so you can see exactly where each image came from. How matching works Each image on the page is scored against the whole Hub using four signals: filename stem file size dimensions and a perceptual hash (a 64-bit visual fingerprint that can match re-encoded or renamed copies). Each result gets a confidence badge: New - not currently in the Hub Exact match - identical file already in the Hub Likely match - looks like an asset already in the Hub Possible match - filename matches a Hub asset but the file content appears different Already added - already used in this field When a match is confirmed, MediaHub adds a reference to the existing asset rather than copying the file again. Hardening for large pagesThere's also longer scan and import time limits, JSON error handling, a 200-selection cap per request, and client-side checks so a timeout doesn't surface as a cryptic error. Import Page Images was one of MediaHub's earliest features, and I think there's more we can do here. The import modal in particular could use a bit more cleanup, so that's on the list. That's pretty much it. Thanks for reading and scrolling! MediaHub is currently available for single sites, developers of multiple sites and agencies. If you'd like to try it first, DM me. Have a great weekend, Peter
    7 points
  27. Hey everyone! Built a heatmap module for ProcessWire, thought I'd share. It tracks clicks, mouse movement, rage clicks and scroll depth — and shows everything as a heatmap right in the admin. No third-party services, all data stays on your server. You pick a page from the list, it loads in an iframe with the heatmap overlaid on top. Switch between click/movement/rage/scroll tabs, filter by date and device type, export as PNG if you need it. Install is just drop the folder into /site/modules/ and hit Install. The tracker injects itself automatically, nothing to configure. GitHub: https://github.com/mxmsmnv/Compass
    7 points
  28. 7 points
  29. It's a rainy Sunday where I'm at. The monsoon is hitting hard. Perfect time to write a love letter. Addressed to a module, my favorite PW module of all times. And its creator @bernhard, of course. A Love Letter to RockMigrations by a long-time user and contributor Dear RockMigrations, I’ve been using you for years. Built sites with you, shipped modules that lean on you, even chipped in a few pull requests along the way. And honestly, I took you for granted. You just worked. Fields appeared. Templates materialized. Modules shipped their own schema like little self-contained suitcases. I never wrote down why you’re so good. I just kept using you. Recently I found myself comparing you to other approaches, side by side, line by line. And I realized: I should write this down. Not because I learned something new, but because seeing the alternatives made your elegance impossible to ignore. Let me count the ways. The Declaration of Truth Most migration systems hand me a blank file and say “write code.” Fair enough. But you, RM, let me declare what a field or template is: // site/RockMigrations/fields/subtitle.php return [ 'type' => 'text', 'label' => 'Subtitle', 'columnWidth' => 50, ]; That’s it. No $fields->save(), no $field->type = wire('modules')->get(...). Just the truth. The current, desired state. You figure out the rest. This means the file is my source of truth, not a historical log of what someone did at 9:14 AM on a Tuesday. If I want to know what the subtitle field looks like right now, I open one file. Not a trail of timestamped breadcrumbs. Circular References? You Solved Them. Elegantly. Every migration system hits the same wall: Template A needs to know about Template B, and Template B needs to know about Template A. Who goes first? Most systems tell me to manage this manually. Order my timestamps. Cross my fingers. You do something way smarter: two passes. Pass 1: Create everything. All fields, all templates, all roles. They exist as empty vessels. Pass 2: Configure everything. Wire up parent-child relationships, attach fields, set permissions. Everyone knows everyone now. Circular dependencies become non-circular across passes. No timestamp juggling. No depends_on arrays. It just works. And if I need to inject imperative code at the right moment, you give me four lifecycle hooks: beforeAssets, afterAssets, beforeData, afterData. Escape hatches at exactly the right places. Not too early, not too late. I’ve used these to install modules, create reference pages, and detach stale fields, all in the right order, every time. You’re Non-Destructive by Default If I remove a field from a template’s config array, you don’t rip it out. You leave it alone. Because you assume I might still need it, or that removing it accidentally would be catastrophic. If I actually want to detach a field, I tell you explicitly: a removeFieldFromTemplate() call, right there in the same config file or in the relevant hook. Destruction is a conscious choice, not a side effect. This alone has saved me from myself more times than I’d like to admit. Modules Ship Their Own Schema A module can drop a RockMigrations/fields/ folder into its own directory, and you just… discover it. No registration. No global config. No “please add this module to your migration scan path.” site/modules/Foo/ ├── Foo.module.php └── RockMigrations/ ├── fields/bar.php → foo_bar └── templates/baz.php → foo_baz You auto-prefix field names with the module name. You auto-tag them. When I look at a field in the admin, I can see which module owns it. And when the module uninstalls, its ___uninstall() method can call a migration script that cleans everything up, fields, templates, the lot. Optionally guarded behind a checkbox in the module config so the site admin decides whether to keep the data. No manual cleanup. No orphaned fields haunting the database for years. A module is a self-contained package: code and schema. And you respect that boundary. No sprawl into a shared site/migrations/. No “did file 47 or module X create this field?” The answer is in the file path on disk. This design choice makes every module I’ve built feel cleaner and more portable. Free Constants, Free IDE Support By just creating a marker file (RockMigrationsConstants.php), you auto-generate class constants for every field and template: // Auto-generated. Do not edit. class RockMigrationsConstants { const field_subtitle = 'subtitle'; const template_award = 'award'; } Now my IDE autocompletes RockMigrationsConstants::field_... and I never type "subtitle" as a raw string again. No typos. No silent breakage when I rename a field. You generate this from the config files, the same single source of truth. Silly bugs prevented. You’ve Grown Without Bloating You’re not a proof of concept. You’re not a shiny new thing I’m nervously trying in production. You’ve grown organically across versions, picking up MagicPages, deploy helpers, auto-release workflows, and about forty other things. You’re the Swiss Army knife that somehow doesn’t feel bloated. I’ve watched you mature over the years. Every feature felt like it earned its place. Nothing feels bolted on. Things that did at one time got removed. Other solutions are ... not bad Look, I need to say this clearly: the other migration approaches out there are not bad. They’re genuinely good work by talented people, and I’m grateful we have choices. A ProcessWire ecosystem with multiple migration approaches is a healthy ecosystem. Every approach has its place and its audience. But every time I sit down and compare feature to feature, RockMigrations comes out uniquely complete. Declarative config? Check. Two-pass circular dependency resolution? Check. Module self-containment with auto-discovery? Check. Non-destructive by default? Check. Auto-generated constants? Check. Battle-tested over years? Check. Not all in one package anywhere else. So if you’re on the fence, give it a spin. Create a site/RockMigrations/fields/ folder, drop in a config array, refresh modules, and watch it work. You might just stick around for years too. Yours declaratively, a long-time user
    7 points
  30. Whether in the forum or in a blog post or anywhere, I wouldn't post anything that I knew contained incorrect info. The API.md files I've been adding to the core are started by claude sonnet, and it uses my phpdoc and the method implementations to craft the text. After that I spend quite a bit of time fixing details, changing wording, creating better example code, etc. Then after I finish with my changes, I send them to Codex GPT 5.5 to test all the examples and make sure everything works as written. So when I mentioned AI generated content I suppose I should say that it's much more collaborative than that. If I were to post one as a blog or forum post then it would go through the same process and scrutiny. The main thing is that I don't have as much bandwidth as I'd like for writing blog posts, so they end up being very occasional. But I think having more regular blog posts could be helpful for attracting new users. Maybe I should just make the weekly posts blog posts. But I like posting in the forum because it's not as formal, and of course more likely to spawn good discussions, like here.
    7 points
  31. I’m loving how fast all of this is happening, @ryan, even as I’m struggling to keep up. Don’t slow down! This is good for us all! <pant, pant! 😅>
    7 points
  32. Now available in the Modules directory at https://processwire.com/modules/chat-ai/ and on gitHub at https://github.com/clipmagic/ChatAI What is it? ChatAI is a native ProcessWire AI chatbot module designed to answer questions about your site content, with a focus on: ProcessWire-first workflows Site-aware answers using RAG (Retrieval Augmented Generation) Respect for ProcessWire access control rule Admin visibility into usage and performance Keeping content ownership within ProcessWire Features include AI chat widget for frontend use RAG indexing of ProcessWire content Multi-role message support (system / assistant / user) Admin dashboard with metrics and observations Configurable prompts and behaviour Integration with AgentTools for model selection Role-aware retrieval (users only see content they can access) Frontend page restrictions Dictionary / weighting support for retrieval tuning Quick Start Ensure prereqs are installed, ie: PHP>=8.0, ProcessWire>=3.0.255, AgentTools>=0.1.1, TextformatterEntities, TextformatterNewlineBR Configure your chat LLM in AgentTools Configure your textembedder LLM in AgentTools Configure ChatAI module config with both the above Go to Setup->ChatAI and index your site pages (turn off Dry Run when you're ready). Add the CSS, Script and Widget to your template(s). After that, tweak to your heart's content, including adding languages, widget theming and prompts. Current status: Alpha Test thoroughly before using on live sites. Feedback welcome.
    7 points
  33. Update (1.1.1) – image downloads and UI improvements Hi all, A couple of releases on from the grid-view update, with a proper new feature this time. @adrian asked for a way to pull images back out of the library, so downloads are in: every thumbnail now has a download button that hands you the original file, not the table thumbnail. Tick a few images first and instead of separate downloads it bundles the whole selection into a single ZIP. It uses the same selection you already use for bulk edits, so it stays consistent with the rest of the module. A few things around it got nicer too. The small per-thumbnail actions (replace, delete, download, select) now use a custom tooltip instead of the browser's native one, and on a multi-selection they tell you what the action will actually do, e.g. "Download 8 selected as ZIP". And replacing a file in place finally gives proper feedback right on the row, success or error, instead of failing quietly. One under-the-hood change worth a mention: the module no longer loads on anonymous front-end requests. There was no reason for it to run on a normal public page view, so now it doesn't, and a guest hit carries zero overhead. Admin, logged-in editors and front-end inline editing all behave exactly as before, and the hourly de-duplication / where-used maintenance still runs. (thanks @adrian for the nudge on that one too) Feedback and bug reports welcome, Cheers, Mike
    6 points
  34. Just a note for everyone to be aware of this critical bug in this version if you have any save hooks: https://github.com/processwire/processwire-issues/issues/2269
    6 points
  35. Hi all, I've made the SeoNeo repo public as planned. SEO Neo is a modern SEO module for ProcessWire that started as a practical fix for real-world canonical, pagination, and hreflang bugs on multilingual sites, and grew into a full replacement path for MarkupSEO and Seo Maestro. GitHub: https://github.com/PeterKnightDigital/SeoNeo Current version: 1.1.3 · Requires: ProcessWire 3.0.200+, PHP 8.1+ What makes it different SeoNeo is a coordinator module, not a custom Fieldtype. It creates ordinary ProcessWire fields (Text, Textarea, URL, Checkbox, etc.), reads them via a configurable mapping, resolves fallbacks, and renders the <head> block. That means: Every SEO value is a real PW field — full multi-language support, selectors, import/export No custom database schema or Fieldtype complexity The SEO tab sits alongside your existing Content / Settings tabs Add seoneo_tab to a template and save — the rest of the SEO fieldset (seoneo_preview, title, description, canonical, robots, etc.) is inserted automatically. What it outputs Full <head> SEO block in one call: echo $page->seoneo; // or echo $page->seoneo->render(); Includes: <title> with configurable format, separator, site name, pagination placeholders Meta description, keywords, author Canonical URL (with configurable URL-segment and pagination policy) Robots meta (noindex/nofollow per page, auto-noindex for unpublished/hidden pages, site-wide defaults) Granular Google robots directives (max-snippet, max-image-preview, etc.) AI/LLM opt-out signals (noai, noimageai) — polite signals, not a substitute for blocking bots at HTTP/robots.txt level Open Graph (title, description, url, type, site name, locale, image + dimensions/secure_url/type) Twitter/X cards (auto summary vs summary_large_image) Hreflang alternates with configurable BCP47 map (default=en-GB, de=de-AT, etc.) Search-engine verification tags (Google, Bing, Yandex, Pinterest, Facebook, Baidu) JSON-LD @graph emitter (Consider BETA IE works, but API/defaults may still change; hooks recommended for production-critical schema) Partial renders and resolved values are available too — flat API ($page->seoneo->renderOg()) or SeoMaestro-style namespaces ($page->seoneo->og->render()). Everything is hookable. Editor / admin features Bundled InputfieldSeoNeoPreview (installs with the module): Live Google SERP preview that updates as you type Desktop / mobile toggle — mobile truncates earlier (separate char budgets) Multilingual language switcher on the preview card Surface-aware character counters (green/amber/red zones, optional hard maxlength) Per-page noindex/nofollow checkboxes Optional NEO badge on the Wire tab — handy when running alongside MarkupSEO's also-named "SEO" tab during migration Configuration highlights Module config covers site name (per-language) title format smart field mapping with ancestor walk (*summary) per-template defaults with placeholders OG image field paths (including dotted paths like banner.image) default OG image locale map Twitter handles auto-inject position canonical policy and more. ProCache: documented and tested on cache-miss and cache-hit paths. Migrating from MarkupSEO or Seo Maestro You can run both modules during migration . You keep legacy fields on the template, copy values into seoneo_* fields at your own pace, then switch templates from $page->seo to $page->seoneo (shape is largely preserved). Migration is being worked on. Watch for doubled <head> output if both modules auto-inject — disable auto-inject on whichever isn't authoritative yet. Quick steps: Install SeoNeo (Modules → Refresh → Install) Add seoneo_tab to templates Copy field values (seo_description → seoneo_description, etc.) Rewrite template API calls Uninstall legacy module when ready Full feature comparison and migration notes are in the README: https://github.com/PeterKnightDigital/SeoNeo#migrating-from-markupseo-or-seo-maestro Deliberately out of scope SeoNeo focuses on <head> SEO coordination, not Swiss-Army-knife extras. For these, dedicated modules are a currently a better fit: Sitemap → MarkupSitemap Redirects → Jumplinks2 or ProcessRedirects robots.txt editor → MarkupRobotsTxt or a template override Analytics/GTM → MarkupGoogleTagManager or similar (A PRO companion bundle with deeper editor tooling is planned separately) Install Copy SeoNeo to site/modules/ Modules → Refresh → Install SeoNeo Add seoneo_tab to any template that needs SEO Feedback very welcome. Especially from anyone migrating off MarkupSEO or Seo Maestro on multilingual or ProCache sites. Issues and PRs on GitHub are the best place for bugs and feature requests. Cheers, Peter
    6 points
  36. "Claude please convert for me the processwire.com/talk IP.Board into a native ProcessWire CMS/CMF application. Make no mistakes." 😊
    6 points
  37. Hey everyone, MediaHub 1.19.0 is ready. That is good news for two reasons: you might find it useful, and you might be too distracted to wonder where 1.18 went. 🙂 Anywho, MH 1.19 has two main areas of improvement: the main MediaHub library screen (for editors) a new read API for galleries at scale (for developers). MediaHub Library changes A) Redesigned sidebar The sidebar has been redesigned and refined, with new shortcut filters at the top B) Favourites Mark any asset as a favourite (site-wide) and filter the library to favourites only. You can favourite from the asset detail page, the tile menu, or by dragging tiles onto Favourites in the sidebar. I am still debating Favourite/unfavourite vs star/unstar for the wording. Open to opinions there. C) Recent See your newest uploads and choose what “recent” means: last 24 hours, last 7 days, and other presets. More flexibility planned here soon. D) Filter breadcrumb type thing When you filter the library, a breadcrumb-style trail shows where you are (for example All Assets › Labels: Dublin). Clear a filter with one click instead of guessing how you narrowed the view. E) Labels If you use Labels for organisation, they are now a first-class shortcut in the sidebar, with counts per label. The section collapses when you do not need it. Drag assets onto a label row to assign in bulk; rename or delete from the row menu. F) Hover info on asset cards Hover over an asset to quickly see the title and filename without turning on Details view or opening the full asset page. This way, you get a really clean Library grid and still benefit from an in-betweeny view. G) Favourites (pt2) You can mark an asset as a favourite from the assets card/panel. Admittedly, 'unfavourite' is a little awkward-sounding. How does it read in German, Austrian, Bulgarian, Irish? I'll find out shortly. I might change this to star/unstar. MediaHub - drag and drop Another gap closed. You can drag and drop assets directly onto a MediaHub field. If you want to disable that function, you can do it on a field-by-field basis. You can also re-order assets as per the native input field. And if you're not a drag-and-drop fan, you can use the buttons instead. The MediaHub button will open your Library. Upload button opens your Finder. Uploads and drag-and-drop currently land new files in the root of the library in the background. I am planning more flexibility there soon. getRaw () API (beta) I'll be straight: I have not fully tested this myself on production sites yet, but I did run it through rigorous automated test scripts, and the performance benefits look promising. There is a more comprehensive write-up in the MediaHub docs. And I want to thank @David Karich for pushing this feature forward and flagging the original limitation in the first instance. Not strictly 1.19 related Search the docs The MediaHub documentation has a new search box in the sidebar Limitations and planned improvements Created a page with the known gaps in MediaHub. This is going to be a living (but rapidly shrinking) list as I move towards MediaHub 1.20.X. Get 1.19: About MediaHub: https://www.peterknight.digital/products/mediahub/ Upgrade to 1.19: https://www.peterknight.digital/downloads/mediahub/ Read the blog: https://www.peterknight.digital/blog/posts/mediahub-1-19-release/ Full changelog: https://www.peterknight.digital/docs/mediahub/v1/changelog/ Cheers 🍻 Peter
    6 points
  38. Hi everyone, I’ve released a new ProcessWire module: Oidc. Oidc adds lightweight OAuth 2.0 and OpenID Connect login to ProcessWire sites. It is meant for projects that need social login or company SSO without a full front-end account-management suite. Repository: https://github.com/mxmsmnv/Oidc What it does Adds OAuth/OIDC login buttons to ProcessWire templates Supports Google, GitHub, LinkedIn, Microsoft, Yandex and Yahoo out of the box Supports custom OIDC providers via discovery URL Works with providers such as Okta, Auth0, Keycloak, authentik, Azure AD and Dex Handles callbacks automatically on frontend pages Supports auto-registration of new ProcessWire users Preserves return URLs through the login flow Includes silent mode for SSO-only sites and intranets Provides hooks for identity resolution, login, registration and provider definitions Verifies OIDC id_token claims with nonce, issuer, audience, expiry and RS256/JWKS checks when available Basic usage After installing and configuring providers, render login buttons in a template: $oidc = $modules->get('Oidc'); echo $oidc->renderButtons(); The page that renders the buttons is also the callback page. Set that URL in the module settings and register the same URL in your OAuth/OIDC provider app. Example use cases Add “Continue with Google” or “Continue with GitHub” to a member area Use Okta/Auth0/Keycloak for company SSO Protect an intranet or private section with silent SSO Auto-create ProcessWire users after successful provider login Add custom rules through hooks, for example restricting registration to a company email domain Documentation The README gives the overview, and the repository includes detailed documentation and agent-readable integration notes: README: https://github.com/mxmsmnv/Oidc Documentation: https://github.com/mxmsmnv/Oidc/blob/main/DOCUMENTATION.md Agent guide: https://github.com/mxmsmnv/Oidc/blob/main/AGENTS.md Requirements ProcessWire 3.0.200+ PHP 8.1+ curl openssl The module is MIT licensed. Feedback, bug reports and suggestions are very welcome.
    5 points
  39. How KeyHelp for ProcessWire was built: from a phone idea to a tested module I have been building modules for ProcessWire for a long time, but this one had a slightly different origin story. KeyHelp started not as a carefully planned “module project”, but as a practical question while I was away from my computer: What if ProcessWire could manage a hosting panel directly from the admin area? The initial research happened on my phone. I was looking at how other systems handle hosting integrations, especially WordPress. WordPress has a huge ecosystem around this: hosting panels, one-click installers, backup plugins, deployment tools, billing integrations, and managed hosting workflows. Panels like HestiaCP, FastPanel, CyberPanel and CloudPanel all have their own approaches to WordPress automation. That made me think about ProcessWire. ProcessWire is a very strong developer CMS, and I have built modules for it for years, but hosting operations usually still live outside the CMS: domain setup, databases, SSL, PHP versions, DNS records, FTP accounts and cron jobs. For editors and site owners, that means switching between the CMS and the hosting panel. For developers, it means repeating small operational tasks that could probably live closer to the project. The research then moved toward German hosting panels. I used Google Gemini on my phone to explore that landscape. The conversation started around hosting panel integrations, WordPress automation, CloudPanel, CLI/API wrappers and then German panels specifically. KeyHelp stood out because it is widely used in the German hosting world and has a REST API. I also looked at the broader German ecosystem: LiveConfig, Froxlor, older i-MSCP setups, and custom panels from German hosts like All-Inkl and Mittwald. That context mattered, because KeyHelp is not just “another control panel”. It belongs to a hosting culture where stability, server control and GDPR-friendly infrastructure matter. After that research, I moved to Claude on my phone for the first implementation attempt. The first prompt was essentially: Need build integration module with KeyHelp web panel for administration Webserver inside PW Then I defined the first scope: Domains: create, delete, list Clients / users Databases Email accounts SSL / Let’s Encrypt PHP settings DNS records A ProcessWire admin dashboard with server status, domains and database widgets API access through a KeyHelp admin key Claude produced the first module draft. It created the initial structure with a module file, assets, views, dashboard, domain list, create forms, SSL/PHP/DNS screens, client views, database screens and basic cache/API handling. But the first version was too monolithic. So the next correction was immediate: Do not make it a monolith. Put the settings into the module. Where is the Process module? That changed the architecture. The module was split into: KeyHelp.module.php for configuration, API client, cache, debug logging and module settings ProcessKeyHelp.module.php for the ProcessWire admin UI later, multiple traits under src/ProcessKeyHelp/ views under views/ CSS/JS and Remix Icon SVG assets under assets/ That was the first important turn: not just “make something that works”, but make it feel like a real ProcessWire module. Claude also helped shape the first permission model. The early version was basically superuser-only, but it became clear that a real module needed ProcessWire permissions. The model became: keyhelp-view keyhelp-edit keyhelp-delete keyhelp-server So even before the heavy testing phase, the direction was already moving from “prototype” to “proper ProcessWire admin module”. When I got home, I saved the generated module, opened the module folder in Codex, and the real work began. At that point I also decided to buy a fresh Hetzner test server specifically for this module. That turned into its own small side quest: I had to create a Hetzner account, go through identity verification, take a photo of my government ID / driver license, take a selfie, submit everything, and wait for approval before I could even create the server. Only after that could I create the VPS, install KeyHelp, connect it to a real domain, create an API key and start testing the ProcessWire module against a real hosting panel. The server uptime later showed: 17h 12m That became a nice marker of the whole process. This was not just a fake UI built against assumptions. There was a real KeyHelp panel, a real API key, a real ProcessWire installation, and a real remote server used specifically to test the integration properly. The first real problem appeared immediately in ProcessWire: Cannot reach KeyHelp API: cURL: Could not resolve host: key.smnv.org That led to the first practical improvements in Codex: API URL handling; API access notes; SSL verification settings; DNS resolution override; better module configuration hints; safer debug logging. The KeyHelp panel showed API version 2.14, so the implementation was aligned with the KeyHelp REST API docs. From there, the module became a loop of implementation, browser testing, corrections and UI review. This was the main Codex phase. I had Chrome open with two real things side by side: the KeyHelp panel on the test server; the ProcessWire admin module running on my site. Codex was editing the module files locally, and I was checking the result in the browser. I asked it to click through pages, inspect forms, open tabs, check GET/POST behavior, test UI states, and compare the result with what I saw. The module was tested in Chrome and in the in-app browser across: dashboard domains domain view SSL tab PHP tab DNS tab clients databases email FTP SSL certificates cron jobs installer server page module settings A lot of the work was not glamorous. It was small things that make a module feel real: tabs did not work correctly at one point; buttons had unwanted underlines; Font Awesome icons were jumping because more than one icon set was involved; icons were replaced with Remix Icon SVGs; tables were too custom and started hiding API data; badges were too small; breadcrumbs were inconsistent; some titles and breadcrumbs were not translated; German labels were missing in places; uk-table-small, uk-margin-remove, kh-mono and other styling leftovers had to be removed; dashboard and server screens needed better information layout; module settings needed better UI/UX; slow API pages initially felt frozen, so lazy background loading was added. This is where the human + AI workflow was useful. Codex would implement a change. Then I would look at the browser and say things like: “the tabs are broken” “what happened to the design?” “why is there a big left gap?” “the icons jump” “this table is wrong” “the German version still says Refresh” “this should look like Ichiban” “remove custom table styles” “don’t use Font Awesome” “test this in Chrome” “go through all tabs yourself” Then Codex would inspect the code, patch it, test again, and refine the result. There was also a strong UI direction: the module should not look like a random SaaS dashboard. It should feel like a ProcessWire admin tool. Dense, calm, useful, and close to the Ichiban / AdminThemeUikit style. No decorative landing page, no marketing hero, no unnecessary cards inside cards. The installer became the biggest feature. The goal was: From ProcessWire Admin, choose a KeyHelp domain, choose ProcessWire stable or dev, and let the module install ProcessWire there. That meant the module needed to: resolve the domain document root; support the case where KeyHelp and ProcessWire run on different hosts; support local and SSH remote deployment; download the ProcessWire archive; unpack it; create a KeyHelp database and database user; generate a random admin URL; generate secure credentials; run the ProcessWire CLI installer; adapt Apache configuration for KeyHelp; remove installer files; store credentials securely. At first, the installer assumed the document root was visible locally. That was wrong for real deployments. The KeyHelp panel can be on one server and the ProcessWire admin module on another. So the installer had to learn the difference between local filesystem and remote SSH deployment. That was a very important correction. The module should not only work on my local setup. It should handle the realistic case where the ProcessWire admin site and the KeyHelp hosting server are separate machines. Then came another important question: After ProcessWire is installed, where are the generated credentials stored? The answer became: in a module-managed table, encrypted using the ProcessWire site salt. The domain list now shows a ProcessWire badge when an installation is known, and the domain detail page can show stored admin URL, admin login and database credentials to users with the right permission. There is also a GitHub module installer for already installed ProcessWire sites. It checks whether ProcessWire exists in the target path, downloads a GitHub repository archive, validates PHP syntax, and copies it into /site/modules. The server page also evolved during testing. At first it was too raw. Then it became a proper admin view with: server information; software versions; resource usage; services; server reboot action; links back to KeyHelp where the API does not expose something. A specific note was added because the KeyHelp REST API does not expose start/stop/restart endpoints for individual services. Server-level reboot is available through POST /server/reboot for KeyHelp 26.0+. Debugging support was added too. The module can log sanitized API requests, payloads, responses, cache hits and timings to keyhelp-debug, while redacting sensitive values like API keys, passwords, tokens and secrets. One of the important late-stage changes was performance. Some KeyHelp API calls could take many seconds. At one point Tracy showed pages taking a long time because data was being fetched directly before the admin page rendered. That is bad UX: it makes the admin feel frozen. So the module was changed so slow API-backed pages render an admin shell first, then load data in the background. The user sees that the page is alive, and the data fills in when ready. The module also gained English and German UI support. Since KeyHelp is a German hosting panel, German translation was not just a nice extra. It made sense for the module’s audience. The documentation evolved during the same process: README was rewritten in a release-ready style; German documentation was added; CHANGELOG was made first-release friendly with only an Added section; sponsorship metadata was added; version was set to 100; AGENTS.md was added so future AI agents understand how to work with the module, what not to touch, how the installer works, what API calls exist, and how to test safely. Near the end, the repository was cleaned up: the monolith was split into src/, views/ and assets/; tests were kept local and ignored; unused generated image assets were removed; README was kept version-free; the GitHub branch was recreated using an orphan commit so the public repository starts cleanly with one init commit. The final result is not only a KeyHelp API wrapper. It is a ProcessWire admin integration for a German hosting panel, with a real installer workflow for creating ProcessWire sites on KeyHelp domains. The full path looked roughly like this: I researched hosting integrations and German panels with Google Gemini on my phone. I used Claude on my phone to create the first KeyHelp module prototype. I corrected the architecture so it became a real ProcessWire module pair instead of a monolith. I created a Hetzner account and went through ID/selfie verification. I bought a fresh test server specifically for this module. I installed KeyHelp on the server. I saved the generated module locally. I opened the module folder in Codex. I connected the module to the real KeyHelp API. I tested the UI and API workflows in Chrome. I repeatedly corrected UX, tables, icons, tabs, translations and layouts. I added the ProcessWire installer. I added SSH remote deployment. I added encrypted credential storage. I added GitHub module installation for existing ProcessWire sites. I added lazy loading for slow API pages. I cleaned up README, German docs, CHANGELOG and release metadata. I published the repository as a clean orphan init commit. This is what I found interesting about the process: the AI did not replace testing or product judgment. Gemini helped with research and context. Claude helped create the first prototype. Codex helped turn it into a tested module by repeatedly editing code, running checks, using Chrome, and reacting to very specific feedback from the real UI. The quality came from the loop: build, open in browser, click, notice what feels wrong, fix, test again. The test server uptime, 17h 12m, is a funny detail, but it captures the mood of the whole thing: a fresh server bought just to make sure this module was not theoretical. Repository: https://github.com/mxmsmnv/KeyHelp
    5 points
  40. This is already possible, but you do need either an external LLM or a good local one (like Qwen Code 2.5, or Gemma 4) with the hardware to handle it. I am running Qwen 3 and Qwen Code 2.5 on my iMac and they are good, but slow (my iMac is from 2017) and I'm using 7-9b parameter models, so they are pretty limited in what they can do. These local models have ProcessWire knowledge, but not at the level that external models do. Qwen 3 is cool because you can watch its thought process before it answers your prompt. But Qwen Code 2.5 does better with PW, despite being older. I think it's highly unlikely that Rambler would ever be good enough to even be a replacement for a pre trained local model. Even those open source models have millions of dollars behind them. But I am hoping that Rambler gets good enough to have some sort of production value eventually, so I'm going to keep rambling on. 🙂
    5 points
  41. Update: v1.0 – out of beta (and nested collections landed) Hi all, after a good run in production across a few sites, we’re finally tagging a stable release: v1.0.2. Quick recap for anyone just finding this: Image Library is a Process module that surfaces every image already sitting in your native FieldtypeImage fields, across every page, template and (Matrix)Repeater, in one cross-site, filterable, inline-editable table, with serious bulk editing, content-based de-duplication and where-used on top. It does nothing to your data and needs no migration. The full story is in the three posts above. Collections, all grown up. What was a flat “tick some images, save them as a tab” feature is now a proper little hierarchy: Folders / nesting. A collection can hold its own images and nest sub-collections, up to 3 levels, like folders that also hold files. There’s no separate “container” type: an empty collection is just one with no images yet. Open a parent and you see the union of its own images plus every descendant’s; remove an image from a parent and it cascades down. A manager dialog. One tabbed dialog (Collections | Bookmarks) to rename, reorder, nest / un-nest and delete, by drag or buttons. Deleting a collection cascades to its sub-collections; the images stay put in the library. A Collections column. A new table column shows the collections each image belongs to (linked), with inline re-assignment via a checkbox tree, for one row or a whole batch selection. Membership shows as a union, so a parent lights up whenever the image is in one of its sub-collections. Team-wide now. One thing changed under the hood: bookmarks and collections moved from per-user storage to a single team store, so the whole editorial team works from the same set. Gated by a manage-shared permission; bookmarks got folders too. And on the phone: the strip collapses to three tabs (Show all / Bookmarks / Collections) with cascading tap-to-open fly-outs, so curation works on touch, not just desktop hover. Search that actually narrows. The search box learned operators: multiple words match any (OR), +word requires, -word excludes, and "a phrase" matches exactly, e.g. red +rose -draft. Across title, description, tags, filename and custom subfields. Why “stable” now. Beyond the features, this release got the boring-but-important pass: uploads on Replace are validated by content and not just by extension, a rename now keeps an image’s “Used in” entry, output is escaped, lookups are hardened, and the codebase went through a sizable DRY refactor with no behaviour change. It has been running real editorial work, so 1.0 felt earned. Status. v1.0.2, first stable. Module + docs on GitHub and the Modules Directory. As always, feedback and edge cases very welcome: deep Repeater / RepeaterMatrix nesting, odd Fieldtype combos in custom-field templates, ProFields, and anything the collections manager or front-end picker trips over. Cheers, Mike
    5 points
  42. No Num-Pad you say? Then you aren't ready for this... Vortex Core, Cherry MX Brown switches...
    5 points
  43. @AndZyk It's been requested by another person and I was just waiting for someone else to ask for it. I've added a setting (in tomorrow's version) that let's you choose where you want the response to appear: notification, in the field, or both. In my case, I need the "both" option because my PageEngineer fields are pretty far down on the page. If there's no notification, the person editing sometimes misses the response. And if it doesn't appear in the PageEditor field, then you don't see what you are replying to. But now you can choose what works best for your case. I agree, tomorrow's version will also repeat the prompt above the response. Maybe for a future version. Right now PageEngineer is a Fieldtype using an Inputfield that's part of the page editor form, and that gives you the flexibility of putting it where you want it. The current setup means you could always present it as its own tab in the page editor too. But I do like your idea as a potential option for a future version. I found that some of these requests can take quite awhile to complete and something more than a spinner was needed to communicate what was happening. I asked Codex to come up with a spinner that better represented the thinking process that takes place when you submit an engineer request, and I was pleasantly surprised by the result. Like anything else, if some people don't like it we could always make it optional. It's possible, but it depends on the Inputfield supporting it. Most core ones do, but at this point I prefer predictable reliability, aiming for stuff that will work everywhere. As the AgentTools module matures maybe more options like this can be available. Not all models support image files, though I think most of the big ones do. Likely we'll add this at least to the site engineer. Maybe for page engineer too, though you could do it now just by adding a file field to the page and telling page engineer to use it, whether in a prompt, or in the system prompt defined with the page engineer field.
    5 points
  44. Thank you @ryan. I really appreciate it. But in case anyone wants to try OpenCode Go, please use Ryan's link so ProcessWire development will benefit from it.
    5 points
  45. I just want to say a big thank you to everyone who has been part of the first round of early testing. I'll give you all a proper shoutout soon. I have capacity for roughly 2 to 5 more testers. If that sounds like you, DM me and I'll send you a full copy to try on a local install a generous discount code valid for 30 days if you or your agency wants to move ahead Let's keep it simple - all I ask is that you flag any bugs or UI issues and give it proper, consistent use as you develop your site. You can install MediaHub on your site and run it in tandem with your existing Input Fields, or you can use it exclusively for all your media management. I'm working on 1.19 right now, due for release shortly. Once 1.20 is out, the launch price expires. Cheers P
    4 points
  46. What I really like about this coming of AI to ProcessWire is the discussion, the human communication it brings. Ryan is active and passionate in the forums like we hasn't been for a while. We all are once again experimenting and regaining faith in that our beloved computer thing will not stagnate but make its way onto the next level. I do not want that to end in no way. The same time I am too a bit afraid of AI lowering the quality of content here and the product itself. Too much enthusiastic awe and uncontrolled trust in AI could lead us there. So our collective goal is to balance things up. I can see you Ryan delighted to share the cool things you've built with AI with us all. And I personally want to read those blog posts. But if they get published alongside with your content unchecked or even checked but still containing some mistakes, that could not only turn away human readers, but also be the source for future less good answers from AI itself, as it is reading its own writings for sure. We could create a dedicated site section or dedicated forum section for AI-written articles. And maybe make it non-indexable by search engines. We should add an explicit warning like @Peter Knight suggested for sure. And maybe propose a game/challenge for the forum members to read them critically and comment on them. Maybe establish a workflow for AI to respond to those comments and to correct the text. We can really come up with something quite good in the end. Not only better texts that could be published. But also smarter AI and a more cohesive community. What do you think? P.S. We probably should also limit the amount of those articles. Humans are not as capable of reading as AI is of writing. The new era of a consumer rule is already here - it is harder and harder to find a good consumer for your stuff, especially something like a blog post)))
    4 points
  47. A big thanks to @@Mikel for flagging some of the items which form 1.19.1. Uninstall (fixed) The safety check that requires you to opt-in via `$config->MediaHubUninstall` in `site/config.php` was firing a beat too late. By the time the error showed, companion modules and their pages were already partially torn down. 1.19.1 hoists that check into a `hookBefore('Modules::uninstall')` so nothing is touched if the flag is missing. Also tightened companion module cleanup, admin page deletion, and crop-field removal so the whole flow is now atomic: succeeds completely or aborts cleanly. Import (fixed) Importing existing images on a non-MediaHub site (Setup → Media Hub → Import Existing) wasn't generating thumbnails. Bonus: retina `srcset` (1x/2x) and lazy loading on inputfield thumbnails. Icons (aligned) The library and the MediaHub inputfield each have view-mode toggles (grid, proportional or masonry, detail or list), but 1.19.0 used slightly different icon sets between the two surfaces. 1.19.1 aligns them on the same Lucide set. Labels bug (fixed) The new Labels section in 1.19.0 shared structural class names with Collections. This meant Labels were incorrectly displayed on some Collections filters nd menus. Labels and Collections are now strictly separate row types throughout the sidebar. Full changelog 1.19.1 blog post 1.19.1 download
    4 points
  48. I couldn't agree with this "love letter" any more than the others already have! When we explored the possibilities of SiteSync, the interplay with RockMigrations and coding agents opened up an incredibly fascinating workflow that we wouldn't want to do without anymore. Our front-end and back-end developers have barely needed to talk to each other since. 😅 Fantastic module, thanks a lot, @bernhard!
    4 points
  49. I love this Love Letter! Not only because it's true, but because it showed me something I didn't know about RockMigrations - something I always wanted but couldn't really explain, something that was already there. Or as it is called Config Migrations. Buried deep, somewhere in the docs (at least for me). https://github.com/baumrock/RockMigrations/tree/main/docs/config-migrations I didn't know that. At all. I tried to handle and manage so many site/migrate.php files but it never felt right for me. At some point I lost track of changes, those hundreds of lines of code became unmaintainable for me. Wasn't fun. Stopped using it. One project after the other. Never really tried again. Did it the old way. But this, config migrations, this is a game changer for me. Sure, I quite often sleep under a stone, but this. Thank you @gebeer for making this your example. Thank you @bernhard for making RockMigrations - and this is exact feature. 🤯
    4 points
  50. For a number of years we've grappled with how best to implement on-page structured data via JSON-LD. For almost all projects we've ended up using a code-based solution, with a standard JSON-LD output defined for all pages/templates, with any template specific structure defined in the template file (e.g. Article for a post template). This approach isn't particularly malleable, at least not for clients who actually want to implement JSON-LD themselves or via their SEO partners. MarkupJsonldModels is an attempt to find a good solution that's workable for everyone, including AgentTools. It uses the concept of "models" - valid JSON-LD using placeholder tags which resolve to actual page values when output on a rendered HTML page. It allows you to define a default model in the module config, a model for each template (assuming it will be output as HTML), and adds a field (jsonld_model) on installation which you can add to a template to allow a model to be defined per page. As GEO becomes more and more important, this is a module I hope will prove quite useful, however my own motivation for it is more laziness than anything else - I wanted to build something where I could just get an AI Agent to suggest and implement models via Agent Tools! Preliminary tests with this approach have been reasonable, and I can definitely see it being a time saver when I get it integrated into our dev process, and I expect the quality of the JSON-LD will end up significantly better too. However, it doesn't require Agent Tools to work - but an ability to work with JSON is... It is currently in Alpha, as it really hasn't been tested in the wild (just yet). I expect there will be another couple of rounds of dev and improvements once we start using it for existing sites that are current looking for GEO improvements. There's a pretty lengthy README - https://github.com/nbcommunication/MarkupJsonldModels/blob/main/README.md - which is likely a giveaway that I've been using Agent Tools (Claude 4.7) to assist in code and documentation review! Any thoughts and feedback would be much appreciated. Chris
    4 points
×
×
  • Create New...