-
Posts
653 -
Joined
-
Last visited
-
Days Won
7
Everything posted by rick
-
Thanks @szabesz My comparison would be based strictly on licensing and 3rd party dependencies, so it wouldn't be fair. I didn't want to inject any processwire modules/jquery, etc. into the frontend. I leave processwire to do what it does best. I found sundeditor while working on a forum project a few years back. The source css/js files are fairly easy to update/customize, and the plain javascript, MIT license, and source were big selling points. So I can't really offer a comparison as I haven't tried modifying tinymce/ckeditor.
-
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
- 2 replies
-
- 12
-
-
I am creating a module. The first-time install works without error, and the module functions without error. However, when I uninstall the module, then attempt to install the module again, I get the following error: "Unable to install module [module]: Role collision detected ([role]): role already exists." The role was deleted successfully, and no reference to that name exists in the database. Has anyone come across this before?
-
I appreciate that Jan. Works like a charm.
-
Two things first: 1) My searches returned no results for this issue. 2) I don't use github so I do not know how to submit a possible issue. So I would like to ask the community if I have either made a mistake (which is most likely) or have an actual issue; In which case I hope one of you more familiar with github can submit this as an issue. I've create a user profile page whereby the user can upload an avatar image. It works to the point of actually saving the image file in the correct location with the correct filename, but does not assign that to the image field due to the issues noted later. This is the code: ... $upload_path = wire()->config->paths->assets . "files/avatar_uploads/"; if(!is_dir($upload_path)) { if(!wireMkdir($upload_path)) throw new WireException("No upload path!"); } try { $user->first_name = $first_name; $user->last_name = $last_name; $user->user_bio = $user_bio; $user->share_bio = $share_bio; $files = new WireUpload('avatar'); $files->setDestinationPath($upload_path); $files->setTargetFilename($user->name."-avatar"); $files->setValidExtensions(['jpg','jpeg','png']); $files->setOverwrite(true); $file = $files->execute(); //returns array of uploaded filenames if($files->getErrors()){ foreach($file as $filename) @unlink($upload_path.$filename); ... } $user->avatar->removeAll(); $user->avatar = $upload_path.$file[0]; ... $user->save(['quiet'=>true]); The result of which issues two messages: I 've changed the WireUpload.php (I know, but it was a fast fix) to the following: protected function getTargetFilename($filename) { if(!$this->targetFilename) return $filename; // Parse incoming filename $pathInfo = pathinfo($filename); $extension = isset($pathInfo['extension']) ? $pathInfo['extension'] : ''; // Parse the set targetFilename $targetPathInfo = pathinfo($this->targetFilename); $targetExtension = isset($targetPathInfo['extension']) ? $targetPathInfo['extension'] : ''; // Strip off the target’s extension (if any) $base = basename( $this->targetFilename, $targetExtension !== '' ? '.' . $targetExtension : '' ); // Re‑append the incoming file’s extension (if any) return $base . ($extension !== '' ? '.' . $extension : ''); } This runs on Debian 12 Apache2, ProcessWire 3.0.246 Master, php8.4 if it matters. Thanks for your help!
-
@adrian I did a fresh install on localhost and Tracy runs just fine, so there must be something with my server. Although my localhost setup is the same as the server. I'm going to mark this thread as solved.
-
Thanks @adrian! Just an FYI... My server has php tokenizer installed and accessible. Debian 12, php 8.3, mysql 15.1, and apache 2.4 installed Nov '24. I completely uninstalled Tracy, deleted all Tracy related files from the server, and removed all references to Tracy in the DB. I installed Tracy from Modules > New. The Tracy Bar is not displayed at the bottom right of the screen. I went back into Tracy settings and selected the Force superusers into DEVELOPMENT mode. <-- I read somewhere on here about that setting. I submitted that change. The Tracy bar now shows at the bottom right but still displays the previous two errors. I unchecked the Force superuser checkbox and submitted that change. The Tracy bar disappears.
-
It obviously has to be something on my end. You don't need to spend time troubleshooting it since Tracy is just reporting the problem and not failing herself. When I get it sorted, I'll post back. I appreciate your help! Rick
-
Ok, I made the change in PwApiData.php } elseif ($type == 'proceduralFunctions') { $proceduralFunctionsFunctions = $this->getProceduralFunctions('Functions'); $apiData = array('Functions' => isset($proceduralFunctionsFunctions['pwFunctions']) ? $proceduralFunctionsFunctions['pwFunctions'] : array()); if (file_exists($this->wire('config')->paths->core . 'FunctionsAPI.php')) $apiData += array('FunctionsAPI' => $this->getProceduralFunctions('FunctionsAPI')['pwFunctions']); /* elseif($type == 'proceduralFunctions') { $apiData = array('Functions' => $this->getProceduralFunctions('Functions')['pwFunctions']); if(file_exists($this->wire('config')->paths->core . 'FunctionsAPI.php')) $apiData += array('FunctionsAPI' => $this->getProceduralFunctions('FunctionsAPI')['pwFunctions']); */ } ... Cleared DB cache, Refreshed modules, Restarted PW and got the same errors...
-
-
-
Sure thing! Gimme a sec...
-
ProcessWire 3.0.244 PHP 8.3.19
-
lol. You are correct, Sir! This is where I would have told you. 😄 The same error is present. I logged out and shutdown PW and logged back in.
-
Howdy @adrian, I don't know what the previous version was. It was a recent install within a month though. I removed these two resulting rows.
-
Howdy Adrian, I just upgraded to v4.26.67 in an attempt to fix a console error in the previous version. However this new error is different from the last. I read through the earlier post by nurkka, November 19, 2024 but your suggestions there did not resolve my issue. My original error suddenly appeared this morning after logging into the admin. "Error in ConsolePanel" is displayed in the debug bar. Everything was fine last evening before I logged out. This morning after logging in I noticed the error. I followed your suggestions from the post above without affect. After upgrading Tracy to this new version I now get the following error: Undefined array key "pwFunctions" in .../site/assets/cache/FileCompiler/site/modules/TracyDebugger/includes/PwApiData.php:25 This error is different from my original error which referenced a different file -- Sorry I don't have that exact filename as I deleted the cache as one of my troubleshooting steps. I believe it was the cached phpconsole file. Please let me know what additional information you need from me. Thanks! Rick
-
Shoutout to @matjazp! Thanks for all your help!
-
You can check out the blog module. It will handle the article content and related info. As far as the front-end looking like other magazine layouts, there are numerous free magazine html/css templates and more paid templates. You may want to consider a paid template. The free templates look like free templates. ?
-
Basically reiterating @wbmnfktr a Table is semantic while the DIVs are not. There a quite a few google search results for css div tables dating back to '08 where others have attempted replicating the html table using div elements. The main issue nowadays is the device screen sizes. Tables don't scale well at different resolutions. That being said, css has display:table, display:table-row, and display:table-cell type attributes, as well as using pseudo selectors like :last-child to reference specific elements. You could also hide specific table columns at different resolutions. I know this isn't the answer you were looking for, but maybe it helps with segregating large datasets into manageable chunks.
-
Payment related Project, opinions, hints, all much appreciated
rick replied to olafgleba's topic in General Support
Hello @olafgleba , What a fun project to work on. Reading through your post a couple of times it is clear you have everything under control, but simply have a question about processing payments for this parent (registration) -> child (art schools) relationship. First, I have no experience with padloper or formbuilder to say whether they can handle multiple vendors with multiple products. I'll leave that for the more experienced members. Lastly, I would contact stripe support and ask them the best means to handle multiple merchant accounts from a single URL (registration). This is where I think issues will arise. Normally you would define a single URL in the merchant account setup. But your solution would mean that you (registration) would also provide one or more merchant ids for each transaction. Stripe support will be able to answer that. I hope this helps. Also, keep us posted on this project (if allowed). -
Typography is not a simple topic but I'll try to explain how I use it. But first, the difference between EM/REM. Generally, an EM got it roots in print where it refers to the width of the upper-case M. When css came about, they changed it to refer to a relationship of the parent element. By default, one EM generally is 16 pixels in the browser world. The REM (Root EM) is in relation to the root (usually the HTML definition if no ::root{} is defined) font size as you have defined in CSS. Some inconsistency -- You can use both EM and REM in margin/padding definitions. However, when using EM as the unit in a margin definition it looks only at the current element; It does not refer back up to a parent. This is not the case with REMs. The REM declaration is always looking at the root. // EM html { font-size: 16px; } article { font-size: 20px; } p { font-size: 1.1em; margin-bottom: 1em; } <article> This is 20px. <p>This is 1.1 x 20px. The bottom margin is 1.1em regardless of parent font size setting.</p> </article> // REM html { font-size: 18px; } article { font-size: 20px; } p { font-size: 1.1rem; margin-bottom: 1rem; } <article> This is 20px. <p>This is 1.1 x 18px. The bottom margin is 1 x 18px.</p> </article> The viewport width/height (VW/VH) allows for a 'fluid' scaling. However, the drawback is that smaller devices will not zoom in or out should the font size scale too small. Also, on very wide screens this scaling would likely make the text way too big, to the point of interfering with other content. My method: I define my base font size as 16px (usually the browser default). I also include variables to make future changes very simple. I put my font size break points at the top of my css file (usually after ::ROOT{} ) so that I don't have to go hunting should I need to change something. To avoid the confusion between parent references, I define all font sizes, margins, paddings, widths, etc. in REMs, except for the base ::root{} definition which is in pixels. :root { --fs-norm: 1rem; // defaults to browser's font-size definition --fs-big: 2rem; --fs-hdg: clamp(3.5rem, 10vw + 1rem, 14rem); // This sets the min and max font-sizes and scales in between } @media (min-width: 40rem) { :root { --fs-norm: 1.125rem; --fs-big: 3rem; } } body { font-size: var(--fs-norm); line-height: 1.6; } h1 { font-size: var(--fs-hdg); text-transform: uppercase; } I hope this helps. Like I said, this isn't a simple topic where you can say, "Use x because...". ps. Not a front-end guru so others may have a better reply.
-
Maybe I'm off base here, but since you say the user is the issue, why not create your own ISAM routine by hooking the registration to it's own domain/database and passing the result to the proper installation login/user.
-
Should custom forms be built with the ProcessWire Form API?
rick replied to Jonathan Lahijani's topic in General Support
Personally, I prefer the old-school way rather than the ProcessWire way simply because it becomes too cumbersome to use ProcessWire to create Admissions forms, Tax forms, Milspec forms, Aircraft maintenance forms, etc. which I do almost monthly. I did begin with the ProcessWire way and spent way too much time trying to get the layout correct on both the front end and back end, so I stopped using its forms. As you know PHP has some built-in validation such as email, url, dns, etc. The remaining form types are simple enough to validate manually (ie, not using ProcessWire). Don't get me wrong, ProcessWire is great for the majority of solutions I require and I use it as the base for all current projects. But Ryan built it to satisfy his requirements, which don't necessarily meet with our requirements 100% of the time. When I create complex forms I use css grid and flexbox with custom styles to achieve the necessary front-end layout -- I don't allow user access to any back-end forms. Custom javascript is sometimes required to validate some form elements client side, including ajax calls, but it is nothing major. Also, I don't have to bloat my forms by including one or more third-party javascript libraries. If I receive a request for a simple form such as a contact form, then yes, I use the ProcessWire way. Otherwise, I find it much simpler to do it the old-school manual way. -
Add public / web directory to $config->paths
rick replied to SwimToWin's topic in Wishlist & Roadmap
The config->paths->root also contains the current process as part of the path. For example, if you are currently within a custom process module named 'widget', then the returned path is '/path/to/docroot/widget/'. You could use a combination of string functions with DOCUMENT_ROOT, CONTEXT_DOCUMENT_ROOT, and SCRIPT_FILENAME values to validate the directory. As far as I know, the script_filename value is not vulnerable so should provide a base from which to test and extract the doc root with some confidence.