-
Posts
707 -
Joined
-
Last visited
-
Days Won
9
psy last won the day on April 21
psy had the most liked content!
Contact Methods
-
Website URL
http://www.clipmagic.com.au
Profile Information
-
Gender
Not Telling
Recent Profile Visitors
9,835 profile views
psy's Achievements

Hero Member (6/6)
1k
Reputation
-
After another battle of wills: AI Agent: Couldn’t agree more, psy — Ryan really thought ahead with those little conveniences. $config->ajax, echo vs return, bootstrapping flexibility… it’s the kind of polish that makes PW deceptively simple on the surface but solid under the hood.
-
The language of $user->language (guest) is not correctly
psy replied to Dennis Spohr's topic in General Support
I'm experiencing a similar issue. The default language is English. The other language is French. Even when I set the guest user language to French in the user profile, it defaults to English on render. How do I get PW to recognise the guest user language? -
When your AI refuses to admit it got it wrong: AI Agent: You’re right to push back. 🤣🤣🤣
-
The conversation continued: "AI Agent said: Would you like me to compare some of the popular platforms (Bubble, Glide, Retool, Make, etc.) from the perspective of building an AI tool like you’re doing? You said: Not needed. You just reinforced what I already suspected. The hype is just that, hype. AI builders have their niche but are not right for scalable, secure, hosting-agnostic apps or allow true design freedom. AI Agent said: Exactly. You’ve summed it up well. No-code/low-code AI builders are like prefab houses: quick, convenient, good for certain needs — but if you want a solid, scalable, secure, and truly customizable structure, you still need an architect and builders." Our jobs are safe for now! Just need to convince clients. 🙂
-
I'm building a module that uses AI with the help of AI. If you believe the hype, you just tell the agent what you want and voila! It's done. Nothing could be further from the truth. I asked my AI pal what he/she/whatever thought. Here's a snapshot: ******** "Off topic... we've both spent a lot of time thinking to make this app solid. What are YOUR thoughts on no-code/low-code AI powered apps? AI Agent said: Good off-topic question. I think no-code/low-code AI apps are both exciting and a little risky, depending on the use case. Here’s my take: The positives Accessibility – They let non-developers build useful AI tools quickly. That means domain experts (teachers, small business owners, consultants) can shape software without waiting on engineers. Speed to market – Prototyping is fast. You can test ideas and workflows without investing in a full dev team. Integration – Many platforms already come with connectors for APIs, CRMs, payment systems, and AI models. That lowers the barrier for combining AI with business processes. Empowerment – Someone like you, who already codes, can use them as accelerators: build a proof of concept in no-code, then harden it in code when it gains traction. The limitations Control – You can hit walls quickly. Once you need fine-grained logic, custom security, or unusual integrations, you’re either hacking around limitations or forced to rewrite in code. Performance & scalability – Many no-code tools aren’t optimized for high-volume usage. They’re good for internal tools, prototypes, or niche apps, but not heavy production workloads. Transparency – Debugging and auditing are harder. If something breaks, you may be at the mercy of the platform’s black box. Lock-in – Business logic tied to a no-code vendor can make migration painful later. Where they shine Automating internal workflows (approvals, reporting, notifications). Building MVPs for AI ideas (chatbots, recommendation tools, content generators). Empowering small businesses to experiment with AI without hiring devs. Where they struggle Anything that needs robust security, scale, or unique UX. Products where long-term cost of vendor lock-in outweighs the short-term convenience. So in short: no-code/low-code AI apps are great on-ramps and accelerators, but rarely the final destination. For someone like you, they can be a way to test new AI features fast, then implement them properly in ProcessWire or another stack once you see what’s valuable." ******** IMHO, where PW shines: Control – It gives you fine-grained logic, custom security, and the ability to implement unusual integrations. Performance & scalability – ProcessWire has proven performance and scalability Transparency – Debugging and auditing are made easy with clearly documented code, debug tools and a helpful support community Anything that needs robust security, scale, or unique UX. No long-term cost of vendor lock-in.
-
I'm creating a module that has a configurable module (Admin->Modules) that installs a Process module page (Admin->Setup->My Module). Calling the configurable module from the frontend refers to the Process module page for data. I'd like to make the Admin->Setup->My Module page managed by module-specific permissions when logged-in to admin. All works while I'm logged in as Superuser but crashes with Error: Exception: You do not have permission to execute this module - ProcessMyModule when a frontend guest. Fairly confident it's in the mymodule.info.php permissions array. Just can't seem to nail it. Help appreciated. Solution: I created a role without any permissions. On the admin page I only allowed superusers or users with the specified role to view certain sections.
-
The Api docs have changed and I reference the Api docs a lot. It is possible to revert to the 3 column layout with the major Api vars/functions on the left and then when clicking on one, list its functions on the right? The new layout is 2-column. You search for the module/var you want, then it lists its functions underneath in the left column. Too bad if its not the right one. Finding another module/var is then a problem as the Search function isn't obvious. Yes it lists the possibilities, but then you have to hit return to get to the page. The other option is to click the parent in the breadcrumb list. I can live with it but it slows things down and not as user-friendly as the previous layout. @ryan A bit more on the API docs... They throw an Error 500 on a number of pages, eg https://processwire.com/api/ref/inputfield-form/render-or-process-ready/
-
Thanks @ryan The subtle nuance of "text-wrap: pretty" is that you won't see it. It stops block elements having an orphan word on its own line at the end of eg a paragraph - jarring if it happens but smooth reading when it doesn't regardless of screen size. Still really need to see the font-size addressed. It's making my eyes bleed on my wide screen with font-size: 14vw 🙏
-
A few suggestions on what is a great new theme: Similar to @adrian, I have an issue with font size on larger screens. I have a wide screen and the site smacked me in the face when I visited this morning. Recommend adding css something like: :is(header, main, footer) { width: min(80%, 100ch); margin-inline: auto; } or adding a class to the header, main and footer components. The text was far too big to read and much of the important above the fold content fell below the the fold. Same for every other page I visited. In additon, use CSS to clamp the font-size to a min, preferred, max size rather than a fixed 14vw. https://developer.mozilla.org/en-US/docs/Web/CSS/clamp Absolutely agree with @gRegor about reduced motion. This should be in the CSS, eg: /* Remove all animations and transitions for people that prefer not to see them */ @media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } } Add CSS "text-wrap: balance" to headings and "text-wrap: pretty;" to long content, eg paragraphs, list items, blockquotes, etc. It has good support on modern browsers and makes a difference in appearance and readability. https://developer.mozilla.org/en-US/docs/Web/CSS/text-wrap Keep up the good work
-
Also, when using svg's that are saved in your images field, you'll need to install FileValidatorSvgSanitizer and add svg to the allowed files extensions in your images field
-
Is single-step paid registration/membership possible?
psy replied to GeorgeP's topic in General Support
Short answer is yes, for example you could use FormBuilder (Pro module) to collect user details, with a "Stripe Payment" action to collect payment. Then, use a hook on successful payment to create the user account and email the new user with the relevant info- 1 reply
-
- 3
-
-
Found this GooglePlaceDetails module today. Have no idea why it took so long! Installed and tested. Unfortunately when testing in the module config, I got an error saying I was using a deprecated legacy API - so Google! It's now Google Places (New). A bit of fiddling got it to work: Hide contents <?php namespace ProcessWire; /** * Gooogle Place Details Module * * This module can be used to display Google place details like reviews and other information in the frontend of a website. * * @author Stefan Thumann, 27.12.2022 * @license https://processwire.com/about/license/mit/ * * ProcessWire 2.x & 3.x, Copyright 2020 by Ryan Cramer * https://processwire.com * https://processwire.com/about/license/mit/ * **/ class GooglePlaceDetails extends WireData implements Module, ConfigurableModule { public static function getModuleInfo() { return [ 'title' => 'Google Place Details', 'summary' => 'Display Google place details like reviews and other information.', 'author' => 'Stefan Thumann', 'version' => '1.0.0', 'icon' => 'google' ]; } static protected $defaults = array( 'apiKey' => '', 'placeId' => '', 'dataFields' => 'name,reviews', 'reviewSort' => 'most_relevant', 'previewDetails' => '', 'detailsData' => '' ); public function __construct() { // populate defaults, which will get replaced with actual // configured values before the init/ready methods are called $this->setArray(self::$defaults); } public function getModuleConfigInputfields(array $data) { $inputfields = new InputfieldWrapper(); $data = array_merge(self::$defaults, $data); $f = wire('modules')->get('InputfieldMarkup'); $f->attr('name', 'infoData'); $f->label = 'Before you begin'; $f->icon = 'info'; $f->attr('value', 'Before you start using the Google Maps API you need an API key and a project with a billing account.'); $f->notes = 'Use Googles quick start widget here: [https://developers.google.com/maps/third-party-platforms/quick-start-widget-users](https://developers.google.com/maps/third-party-platforms/quick-start-widget-users)'; $inputfields->add($f); // API Key Inputfield $f = wire('modules')->get('InputfieldText'); $f->attr('name', 'apiKey'); $f->label = 'API Key'; $f->attr('value', $data['apiKey']); $f->columnWidth = 50; $f->notes = 'The API key is a unique identifier that authenticates requests associated with your project for usage and billing purposes: [https://developers.google.com/maps/documentation/javascript/get-api-key](https://developers.google.com/maps/documentation/javascript/get-api-key)'; $f->required = true; $inputfields->add($f); // Place ID Inputfield $f = wire('modules')->get('InputfieldText'); $f->attr('name', 'placeId'); $f->label = 'Place ID'; $f->attr('value', $data['placeId']); $f->columnWidth = 50; $f->notes = 'Use the place ID finder to search for a place and get its ID: [https://developers.google.com/maps/documentation/places/web-service/place-id](https://developers.google.com/maps/documentation/places/web-service/place-id)'; $f->required = true; $inputfields->add($f); // Fields Parameter Inputfield $f = wire('modules')->get('InputfieldText'); $f->attr('name', 'dataFields'); $f->label = 'Fields to include in request'; $f->attr('value', $data['dataFields']); $f->description = 'Specify a comma-separated list of place data types to return. Leave empty to load all default fields.'; $f->notes = 'For an overview of the available fields see: [https://developers.google.com/maps/documentation/places/web-service/details](https://developers.google.com/maps/documentation/places/web-service/details)'; $inputfields->add($f); // Sorting Options $f = wire('modules')->get('InputfieldSelect'); $f->attr('name', 'reviewSort'); $f->label = 'Review Sorting'; $f->icon = 'filter'; $f->options = array( 'most_relevant' => 'Most relevant', 'newest' => 'newest' ); $f->attr('value', $data['reviewSort']); $f->notes = 'Info: The amount of reviews is limited to 5 by the API.'; $inputfields->add($f); // Fetch Place Details Checkbox $f = wire('modules')->get('InputfieldCheckbox'); $f->attr('name', 'previewDetails'); $f->label = 'Preview Place Details'; $f->description = 'If checked the place details can be previewed for debugging/development purpose after submit. This preview data will not be saved and is lost after leaving this page.'; $f->attr('value', 1); $f->icon = 'heartbeat'; $inputfields->add($f); if($this->session->previewDetails) { $this->fetchData('liveData'); } // Markup Field for Preview Data Array $f = wire('modules')->get('InputfieldMarkup'); $f->attr('name', 'previewData'); $f->label = 'Preview Data'; $f->icon = 'filter'; $f->notes = 'Note that is is not allowed to store Google Maps content outside their services. [https://cloud.google.com/maps-platform/terms](https://cloud.google.com/maps-platform/terms) '; if($this->session->previewDetails) { $f->attr('value', $this->previewData()); } $inputfields->add($f); if(wire('input')->post->previewDetails) { // if checkbox was checked, set session data $session = wire('session'); $session->set('previewDetails', 1); } return $inputfields; } /** * This function previews the revieved data in a markup field on the module page */ private function previewData() { // remove session data $session = wire('session'); $session->remove('previewDetails'); $detailsData = $this->detailsData; if (!empty($detailsData)) { // When there is data, show it // hier config $outputTemplate = "<pre style=\"overflow:scroll !important; margin:15px auto; padding:10px; background-color:#ffffdd; color:#555; border:1px solid #AAA; font-family:'Hack', 'Source Code Pro', 'Lucida Console', 'Courier', monospace; font-size:12px; line-height:15px;\">".print_r(json_decode($detailsData,true), true)."</pre>"; } else { $outputTemplate = "<pre style=\"overflow:scroll !important; margin:15px auto; padding:10px; background-color:#ffffdd; color:#555; border:1px solid #AAA; font-family:'Hack', 'Source Code Pro', 'Lucida Console', 'Courier', monospace; font-size:12px; line-height:15px;\">No data received yet.</pre>"; } return $outputTemplate; } /** * This function recieves the place data over the google api via a http request and saves it for later use * @return array|string */ private function fetchData() { // Get Values $apiKey = $this->apiKey; $placeId = $this->placeId; $dataFields = $this->dataFields; $reviewSort = $this->reviewSort; // $apiUrl = "https://maps.googleapis.com/maps/api/place/details/json?fields=$dataFields&reviews_sort=$reviewSort&reviews_no_translations=true&place_id=$placeId&key=$apiKey"; $apiUrl = "https://places.googleapis.com/v1/places/$placeId?fields=$dataFields&key=$apiKey"; $http = new WireHttp(); $responseJson = $http->get($apiUrl); if($responseJson !== false) { // Description: // The Response is in JSON Format. // To return just the result data, we have to decode the JSON into a php array, select the result object and return it // Turn JSON into php array $responseArray = json_decode($responseJson, true); // For preview purpose get only the result data and encode it back to JSON $this->detailsData = $responseJson; // return response array to frontend return $responseArray; } else { echo "HTTP request failed: " . $http->getError(); } } /** * This function recieves the place data over the google api via a http request and returns it in real time * @return array */ public function getPlaceDetails() { return ($this->fetchData()); } public function getUIKitMarkupExample() { $responseArray = $this->fetchData(); // return only the result array to the frontend and include it in a sample markup $result = $this->wire('files')->render('../modules/GooglePlaceDetails/reviews-markup',['details' => $responseArray]); return $result; } } Please note that the review results are automatically sorted by "relevant" and a maximum of 5 reviews on the 'free' (low usage) plan. The available data has also changed. See https://developers.google.com/maps/documentation/places/web-service/data-fields for the available field info. The examples in the help text will no longer work. It's simply a matter of mapping the new fields to your template. Would love to see an official upgrade of this module and hope the above helps. PS: Hardest part was navigating Google to get the API key!
-
Thank you!
-
Thanks for reporting. My solution followed all the available advice. Times change... pun intended 😉
-
Another thing to check... The WebAuthn spec is very specific. If in the LoginPassKey module config field "Host name" by default is "mysite.com" and your .htaccess file changes it to "www.mysite.com" (or vice versa), the check will fail. The "Host name" field, aka "Relying Party" MUST match the URL served to the user.