Leaderboard
Popular Content
Showing content with the highest reputation on 09/23/2024 in all areas
-
Here's a site from someone new to ProcessWire. I've been developing with PHP and WordPress for donkeys years, but decided to try building a site with PW to see if it might be a better platform choice for the kind of sites and apps I build these days. Spoiler alert: it is. https://www.eltikon.online/ I'm not holding this site up as any kind of design example - it's a really, really basic hobby blog. What's more important to me is that I managed to build it from scratch in PW (previously it was a WordPress site) in about 20 hours, starting with zero PW experience, and my templates folder is really tidy with just a few small, well-structured PHP files. And that 20 hours includes sorting out the RSS feed, the XML sitemap, the SEO, etc etc. So my reason for posting is really to say that I found the PW architecture and methodology instantly comfortable and intuitive, and PW is going to be the first platform I consider for most future builds. It fits with my "hate visual builders, love coding" methodology, and I aim to get the ProFields Repeater Matrix module, since Advanced Custom Fields Pro's flexible content field has been at the core of all my WordPress sites for ages. Site details: I started with a clean install of PW 3.0.229 and the "site-simple-blog" site profile by tcnet, which I proceeded to completely rip apart and rebuild as part of the learning process. Non-core modules installed are SeoMaestro and ProcessPageViewStat, nothing else.7 points
-
Thanks very much for pointing out the error! Now fixed (I had done some refactoring but missed one instance in a selector).2 points
-
Congratulations to your new site. 👍 Just wanted to mention that your tag pages seem to throw an error, f.e.: https://www.eltikon.online/tag/trackwork/ Regards, Andreas2 points
-
@bernhard RockMigrations and ProcessDbMigrate take fundamentally different approaches. RM takes the traditional web application framework approach while ProcessDbMigrate is more of a change recorder, similar to Craft CMS Project Config. There's trade-offs to each approach, but I will say it's nice to have changes to be recorded automatically, even if it's not a 100% full-proof approach (although I'm looking into that). It's basically automating what I typically do when I need to migrate a changes "by hand" on the sites I work on, which is an approach I like. I'm experimenting with the change recorder approach in a module of my own for now, but development of it is on and off for the moment. Understanding ProcessDbMigrate helped with understanding the approach, and the recording part of it is straight-forward. The "playing" of the recording gets tricky for all the reasons we all know about.2 points
-
Generate image placeholders for smoother lazyloading. Currently supports ThumbHash, BlurHash, and average color placeholders. I've been using the wonderful ImageBlurhash module for this in the past, but unfortunately it's no longer in active development. This new module adds ThumbHash and Average Color placeholder algorithms, improves performance by caching generated placeholders, fixes an issue when replacing images, and allows regenerating and clearing placeholders via the admin interface. Try it out using the installation instructions below or check out the GitHub repo for details. Why use image placeholders? Low-Quality Image Placeholders (LQIP) are used to improve the perceived performance of sites by displaying a small, low-quality version of an image while the high-quality version is being loaded. The LQIP technique is often used in combination with progressive lazyloading. How it works This module will automatically generate a small blurry image placeholder for each image that is uploaded to fields configured to use them. In your frontend templates, you can access the image placeholder as a data URI string to display while the high-quality image is loading. See below for markup examples. Placeholder types The module supports generating various types of image placeholders. The recommended type is ThumbHash which encodes most detail and supports transparent images. ThumbHash is a newer image placeholder algorithm with improved color rendering and support for transparency. BlurHash is the original placeholder algorithm, developed at Wolt. It currently has no support for alpha channels and will render transparency in black. Average color calculates the average color of the image. Installation Install the module using composer from the root of your ProcessWire installation. composer require daun/processwire-image-placeholders Open the admin panel of your site and navigate to Modules → Site → ImagePlaceholders to finish installation. Configuration You'll need to configure your image fields to generate image placeholders. Setup → Fields → [images] → Details → Image placeholders There, you can choose the type of placeholder to generate. If you're installing the module on an existing site, you can also choose to batch-generate placeholders for any existing images. Usage Accessing an image's lqip property will return a data URI string of its placeholder. $page->image->lqip; // data:image/png;base64,R0lGODlhEAAQAMQAA Accessing it as a method allows setting a custom width and/or height of the placeholder. $page->image->lqip(300, 200); // 300x200px Markup Using a lazyloading library like lazysizes or vanilla-lazyload, you can show a placeholder image by using its data URI as src of the image. <!-- Using the placeholder as src while lazyloading the image --> <img src="<?= $page->image->lqip ?>" data-src="<?= $page->image->url ?>" data-lazyload /> Another technique is rendering the placeholder and the original image as separate images on top of each other. This allows smoother animations between the blurry unloaded and the final loaded state. <!-- Display placeholder and image on top of each other --> <div class="ratio-box"> <img src="<?= $page->image->lqip ?>" aria-hidden="true"> <img data-src="<?= $page->image->url ?>" data-lazyload> </div>1 point
-
Today while working on RockCalendar I had the need to change the database schema of my daterange fieldtime to add support for recurring events. I didn't know how to do it, so I had to do some research and that took quite some time. Here's the solution that I found in FieldtypeComments.module. When developing the fieldtype you can add a database schema like this and PW will take care of creating the table and columns for you: public function getDatabaseSchema(Field $field) { $schema = parent::getDatabaseSchema($field); $schema['data'] = 'timestamp NOT NULL'; // the from timestamp $schema['foo'] = 'timestamp NOT NULL'; return $schema; } This is quite easy, but I wanted to add another column and tried this: public function getDatabaseSchema(Field $field) { $schema = parent::getDatabaseSchema($field); $schema['data'] = 'timestamp NOT NULL'; // the from timestamp $schema['foo'] = 'timestamp NOT NULL'; $schema['bar'] = 'timestamp NOT NULL'; return $schema; } No luck. You will get an error that column "bar" does not exist. Ok, so we have to modify the table somehow... But we also have to make sure that this is only done once. How to we do that? The solution is to save the schema version to the field and use that to compare versions and conditionally update the schema: public function getDatabaseSchema(Field $field) { $schema = parent::getDatabaseSchema($field); $schema['data'] = 'timestamp NOT NULL'; // the from timestamp $schema['foo'] = 'timestamp NOT NULL'; $schema['bar'] = 'timestamp NOT NULL'; $schemaVersion = (int) $field->get('schemaVersion'); $updateSchema = true; $table = $field->getTable(); $database = wire()->database; if ($schemaVersion < 1 && $updateSchema) { try { if (!$database->columnExists($table, 'bar')) { $database->query("ALTER TABLE `$table` ADD bar " . $schema['bar']); } $field->set('schemaVersion', 1); $field->save(); } catch (\Throwable $th) { $this->error($th->getMessage()); $updateSchema = false; } } return $schema; } And maybe at a later point you want to add another column "baz": public function getDatabaseSchema(Field $field) { $schema = parent::getDatabaseSchema($field); $schema['data'] = 'timestamp NOT NULL'; // the from timestamp $schema['foo'] = 'timestamp NOT NULL'; $schema['bar'] = 'timestamp NOT NULL'; $schema['baz'] = 'timestamp NOT NULL'; $schemaVersion = (int) $field->get('schemaVersion'); $updateSchema = true; $table = $field->getTable(); $database = wire()->database; if ($schemaVersion < 1 && $updateSchema) { try { if (!$database->columnExists($table, 'bar')) { $database->query("ALTER TABLE `$table` ADD bar " . $schema['bar']); } $field->set('schemaVersion', 1); $field->save(); } catch (\Throwable $th) { $this->error($th->getMessage()); $updateSchema = false; } } if ($schemaVersion < 2 && $updateSchema) { try { if (!$database->columnExists($table, 'baz')) { $database->query("ALTER TABLE `$table` ADD baz " . $schema['baz']); } $field->set('schemaVersion', 2); $field->save(); } catch (\Throwable $th) { $this->error($th->getMessage()); $updateSchema = false; } } return $schema; } 😎1 point
-
Looking at the code for processInputFile, it does seem to run against any changes to the field, including deletion, so in that regard it makes sense that it also runs when the ZIP archive file is being deleted. You might be able to get an InputfieldFile after hook to work against ProcessInputAddFile, but I think to be successful you'd have to examine the current method to get access to the values you need since the default method only accepts the filename as a parameter, you can't immediately get the pagefile. Maybe that was the issue when you tried to use it as a hook previously?1 point
-
@BrendonKozThanks for your suggestion. Basically an editor can upload a zip file with PDF participation certificates for courses from a page file field in the backend. Once the ZIP file was uploaded, the PDF files should be extracted to a folder inside site/assets/files/upload, which is protected via .htaccess from direct access. At the end of the year the extracted PDF files will be deleted by the course editor, by deleting the corresponding ZIP archive via pressing the trashcan icon of the page file field. The course attendees can download their PDF certificate after the course is finished from their course page available after frontend login. This is done by clicking a download button, which reads the binary contents of the corresponding PDF file and sends it as PDF file to the browser for download using PHP header functions. So the PDF files are not touched after upload until they get deleted at the end of the year. The PDF files should stay around until the end of the year or a predefined time period before getting deleted. So I do need two hooks. Upload of the ZIP file triggers the unzip task and the deletion of the ZIP archive should trigger the delete extracted PDF files task. Timing won‘t help, as the delete action should not trigger the unzip process again.1 point
-
@bernhard This is the module I mentioned for generating placeholder images. Has worked very well.1 point
-
@bernhard I've never been so excited to see errors in my code! Thank you!1 point
-
Just curious if there's a less PW-like way of dealing with this issue? Are you unpacking the archive, doing some processing on the extracted files, then deleting the files -- all in sequential order? If so, why not call the other methods from within each method/step of the process? That way you'd only need one hook, the other logic can be separated into standard functions/methods and don't need to rely on proper hook order. If that's not possible, I haven't checked to see if PW's file methods are non-blocking, but if they are blocking, then perhaps setting a priority order on the hook calls will suffice?1 point
-
Hey @FireWire please grab the latest version from the dev branch 😎😍 Note that you even have tabs for latte/php to show both the latte file and the compiled php file!! I got so used to these unhelpful error messages on a white screen that I didn't even think of looking into it. Thx a lot! This improves the dev experience a lot!1 point
-
Hey @BrendonKozand @poljpocket, I just wanted to update that I have used ddev based on your suggestions and scripts and it's great. Thanks again for sharing that info and those configurations. I had been using MAMP pro (trial) but when the trial expired, the free version didn't work for some reason. I gave ddev a try with MariaDB, and it worked great. Thank you both for that suggestion. I didn't get to test Lightspeed but I think it'll probably be fine. 🤞 Thanks again!1 point
-
Generally speaking this is already possible. The syntax is: !your_field_name^=foo Paths are a special case though because these are only searchable when the PagePaths module is installed and that module doesn't support this kind of negation: But I don't think this actually matters because there is a dedicated selector option for when you want to find pages that do or do not have a particular ancestor, and that is "has_parent": https://processwire.com/docs/selectors/#finding2 So rather than needing anything like... ...you can instead do... has_parent!=/foo/|/bar/|/baz/1 point