Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 03/27/2026 in all areas

  1. Sooo, I'm still alive and in August, I will start on a new job and project that probably brings me back to work with the cool and great Processwire. :D 7 years ago I was forced to change job and ended in a cool new place doing front-end dev not using PW anymore. So unfortunately I didn't really use or follow PW in that time except once a year doing something tiny bits on the handful of websites I am responsible for. I'm sorry if I just disappeared "over night" and maybe left some things behind I was doing for PW, and didn't spend time looking out for them. The reason is, I also was very frustrated with a lot of things with the job and life at that time and 2019 was also when I started painting again digitally, as maybe some of you know. I went full hyper focus mode, everyday almost for 2-3 years in my spare time and since then slowed down. I was able to make a small career with it, and made a lot of new connections and experiences which was awesome. I will continue to work on making art and illustrations as I have a lot of new freedom with the new job too combine a lot of my skills. Finally I can work from home full time. A little dream come true. Thanks for still being here and keeping this small but awesome community alive! I have to catch up now! :D Cheers Soma
    16 points
  2. Cre8aplace -- ProcessWire Showcase Overview Cre8aplace is a full-featured social networking platform built entirely on ProcessWire 3.0.255, PHP 8.4, and plain JavaScript/HTML/CSS. No frontend frameworks, no build tools, no bundlers. It delivers the core experience people expect from a social platform -- profiles, messaging, groups, newsfeeds, marketplace, blogs -- while prioritizing user privacy and data control. The site is currently in soft launch at [cre8aplace.com](https://cre8aplace.com). I started this project over five years ago and was recently revived with the availability of time (I'm retired). The goal was to build a platform where users own their experience -- where no data is sold, no algorithms manipulate what you see, and the platform exists to serve its members rather than advertisers. I've used ProcessWire for many years on various projects so it was the natural foundation. Ryan's philosophy of minimal opinions and maximum flexibility aligned perfectly with my project that needed to bend a CMS into something it wasn't explicitly designed for -- a real-time social networking platform. Where other CMSs would have fought at every turn with rigid content models and opinionated routing, ProcessWire simply got out of the way. A major thank you to Ryan! What ProcessWire Solved User Management Without a Separate Database ProcessWire's user system eliminated the need for a standalone user table entirely. Each member account carries approximately 50 custom fields -- everything from profile settings and privacy controls to notification preferences and subscription status. PW's field system handles all of this natively, with no separate profile table, no ORM, and no migration headaches when fields change. The Entity Page Tree Every entity type on the platform -- Groups, Pages (business presence), Events, Markets, Blogs, Movies, Games -- lives as a ProcessWire page with its own template. The page tree gives a natural hierarchy: /groups/ /groups/hiking-enthusiasts/ /groups/local-music/ /pages/ /pages/joes-coffee/ /markets/ /markets/handmade-goods/ Each entity type gets its own template file, its own toolbar configuration, and its own permission rules, but they all share the same rendering components (page header, toolbar, left aside, right aside). ProcessWire's template system made this architecture effortless. Flexible Templating as a Router I used ProcessWire's template system not as a traditional page renderer, but as a thin router/dispatcher. A single 'profile.php' template handles 12 different operations (timeline, account settings, privacy, messages, friends, membership, etc.) by dispatching to focused include files. Each include contains paired display and process functions. PW's template prepend (_init.php) bootstraps every request with shared utilities, configuration, and security setup. Admin Backend for Free ProcessWire's admin interface provided a complete backend without building one. SVG icons are stored as PW pages (37 icons, each with SVG markup in a textarea field). Site-wide configuration lives in a JSON field on a settings page, accessible from both PHP and JavaScript. Reaction types, emoji sets, and content categories are all managed through PW's admin. Authentication Built on Solid Ground Rather than building authentication from scratch, I extended ProcessWire's session and user system with custom email verification, token-based password reset, persistent remember-me login, and bot prevention. PW handles the password hashing, session management, and role assignment while I built the user-facing flows on top. Role Hierarchy The platform's permission system maps directly to ProcessWire roles: member < moderator < owner < administrator < superuser PW's role checking ($user->hasRole(), $user->isSuperuser()) powers everything from toolbar visibility to AJAX endpoint authorization. Group moderators and page employees add entity-scoped permissions on top of the global role hierarchy. Custom ProcessWire Modules I built four custom modules over the years while working on various other projects that extend ProcessWire's capabilities: - ViridiaCaptcha -- Bot prevention for registration and authentication forms. Integrates directly with PW's form processing. - ViridiaCalendar -- A full event calendar with RSVP tracking, recurring events, ICS, and profile-level event management. Renders calendar views and manages event CRUD through ProcessWire's module API. - ViridiaTicketing -- Support ticket system for user-to-admin communication. Built as a PW module with its own database tables and admin interface. - ViridiaGovFeed -- An automated government legislative feed that fetches federal bills from Congress.gov, executive orders and rules from the Federal Register, and state-level legislation via the LegiScan API. It publishes curated topics to a system-owned Page entity. Users follow the page to see legislative headlines in their newsfeed. Runs on a 6-hour cron cycle with deduplication and fetch logging. Architecture Highlights Hybrid Data Model I use ProcessWire pages for entities and users, but custom MariaDB tables for high-volume relational data. I created over 20 custom tables to handle topics, comments, replies, messages, relationships, reactions, notifications, marketplace orders, product variants, reviews, and more. This hybrid approach gives me the best of both worlds -- PW's flexible page management for entities, and direct SQL performance for data that sees thousands of writes per day. Encrypted Control Parameters What I am most proud of is no database ID ever appears in a URL, query string, or HTML attribute on this platform. Every sensitive identifier is encrypted with AES-256-CBC using ProcessWire's private key, passed as an opaque 'data-param' attribute, and decrypted server-side before processing. JavaScript never sees a real ID -- it just passes encrypted blobs back to the server. This is the backbone of the platform's privacy guarantees. AJAX-Driven Interface The platform runs over 50 AJAX actions across 8 endpoint routers. Every interactive operation -- posting a topic, sending a message, toggling a reaction, managing a marketplace order -- happens through AJAX calls that return JSON with server-rendered HTML. No client-side templating, no virtual DOM, no state management library. The server renders the HTML, the client inserts it. Single CSS File, No Framework The entire platform's styling lives in a single 132KB CSS file. Layout uses CSS Grid (3-column desktop, single-column mobile). Theming uses OKLch color space with CSS custom properties -- users pick an accent color via a color picker, and the entire UI adapts through computed color relationships. No Bootstrap, no Tailwind, no preprocessor. No Build Pipeline I wrote 23 JavaScript files as plain ES6+ and served directly. For production, a PHP-based minifier (matthiasmullie/minify) compresses them, and a GUID generator creates randomized filenames for cache busting. The entire "build" process is three shell commands. No webpack, no Vite, no npm. External Dependencies The platform uses only three external dependencies: 1. SunEditor (MIT license) -- WYSIWYG editor for blog posts. The only client-side library on the platform. Everything else is hand-written JavaScript. 2. Stripe PHP SDK -- Powers three subscription tiers (Free at $0, Standard at $8.95/month, Extended at $16.95/month) and marketplace seller payouts via Stripe Connect Express. The SDK is vendored directly in the project -- no Composer. 3. Wasabi S3 -- Cloud object storage for album images and marketplace product photos. Accessed via direct S3 API calls from PHP. Everything else -- UI components, animations, form handlers, real-time polling system -- is custom PHP, JavaScript, HTML, and CSS. Platform Features Social Core - User profiles with visitor viewing via opaque GUID URLs, granular privacy controls, and friend relationships with request/accept/block workflows - Newsfeed aggregating activity from friends, followed groups, pages, and markets - Topic creation with multi-image upload (adaptive grid layout, each with caption and tags), comments, replies, reactions, emoji picker, @mentions, link previews, and topic sharing - Real-time messaging supporting direct messages, multi-user conferences, group chat, page staff chat, system announcements, and moderation incident channels - Notification system with smart aggregation (groups repeat members until read), role-based filtering, and infinite scroll - Carousel view of multiple images with editing Community Entities - Groups with public/private/secret visibility, moderator roles, member approval, post approval queue, rules display, and ownership transfer - Pages (business presence) with follower system, employee roles, and staff-only chat channels Premium Features (Subscription-Gated) - Marketplace with Stripe Connect seller onboarding, product variants, shopping cart, checkout, digital downloads via pre-signed S3 URLs, order management, revenue reports, verified-purchase reviews, wishlists, and market following with new-product notifications - Blogs with SunEditor WYSIWYG, full CRUD, and newsfeed integration - Movies and Games as browseable entertainment entity types - Events powered by the ViridiaCalendar module with RSVP tracking - Schedule future topic postings - State-level government legislation tracking (3-state limit on Standard, unlimited on Extended) Administration - Admin panel with a reusable data table module -- a config-driven system where new admin tables require only a configuration array. Six tables migrated: Users, Groups, Pages, Categories, Reactions, Reports - Content moderation with report handling, per-report discussion channels, and moderator action tracking - Government legislative feed management with force-fetch and status monitoring By the Numbers | Metric | Value | | ------------------------- | ---------------------- | | Custom database tables | 20+ | | SQL migration files | 46 | | Custom user fields | ~50 | | JavaScript files | 23 (~500KB source) | | CSS | 1 file (132KB) | | PHP include files | 50+ | | Custom PW modules | 4 | | Utility functions library | 113KB (_functions.php) | | AJAX actions | 50+ | | npm dependencies | 0 | | Composer dependencies | 0 | | Build steps | 0 | Technology Stack - CMS: ProcessWire 3.0.255 - Language: PHP 8.4 - Database: MariaDB 15.0+ on Debian 12 - Server: Apache 2.4 - Frontend: Plain JavaScript (ES6+), HTML5, CSS3 - Payments: Stripe (subscriptions + Connect marketplace payouts) - Storage: Wasabi S3 (images) - Editor: SunEditor (blog WYSIWYG) - Frameworks: None - Build tools: None
    6 points
  3. This week I worked with Claude Code on refactoring the core WireHooks class to improve it in various ways. I do this work because I like to code, so am of course handling that part. But Claude has been very helpful in finding issues as well as reviewing all my code before it gets committed. In this case, Claude found several bugs that are now fixed, and also saved me from introducing more when I added new features. Working with Claude Code reminds me of 1989 when I was in high school learning Turbo Pascal and building a BBS, and getting help from a neighbor and good friend that was doing the same. I learned so much then and now it feels like that time again. But man have things come a long way since then. I asked Claude about helping with the ProcessWire issues and requests GitHub repos, and now it's solving issue reports, replying to them and committing fixes. For simple fixes that involve a line or two, I'm going to let it handle the the whole thing. It's posting under my GitHub account, but it identifies itself as Claude in replies and and in commits. That way it's easy to tell what it does under my account. I had it just cover a couple of issue reports today as a test (see 2195 and 2192), but will be having it do a lot more going forward. Back to the WireHooks class, I asked Claude about writing a commit message for the WireHooks updates, and wow, check this out: Bug fixes in WireHooks: - conditionalArgMatch(): Fix Selector mutation bug where the shared Selector object stored in hook options was permanently mutated after the first call, causing argMatch hooks (e.g. addHook('WireLog::save(=errors)')) to stop matching after their first invocation. - getHooks(): Remove key variable from foreach to prevent shadowing $method param. - getHooks(): Replace array_merge() in loops with direct array append. - addHook(): Prevent 'noAddHooks' option from leaking into stored hook options. - isHookedOrParents(): Use $className string consistently in cache key writes, rather than $class which may be a Wire object. - runHooks(): Scope $useHookReturnValue inside foreach so it resets per hook. - runHooks(): Use strict null comparison ($toObject === null). Bug fixes in HookEvent: - arguments(): Allow setting an argument value to null (use func_num_args() > 1 instead of $value !== null check). - arguments(): Use array_key_exists() instead of isset() for null-correct lookup. - getArgumentNames(): Guard ReflectionMethod instantiation with method_exists() to avoid uncaught ReflectionException for dynamically-added hook methods. - __toString(): Fix argument list building (was appending to string instead of array), add null/bool display, avoid PHP fatal for objects without __toString(). New features in WireHooks: - cancelHooks: Now accepts string values 'before' or 'after' (in addition to boolean true) to cancel only hooks of a given type. For example, a before hook can set $event->cancelHooks = 'after' to cancel remaining after hooks without affecting the hooked method call. - Custom event data carry-forward: Custom data set on a HookEvent (via $event->customKey = $value) is now carried forward to subsequent HookEvent instances within the same runHooks() call. This allows before hooks to pass data to after hooks without using external variables. - getHooks('*'): Supports wildcard method to return all hooks for all objects. Available in debug mode only (uses allStaticHooks/allLocalHooks aggregates). - allStaticHooks: Now tracked alongside allLocalHooks when debug mode is on. - addHooks() comment fix: "If there is a parenthesis" corrected to "no parenthesis". - addHook() exception messages improved with additional context. - runHooks() docblock updated to include 'either' type. - hookTimer() PHPDoc: @param String corrected to lowercase string. New features in HookEvent: - $defaults static property: Defines the canonical set of standard HookEvent fields, used to distinguish custom data from built-in event properties. - $eid property: Each HookEvent instance gets a unique sequential event ID. - set() override: Tracks non-default keys set on the event in $customKeys. - getCustomData(): Returns only the custom (non-default) data set on the event, used internally by runHooks() for the carry-forward feature. - cancelHooks property updated to support bool|string type. Anyway, I know a lot of you here are deep into the AI stuff, and I'm just getting started, so none of this is surprising. But it sure is a pleasure collaborating with Claude Code on this stuff and it brings back that coding wonder and excitement from 1989. Some Claude and Claude Code podcasts I've enjoyed listening to or watching this week include one from AI for Humans Claude is Cooking, Wednesday's AI Daily Brief on How to use Claude's new upgrades, and the Get Educated AI video of how to setup Claude to use your computer and web browser, etc. (though I've not tried this just yet!). Lastly, last week I put together a ProcessWire powered JSON feed of bike tours, and the client prompted that they wanted a map and look what Claude Code did, I'm impressed!
    4 points
  4. @Soma Great to see you back! A couple weeks ago I was just thinking about you and how I wished you were still around here. I've been seeing your amazing paintings on Facebook and figured you had moved on to other things, but thought of sending you a message there, so what a nice surprise to see you here.
    1 point
  5. @szabesz Sorry to hear that. I've been billed from Singapore for Kimi Code... Not sure if it would be useful to add mandarin in the email subject to create urgency? Though I think english is widely spoken there too.
    1 point
  6. @ai_slop No worries. I have news regarding my issue. Finally, today Kimi's support replied! (They are definitely having trouble replying to support requests in a timely manner.) Their reply was a step-by-step guide on how to get to their subscription page, the one we are talking about. They also asked for a screenshot of it, even though I provided it in both of my support request emails. Even though I supposedly deleted my account, I could just log in again as usual. So, I made a full browser window screenshot of the same page you and I have already shared our screenshot of, in which there is no "Cancel Plan" button. Support asked: "If there are any other questions, please contact us." I replied: "Now what?" Just a week or two, and they will probably reply again... Cheers,
    1 point
  7. Hi @Roope, thanks for this great module. I found a markup issue in Email Obfuscation 1.3.1 (ProcessWire 3.x, PHP 8.1+) when an email link contains an aria-label attribute. Reproduction Input HTML: <a href="mailto:mail@domain.de" class="uk-button uk-button-text" aria-label="Send email">mail@domain.de</a> Output with EMO enabled: <a href="mailto:<noscript data-emo="...">Enable JavaScript to view protected content.</noscript>" class="uk-button uk-button-text" aria-label="Send email"><noscript data-emo="...">Enable JavaScript to view protected content.</noscript></a> Expected behavior: the whole <a ...>...</a> should be obfuscated/replaced as one unit, not inject <noscript> into the href attribute. Suspected cause In EmailObfuscation.module (obfuscate()), the skip pattern for label also matches aria-label, which seems to split the anchor tag before the mailto regex can process it as a full link. As a temporary workaround, removing aria-label from mail links avoids the broken markup.
    1 point
  8. It is really a pleasure and an excitement to see you back, @Soma! I am sure something great is going to happen soon!
    1 point
  9. Welcome back.Soma. So nice to have you around again. Gideon
    1 point
  10. Welcome back Soma.
    1 point
  11. Awesome to see you back around 🙂 Game changer, so glad to hear about this!
    1 point
  12. I've been working on sites where the standard sitemap setup started showing cracks: XML generated on every request eating memory, no way to regenerate automatically without writing custom hooks, and no visibility into what actually ended up in the file. So I built Sitemap — a module that generates static XML files to disk, splits output by template name, and handles the full lifecycle from generation to search engine notification. What it does differently: Writes files to disk instead of rendering in memory — no RAM spike on large sites Pages are fetched in chunks of 500 with uncacheAll(), so memory stays flat regardless of page count Each template gets its own named file: sitemap-product.xml, sitemap-blog.xml, etc. — the index at sitemap.xml references them all Auto-regeneration via LazyCron with a configurable interval; the LazyCron hook slot is chosen dynamically to match what you configured (every hour, every 6 hours, daily, etc.) rather than always using everyHour A needs_regen flag is set whenever a page is saved, trashed, or deleted — visible in the admin dashboard IndexNow support: after generation, all URLs are submitted to api.indexnow.org in batches of 10,000 Sitemap: directive written directly to the physical robots.txt on save and on generate Lock file prevents concurrent generation Admin dashboard at Setup > Sitemap showing file count, URL count, total size, and last generated time Settings stored in a dedicated DB table (sitemap_settings, name/value, MEDIUMTEXT) rather than the module's data field — avoids the serialized config size limit when template settings grow large. Image sitemap extension and hreflang alternate links for multilanguage sites are both supported. GitHub: https://github.com/mxmsmnv/Sitemap Screenshots: Still v1.0.0, so feedback is very welcome — especially from anyone running it on a site with 10k+ pages.
    1 point
  13. I guess this article could be helpful: https://screencasting.com/cheap-video-hosting More details about the Cloudflare R2 pricing: https://developers.cloudflare.com/r2/pricing/
    1 point
×
×
  • Create New...