Leaderboard
Popular Content
Showing content with the highest reputation on 06/11/2023 in all areas
-
You can also generate desired image variations when you upload an image in the field: /** * // in ready.php * Image variations * Generate image variations on upload * Pageimage api ref: https://processwire.com/api/ref/pageimage/ */ $this->wire()->addHookAfter('InputfieldImage::fileAdded', function ($event) { $inputfield = $event->object; $image = $event->argumentsByName("pagefile"); // `image` field on `document` template if ($inputfield->hasField == 'image' && $inputfield->hasPage && $inputfield->hasPage->template->name == 'document') { // some custom options $defaultOptions = array( 'upscaling' => false, 'cropping' => false, 'quality' => 90, ); // generate image variations // See wire/config.php for $config->sizeName: https://github.com/processwire/processwire/blob/master/wire/config.php#L726-L789 $image->size('landscape'); // 16/9 defined size `landscape` from $config->sizeName $image->size(200, 250); // 4/3 thumbnails } }); For debugging => https://processwire.com/modules/tracy-debugger/3 points
-
I'll go ahead and invite myself to share some JS thoughts nobody asked for... The don't-reinvent-the-wheel message in the "Do not use React Server Components" video is good, but I think there's a lot more to it when it comes to full stack JS overall. Not only are JS devs largely solving problems that have already be solved, the JS ecosystem is constantly solving problems that it causes for itself. Obviously new features can cause issues to address- but it feels like JS really takes the cake on this one. Hydration and rendering issues were pretty much inevitable when you start overloading the front end and forcing the browser to do so much heavy lifting. Wait... sending a blank page that waits for JavaScript murdered your SEO?! Rendering pages on the server is relatively simple, rendering them in the browser- boy howdy... So then the JS devs cheered when it invented server side rendering... for JavaScript. Then there's the bundle sizes, which is noted in this article about React Server Components: So now they're solving for asset sizes, another layer of complexity to solve an issue that didn't exist (at the level it does now) until massive front end frameworks caused them. We used to worry (showing my age here) about jQuery library sizes... React is excited about saving 240k while the entire jQuery library is 85.4k (uncompressed), and all you have to do is redesign your app architecture with server side JS! BRILLIANT! Before someone interjects to correct me, I'm not saying that React and jQuery are the same, or comparable in functionality and purpose. I am saying that the JavaScript-first approach allowed a lot of people to accept compromises to create the Next Hot Thing™ and a lot of problems with it. JavaScript on the server pretty much just made this more complex by providing increasingly complex ways to make itself more complex- lol, recursion. "But", the full stack JS dev will say, "you can use the same language everywhere so there's consistency." My brother in Christ, you don't even import code between files the same way in Node and and client side ES6. So when I read this in that video: This is from a developer who goes to bed at night thinking about how their Next.js application codebase might be outdated by the time it launches. Then I go back to feeling like this: I used to kind of stress about not pursuing full stack JS, then I realized that I'm happy. Having a front end (whether SSR or a JS framework) and a back end (CMF or non-js app framework) keeps me sane. I know where things go, what they do, and why. There's consistency between projects. It's easier to maintain best practices. My NPM headaches are smaller. My PHP doesn't need nodemon or pm2 to restart the server application if there's a JS error. The amount of documentation I read that has a big red box in the middle saying "Note: this thing that you've built several apps with and know well is going away. We're destroying it because someone on Github said we need to move to stateless functions. Soon there will be pain, and you will cry." is very limited. I made that one up, but you get the idea. I'm not dragging front-end JS frameworks per se, but making the case for why they should not pave the way to the JS-ification of all that is holy and aren't automatically better. Maybe putting JS everywhere has made SPA solutions worse because they didn't leverage how well traditional server side languages worked already. Went off on a tangent here, but I've had a few moments of zen this year about being a PHP developer and wanted to share the energy. All that aside, to answer your question @wbmnfktr I've wanted to pair up a front end framework with PW but haven't had the time or right project to give it a try with. I do like the idea of using PW as a JSON delivery backend in general.2 points
-
So I've just updated my personal website after some 7 or 8 years. It's a small one pager, but with some quirks that I hope you'll enjoy. Have a look: https://heldercervantes.com/ First, the approach was to somehow reinterpret my CV into a web experience. My last CV, along with my Supertiny agency's website share this space exploration / alien contact theme, so the idea here was to incorporate a journey through space as the user scrolls through the website. In the end you'll find a mini-game, where you can attempt to reach the far depths of space and discover what's out there. Most content is static, but I'm using PW to manage the logos you'll fly by at some point and the projects, the game's highscores and SEO. I intend to improve it a bit in time. Sound is probably the next thing I'll add, and I have some ideas to make the game more interesting. Fun fact: Setting up PW for this one took about 2h.1 point
-
I've just bumped the dev branch version to 3.0.220. Relative to 3.0.219, this version is largely focused on issue resolutions, and there are 11 of them, among 14 commits. See the dev branch commit log for details. This is just a brief update so I don't have more to add other than that I hope you are having a great week, and have a great weekend!1 point
-
@flydev: Damn red pill ?. Created my first module hooking after Page::render and replacing text emails like (example@domain.com) into encrypted mailto links which gets automatically decrypted from a Javascript function embedded into the head section if emails are found on the rendered page. Really enjoy my PW journey so far. If you need more options, want to configure stuff from the backend etc. I propose you try out the EMO module from Roope. My module will stay like this without more profound checks like mailto: links, embedded emails in other tags. If you are interested in the code, just send me a PM or post here.1 point
-
@zx80 There's actually several module's for this in the PW Asset Catalog, but you could always take the purple pill option (am I the only one who wanted a purple pill in the matrix?) and create a textformatter module that people could hook up to their textfields.1 point
-
1 point
-
It's actually about PHP/Laravel as a backend/middle-man solution instead of all these JS/node.js solutions around and the benefits it brings. The main focus, at least in 2 of the 3 videos, is still somewhat how it works with JS-frontends but still a nice change compared to most other topics in that sphere. You might want to watch or at least skip through some of the videos, the last one is fairly short, to get an impression. It's super interesting, not because of the JS stuff, but because of what Laravel offers and what some see as easy setup and solution compared to what they build as a final product (portfolios, blogs, and such more minimal projects).1 point
-
I had situations come up that just seemed like AJAX was the right way to handle interactions with the ProcessWire server - pages with an element like a button or link that should cause an action to occur but shouldn't require a form or actually following a link - it should just take the action and only update the toggle (a checkbox in this case) when the interaction is completed. Another use case is with a large page on which there are multiple possible interactions. When the page is heavy enough that redrawing results in a less than optimal user experience then it's nice to be able to submit a form without having to redraw the page in order to update the relevant parts. So with that preamble, here's what I put together. I was going to try to clean it up a bit but that has prevented me from posting this so I figured it's better to post it and clean it up if there is any interest. You'll see references to the namespace whale - the name of our project - that would ultimately be removed. There are two major components - the PHP side and the client-side. On the PHP side there are two functional areas: 1. "wrapping" an entity to be inserted into the HTML on a page Wrapping (the function 'makeContainer()' puts a predefined wrapper around one of three types of objects: FormBuilderForm, InputfieldForm, or Template). The wrapper provides context and attaches classes that allows the client JavaScript code to find the wrapper and figure out what to do with it. // // define a function that makes a "form" of a single button. // function makeButton ($label) { // get the form $form = wire('modules')->get("InputfieldForm"); $form->attr('action', './'); $form->attr('method', 'post'); $submit = wire('modules')->get("InputfieldSubmit"); $submit->attr('id+name', 'submit'); $submit->attr('value', $label); $form->add($submit); return $form; } // wrapper function to set label on submit button function requestUserDeleteList() { return makeButton('Do it!'); } // // makeContainer wraps the rendered InputfieldForm in HTML so the client JavaScript can recognize it and handle // AJAX interactions with the server. // It returns the InputfieldForm object and the HTML to be inserted into the page. Note that makeContainer // is in a different namespace so it requires the function name must be qualified with the \ProcessWire prefix. // list ($form, $deleteUsersHTML) = ajax\Request::makeContainer('do-something', '\ProcessWire\requestUserDeleteList'); 2. helping with the processing of an AJAX request that is submitted to the page. Helping with the AJAX request - the code is invoked on page load and determines where there is a valid AJAX request from something it wrapped. It also allows messages to be returned, classes to be added or removed from specific elements, redirects to be executed, or even wholesale replacement of DOM elements (with plenty of caveats). It will even update a submit key so it is possible for the client to execute a single transaction multiple times. // get a new request object for the AJAX transaction $request = new ajax\Request(); // if it isn't formatted correctly handle the error if (!$request->isValidCall()) { return $request->echoError(); } // get the data and function-specific contents (Whale Ajax Context) $data = $request->data(); $wac = wireDecodeJSON($data['wac']); // if ($request->id('wants-newsletter')) { if (!ajax\Request::hasCorrectProperties($data, ['wac', 'value'])) { return $request->echoError(__('invalid call')); } // implement function here } else if ($request->id('another-function')) { // implement function here } // it didn't match any of the AJAX IDs implemented return $request->echoError('not implemented'); The client code requires jQuery and is packaged as three separate functions because both the form and template processing share a common core set of functions. My original intent was to only load the form or non-form code as needed but they're small enough that it really doesn't matter. See attachments for the Request class and the client code. There are many helper functions. Here is a kind of an unfocused extract that illustrates using the code with more context (from an internal sandbox page): <?php namespace ProcessWire; using whale\ajax; // include server-side code for making forms and processing them require_once './utility/ajaxform.inc'; // custom version of ProcessWire/wire/core/WireFileTools.php render() that returns the // template object, not the rendered HTML require_once './utility/get-file-template.inc'; // START AJAX submitted form processing - decodes the request and stores results in $aaform. $aaform = new ajax\Request(); // // this page handles multiple ajax calls so I check to see if it is valid once and then check IDs. // It's also possible to use $aaform->isValidCall('get-user-delete-list') to check specifically // for a specific AJAX ID. The ID is the name provided to Request::makeContainer() when the object // is wrapped. It's also possible to make calls to $aaform->id('get-user-delete-list') to check // for a specific ID. // // to create the forms/input elements that are submitted via AJAX start with: // Request::makeContainer('unique-name-on-page', object) // unique-name-on-page will become the ID of the element that wraps your object. // object - one of ProcessWire\InputfieldForm, \FormBuilderForm, ProcessWire\Template. // if ($aaform->isValidCall()) { if ($aaform->id() === 'get-user-delete-list') { $form = requestUserDeleteList(); // process using the form. the Request object will check to make sure it's the right type. if (!$aaform->process($form)) { return $aaform->echoError(); } // build new form with usernames for selections to delete. the function getUsersToDelete() // returns a user count and a function that will make the form that includes the users in // a list of checkboxes. list($usercount, $formmaker) = getUsersToDelete(); // this returns a replacement to part of the existing DOM. There are limitations but it // handles adding a form or replacing an existing form. if ($usercount === 0) { $replacement = '<div id="ajax-place">No users to delete</div>'; } else { // we pass the $formmaker function to makeContainer(). It returns the form and the // rendered wrapper and form. list($xform, $xhtml) = ajax\Request::makeContainer('do-delete', $formmaker); $replacement = '<div id="ajax-place">' . $xhtml . '</div>'; } // this makes sure the return is formatted so the client can handle it correctly. in // this case a replacement in the DOM is being returned. The first argument is the // selector, the second is the HTML to replace the selected element with. return $aaform->echoReplacement('#ajax-place', $replacement); } else if ($aaform->id() === 'do-delete') { list($usercount, $formmaker) = getUsersToDelete(); // process using the form returned by $formmaker. this will check to make sure it's // the right type of form. This abstracts FormBuilder forms and InputfieldForms. if (!$aaform->process($formmaker())) { return $aaform->echoError(); } // a bunch of logic where the checked users are deleted $deleted = []; $failed = []; $data = $aaform->data(); foreach($data as $name => $value) { if ($name === $value) { $user = wire('users')->get("name=$name"); $email = $user->email; // delete the user and try to get it again to see if the delete worked wire('users')->delete($user); $u = wire('users')->get("name=$name"); if (!count($u)) { $deleted[] = $email . " ($name)"; } else { $failed[] = $email . " ($name)"; } } } $deleted_users = $failed_deletions = ''; if ($deleted) { $deleted_users = 'deleted:<br/>' . join($deleted, '<br/>') . '<br/>'; } if ($failed) { $failed_deletions = 'failed to delete:<br/>' . join($failed, '<br/>') . '<br/>'; } $replacement = '<div id="ajax-place">' . $deleted_users . $failed_deletions . '</div>'; return $aaform->echoReplacement('#ajax-place', $replacement); } else if ($aaform->id() === 'contact') { // here a FormBuilderForm is being loaded if (!$aaform->process($forms->load('contact'))) { return $aaform->echoError(); } // this sends a notice back. the client will place it in a predefined notice area. // Request::makeContainer() will create an area for notices (or you can supply one). // It is also possible to return errors; notices and errors get different classes. $msg = ajax\Request::makeNotice('bruce says hi'); return $aaform->echoSuccess($msg); } else { // it was a valid form but it doesn't match any ID that this page knows about. return $aaform->echoError('what is this?'); } } // normal processing to render the initial page follows as it was not a valid AJAX post // that is handled by Request(). That's a lot of code, so I won't post anymore. If people have interest I'm happy to explain or provide other bits of code, like the extracted get-file-template.inc function. Wrapping a template is similar to wrapping a form except that only certain HTML elements are tracked and each are sent to the server when they are clicked on (technically it varies). It handles radio buttons, checkboxes, links, and buttons (radios and checkboxes on "change" and links and buttons on "click"). So when a checkbox is checked an AJAX call will be made so it can be acted upon by the server. @microcipcip, @ryan, @valan (sorry to any if this isn't interesting to you - I did a quick scan of what looked like semi-related AJAX posts). ajaxform.inc ajaxclient.js1 point