Leaderboard
Popular Content
Showing content with the highest reputation on 09/06/2024 in all areas
-
I've got a lot of travel coming up in the weeks ahead, so there may be a few quieter-than-usual weeks in terms of core updates. I'll be in and out of town a few times, and I'm not much of a traveler, so will see how it goes. I'm not yet sure whether I can do work remotely, or what will be available in terms of internet access. There's a lot of client work to wrap up before hitting the road, so I've been focused on that this week and will have to next week as well. It's all ProcessWire related work though, so still having fun. There have been a few core updates this week, and there will likely continue to be in the coming weeks, but just not major core updates. By November hopefully all will be back to normal in terms of schedule. I'll be focused on getting a new main/master version out then. I also expect to have a new version of the CustomFields module (from last week's blog post) ready in the next week or so as well. Thanks for reading and have a great weekend!8 points
-
First milestone reached!!! 😍😍 Yesterday I spent the whole day with refactoring the JS of the rrule gui and I added some more options to cover hopefully 99.9 of all necessary scenarios. The gui now has a toggle to switch between simple and advanced mode to keep things as clean as possible and not overwhelm the users for simple rrules like "every week for 4 times". At the moment 80 hours have gone into this module (excluding the daterange input, which was already done some time ago). 💪🤯 Here my answers to the open questions: I don't think there is an ideal solution to this problem. I even found a bug in google calendar when comparing and playing around!!! I decided against a fake "never" option, as it does something different than what the UI shows. So in my case if the user does not enter an end date or a max number of events it get's limited to 100. This limit is configurable in the module's settings, though 😎 I've also added the dates of the first and last event beside the resulting rrule to make it clear what is going to happen. Additionally the user will get a table of all events that will be created. Sometimes - but not always! rrules are tricky 😄 - the first occurrence is the main event itself. In that case the table shows a hopefully clear indicator: Yes and no, I'm using a mix to somehow get the best of both worlds. I'm creating real pages for the events, but I'm only populating the date field. All other fields (like shown with the title in the video) will be inherited from the main event at runtime. This makes it possible to have complex event descriptions (like one built with RockPageBuilder) with images etc. and when creating 1000 recurring events it will still only consume the space of one event + 1000 date inputs (but not 1000 copies of images). I hope the video answers that? 🙂4 points
-
3 points
-
Hi, I don't think you'll see differences between MySQL and MariaDB, and LightSpeed supports .htaccess so this is a good news. To test it, it depends on your habits, I'm not a MacOS user, why not installing a Debian distribution in a virtual machine? MariaDB is the default database, and LightSpeed has a sh script to install it. On Windows I would use Linux WSL. There's also OpenLightSpeed in official Debian packages, but I don't know the differences: https://www.howtoforge.com/how-to-install-lomp-stack-openlitespeed-mariadb-and-php-on-debian-11/3 points
-
Looks interesting! I just created an account https://pinkary.com/@processwire. I'm not sure what happened to Twitter but seems like it's gone downhill. I don't have an appetite for it. I've only kept the ProcessWire account on Twitter to post links to new blog posts, but not sure I'll keep doing that. Threads seems a lot better, but I don't think there's much of a webdev community there, that I've found anyway. This forum is my favorite social network. I look forward to trying out Pinkary more.2 points
-
MariaDB won't be an issue. I'm running ProcessWire on it with quite a few sites, from simple to complex, without any issues. (fun fact, Craft CMS requires MySQL or PostgreSQL; MariaDB won't work) I don't have direct experience with setting up Litespeed but that shouldn't be an issue either. I think Geffen Playhouse (a site I developed) is on Litespeed. The hosting company has abstracted that away and Litespeed is built as a drop-in replacement (?) for Apache so I wouldn't worry about that either. As @da² mentioned, if you use Windows, WSL is an excellent and seamless choice to spin up a VM to test, or DigitalOcean or Hetzner if you don't want to install a VM hypervisor on your system. Either way, you'll have to set up the server software and configure it yourself. I recommend Ubuntu 24.04 and installing everything with apt. If using VS Code, you can connect to the server and modify files as if it were a local VM site using VS Code Remote SSH.2 points
-
I still follow soma on Twitch. He occasionally has (digital) painting sessions. 🙂 Signed up for Pinkary. Hopefully they'll offer an API like the original Twitter days to build out supporting apps. Interestingly, Pinkary runs completely on SQLite.2 points
-
I can see how ProcessWire is not only a great platform to use, but also a great learning tool and a springboard to help jump above your head. Many moved to more enterprisy dev and more appy tools. New active members come and bring some fresh thoughts. All is good 😎2 points
-
Introduction Automated PDF production is a quite common task for ProcessWire developers. PW does not offer core tools for that. There are some external modules that we can utilize. In this showcase I would like to share my considerations for the tool of choice and my experiences working with it. The task at hand PDF production for product pages on an existing ProcessWire installation acniti.com is a Japan based company that specializes in nanotechnology solutions for gases in water. They have developed technologies to create nanobubbles in water, which can change the properties of water and improve dissolved gas levels through super saturation. Their founder and CEO has chosen ProcessWire for his website and has developed the site himself. He tasked me to add functionality for creation of downloadable PDFs from available product data. The client was forwarded to me from Bernhard because of time constraints on his side. I really appreciate that. The core requirements in detail: Produce separate PDF files for multiple languages, including LTR languages Design a template: cover page, images page, content pages, specs pages Cache PDFs Minimal impact on existing template code Tool choice: RockPdf module I had done some PDF generation before, using mPDF library directly. While there are other options available for ProcessWire (Pages2PDF, MakePDF) I decided to use Bernhard's RockPdf module since it seems the most feature rich and well maintained one. It is also based on the quite popular mPDF library. Reasons for choosing RockPdf Auto-reloading of PDFs in concert with RockFrontend This can save a lot of time (and strain on the F5 key), since the PDF is automatically reloaded every time a code change is made. All it requires to achieve that is one line of code: $pdf->save(__DIR__ . "/tmp/{$filename}.pdf", preview: true); Easy font loading I needed to load different fonts depending on the content language. Here is, how I did that RockPdf CSS utility classes They really came in handy, since they are specifically targeted at mPDF, using pt units. I could easily set some base colors with $pdf->setLessVars([ 'col-muted' => '#dddddd', 'col-primary' => '#0b54a2', ]); The utility classes that I mostly used are those for widths, margins and paddings. They are quite handy for setting up the layout of the PDF. Easy file saving to PW field For caching the created PDFs I utilized a RockPdf convenience method to save the files to a field on the product page $pagefile = $pdf->saveToField( page: $page, field: self::PDF_FIELD, filename: $filename, append: true, ); Implementation Modular approach for minimal impact on existing code I created two modules: Main module ProductPdf: non-autoload, holds all logic/templates for generating PDFs and required markup (download button) Module ProductPdfHooks: autoload, hooks: Page(template=product)::render displays PDF for download Pages::saved(template=product|product-type) creates PDFs in all languages and saves them to field for caching Re-usage of existing template logic There was quite a lot of logic (some of it rather complex) already available in the project that I wanted to utilize. Mainly for specs table creation. I had to do some minimal refactoring of original code. Then I was able to include that logic into my module without having to duplicate it. Benefits of this approach: Minimal impact on existing code Easier to maintain Challenges Limitations of mPDF library mPDF is not good at modern CSS features. It is quite limited in terms of CSS support. I had to do some workarounds to make it work with the layout I needed. Different approach to styling Although RockPdf's utility classes helped a lot, I still had to do some inline styling. Display of complex tables Display of tables in particular was a pain point since mPDF does a lot of automatic adjustments to column widths and distribution that I needed to disable in order to get the desired results: // Ensures that tables retain their original proportions $mpdf->keep_table_proportions = true; // And adding autosize="1" attribute to tables. Page headers, footers, margins and page breaks The RockPdf module docs have some great examples for setting up headers and footers, margins and page breaks. I used those to set up the layout of the PDF without having to read too much into the mPDF docs. Minimal impact on exisiting code base This was overcome by the modular approach I described earlier and it worked out really nice. The only addition to the original product template file for rendering the download button, was calling a static method from my module: <?= ProductPdf::renderDownloadbutton($page) ?> That button requests the page URL with a query parameter. The display of the PDF for download is handled through a Page::render hook PHP DOM manipulation of existing markup necessary Since I reused existing logic for constructing specs tables, I needed to add some inline styles and change some URLs on the fly. I used native PHP DOMDocument for that. There is a feature in the RockFrontend module that offers DOM manipulation tools with a nice API. I would have loved to use those but at the the time of working on this project, I just was not aware of their existence. The result Product pages on acniti.com now have a download button that allows the user to download the PDF of the product page in their language. See it live here The PDF is loaded from the cache field on the page, which is updated every time a product is edited and saved. If no cache file exists, the PDF is created on-the-fly and cached for future use. It is presented to the user in a new browser tab for viewing and downloading. The PDFs feature a clean layout / design which corresponds to the acniti branding. Cover page: Content pages: Specs table: Feedback from the client The client has a lot of experience with ProcessWire which one can see from looking at their website at acniti.com. He gave me great feedback on the project: Erik van Berkum, acniti LLC Lessons Learned and conclusion PDF creation in PHP is still not an easy task. The most popular library, mPDF, has some restrictions related to CSS that can make it tedious to work with. Especially when it comes to more complex layouts and tables. Using the RockPdf module for the task was a great choice. Its API is very well designed, offers a lot of conveniences and is well documented. Bernhard responded quickly to my questions and provided great support. In conclusion, the ProcessWire ecosystem offers great tooling for PDF creation that makes life for developers more enjoyable :-) Future considerations Would I use this approach again? Well, that depends mainly on the requirements. For this task the chosen tooling worked very well and I am happy with my choice. But for more complex layouts/designs that require modern CSS features, I would prefer rendering PDFs through Chromium browser using puppeteer or a self-hosted gotenberg service.1 point
-
Just clicked FOLLOW. I'm on there for over 15 years, and while it needs more work done itself, it just got way better. At least for me. I would do that. I'll take care of this. It absolutely is. Best community ever. AND it's a forum/board and not Discord or whatever.1 point
-
I had to review my code a bit to see what I meant. Bad job formulating that question... can confirm that my brain is soup. I needed to get the value of addClass. When I create the custom fields I'm setting a value for 'addClass' and while I can loop through the custom field to get names/values I couldn't access an underlying field object to get to the class string. It ended up being complex but I think that's because of how the data is stored for the child fields. Here's the loop I put together where the subfield had to be accessed separately to get the value set by 'addClass', using that chain I noted above was just because it was in this context. <?php foreach ($customField->defs()->getInputfields()->children() as $subField) { $subField->class; // Provides the class that I added when configuring the child field // Skip this field if it has X class assigned $subField->value; // Doesn't provide the value $page->{$customField->name}->{$subfieldDef->_custom_property_name}; // Used this to get the value because it was accessible here } // Looping this way looped over the 'data' property of the CustomField object foreach ($customField as $subfield) { $subfield; // Not a field object so no value or class property } So it was a workaround because I couldn't iterate over the child fields as if they were normal fields.1 point
-
Thanks @da² and @Jonathan Lahijani for your answers. Yeah, I agree that it seems from what I've read it should just work, I just like to test to be sure and was hoping to do it locally, but maybe it's unnecessary. I have VirtualBox on my Mac but haven't worked with it directly, just via Vagrant and Genymotion (Android emulators). I could see about getting Debian or Ubuntu running in that. Or alternatively, I could just use DigitalOcean since I'm very familiar with them and have used them for several ProcessWire sites in the past (I even wrote this old article for them ~10 years ago: https://www.digitalocean.com/community/tutorials/how-to-install-processwire-on-an-ubuntu-vps) – I suppose setting up LiteSpeed and MariaDB won't be much different, I was just hoping there was a quicker way than provisioning a Linux box myself, e.g. a Docker compose file that maybe works out of the box. Thanks everyone, I appreciate the quick responses!1 point
-
@FireWire You can just iterate $page->your_custom_field as if it were an array. There are a couple of foreach() examples in the blog post, in the section headlined "Outputting custom fields". Though let me know if I've misunderstood what you are looking for.1 point
-
@ryan Is there a way to make the custom field children iterable? I have a script that loops through a couple of fieldtypes on a page where I don't know the names of the custom field children at runtime. My solution requires a little digging to access the '_custom_property_name' by drilling down into $customFields->defs()->getInputfields()->children() then looping through the subfield defs and accessing the value using $page->{$customField->name}->{$subfieldDef->_custom_property_name} I might be missing a better way to do that. I'm coding so much that my brain is going to melt and might have missed the right way to do it.1 point
-
Hi all , I get a "Headers already sent" when i call PW from the commandline. If i start output before including PW. I know that is an Issue if doing output in a webbrowser before starting the actual session but i did not expect this in a CLI call. Here is the script: <?php namespace ProcessWire; $path = "../../index.php"; $path = realpath($path); echo ("\nPath: " .$path. "\n"); include $path; Here is the Error: Path: /usr/local/www/apache24/noexec/mein-kaffe.de/processwire/index.php PHP Warning: session_set_save_handler(): Cannot change save handler when headers already sent in /usr/local/www/apache24/noexec/mein-kaffe.de/processwire/wire/core/WireSessionHandler.php on line 57 Warning: session_set_save_handler(): Cannot change save handler when headers already sent in /usr/local/www/apache24/noexec/mein-kaffe.de/processwire/wire/core/WireSessionHandler.php on line 57 PHP Warning: session_name(): Cannot change session name when headers already sent in /usr/local/www/apache24/noexec/mein-kaffe.de/processwire/wire/core/Session.php on line 286 Warning: session_name(): Cannot change session name when headers already sent in /usr/local/www/apache24/noexec/mein-kaffe.de/processwire/wire/core/Session.php on line 286 PHP Warning: ini_set(): Headers already sent. You cannot change the session module's ini settings at this time in /usr/local/www/apache24/noexec/mein-kaffe.de/processwire/wire/core/Session.php on line 289 Warning: ini_set(): Headers already sent. You cannot change the session module's ini settings at this time in /usr/local/www/apache24/noexec/mein-kaffe.de/processwire/wire/core/Session.php on line 289 PHP Warning: ini_set(): Headers already sent. You cannot change the session module's ini settings at this time in /usr/local/www/apache24/noexec/mein-kaffe.de/processwire/wire/core/Session.php on line 290 Warning: ini_set(): Headers already sent. You cannot change the session module's ini settings at this time in /usr/local/www/apache24/noexec/mein-kaffe.de/processwire/wire/core/Session.php on line 290 PHP Warning: ini_set(): Headers already sent. You cannot change the session module's ini settings at this time in /usr/local/www/apache24/noexec/mein-kaffe.de/processwire/wire/core/Session.php on line 291 Warning: ini_set(): Headers already sent. You cannot change the session module's ini settings at this time in /usr/local/www/apache24/noexec/mein-kaffe.de/processwire/wire/core/Session.php on line 291 PHP Warning: ini_set(): Headers already sent. You cannot change the session module's ini settings at this time in /usr/local/www/apache24/noexec/mein-kaffe.de/processwire/wire/core/Session.php on line 292 Warning: ini_set(): Headers already sent. You cannot change the session module's ini settings at this time in /usr/local/www/apache24/noexec/mein-kaffe.de/processwire/wire/core/Session.php on line 292 PHP Warning: ini_set(): Headers already sent. You cannot change the session module's ini settings at this time in /usr/local/www/apache24/noexec/mein-kaffe.de/processwire/wire/core/Session.php on line 304 Warning: ini_set(): Headers already sent. You cannot change the session module's ini settings at this time in /usr/local/www/apache24/noexec/mein-kaffe.de/processwire/wire/core/Session.php on line 304 I am not sure but shouldn't the session be started when running from CLI ?1 point
-
Hi, So, does the table usr_p624065_1.field_select_hidden_categories exist? 😁1 point
-
I decided not to use that approach for this issue. It would be quite odd to inform the customer "randomly" with a message like, "Hey, the owner of the store is currently editing your cart!" My client and I believe that customers should not be notified in the same way that other backend users are. Such a level of transparency is not what my client wants. Therefore, I opted for the "simplest" solution: locking the cart. However, even locking the cart is not entirely straightforward. In my code, I address not only quantity and price-related issues but also other validation concerns, like this: When a customer loads the cart on the frontend, the system detects all invalid items (it contains outdated prices or product configurations, for example), logs them, and presents the list to the customer before automatically removing them. Based on this log, the backend administrator will receive a daily digest, while customers can optionally request the system to send the list to their registered email address. When a customer loads a cart that is locked by backend users, they are notified that their cart is temporarily locked by the shop owner. They are also presented with a button to click, which will send an automated request email to customer service. The customer will not have to write anything, as the email will contain all the information needed for customer service to unlock the cart. When a backend user accesses a cart edit page, they are presented with any invalid item errors. In this case, we do not want to automatically remove invalid cart items, as the backend user may be working on them, and we cannot determine their intentions. Because of this, no automatic action can be taken, except for one: the cart will be automatically locked if any invalid items are detected. It is then up to the backend user to resolve all issues to make each order item valid again. The backend user must click the 'Save + Unlock Cart' button to make it available for the client again. For editing the cart when there are initially no invalid items, backend users should start their editing session by using the 'Save + Lock Cart' button before proceeding with their edits. As long as no backend user is editing carts, there should be no instances of the cart being locked. Any unfinished tasks by backend users related to editing items should never occur, but they will probably happen a lot of the time. Backend users will receive red error messages that provide clear explanations about invalid items. As long as they do not unlock the cart, the last red error message instructing them to unlock it will remain visible. However, no one is perfect, so backend users will receive an email digest of any carts that may have been left locked accidentally. Our backend users rarely edit cart items, so I believe this relatively simple solution will suffice.1 point
-
1 point
-
1 point
-
One idea to solve this in a quite easy way: Add a hidden field to your template called "quantity_before_edit" Add a saveReady hook that calculates the difference between "quantity_before_edit" and "quantity" Use that delta to calculate the new "quantity" Example: Quantity is 10 User A visits the public website (quantity = 10) User B visits the backend (quantity = 10, quantity_before_edit = 10) User A orders 2 pieces and the page gets saved (quantity = 8 ) User B saves the page without editing the quantity (quantity = 10, quantity_before_edit = 10) saveReady hook kicks in: quantity = 10, quantity_before_edit = 10 ---> diff = 0 get current quantity from uncached page from the database, quantity = 8 add diff and write result to $page->quantity = 8 ideally show a warning to the user that a quantity has been saved that is different from his/her input page is saved, final result is quantity = 81 point
-
AltTextGpt for ProcessWire This ProcessWire module, AltTextGPT, is an interface for generating alt text for all of the images in your website, using the ChatGPT Open AI API. Using the API requires an account with the Open AI API and costs money, although its pay-what-you-use and the charges are minimal. For example, alt text was generated for 200 images, using 94 cents of Open AI Credits. You can create an account with Open AI, from this link, and then once you have an API key, you can enter it below, or configure it as a permanent setting for this module via Modules->Configure->AltTextGpt. After configuring the API key as described above, you can then use the form below to generate alt text for images in the site. The module will attempt to generate alt txt for every image that currently has no alt text, one at a time. Generating alt text takes a few seconds for each image, so this is not an instantaneous process. For this reason, if you have many images, we suggest generating alt text for the images in batches. You can also set a batch size below, generating alt text for 10 or 20 images at a time, and then repeating the process, until you have generated alt text for all of the images in the site. After each run, the table above should show that there are fewer images without alt text in the site, until eventually the table indicates that there are 0 images in the site without alt text. Note, for alt text to show up for images uploaded in the body of a CKEditor field, this configuration must be set for that field as described in this comment. How to install this module Copy all of the module files to /site/modules/AltTextGpt/. In your admin, go to Modules > Refresh. Click “Install” for the “AltTextGpt” module (on the “Site” tab). The code for the module is tested and working and is currently here: https://github.com/mhfowler/AltTextGpt1 point
-
The ProDevTools module of User Activity is another possible option. I haven't used it, so I'm not 100% sure if it prevents this from happening, or just alerts users when the possibility of a collision exists. https://processwire.com/store/pro-dev-tools/user-activity/1 point
-
1 point
-
1 point
-
1 point
-
1 point
-
@MrSnoozles sorry, I have to correct myself here. I should have written that MariaDB and MySQL are generally interchangeable. There are different features since the very beginning, but they are very specific. It seems like, though, MySQL 8.0 has some bigger differences. Concerning JSON functionality. For what I understand, the difference is how JSON is stored on each of them. MySQL stores JSON as binary objects, and MariaDB as strings, but the functionality is (again) generally similar.1 point
-
Sounds very promising @bernhard! I a past project I had the need of creating and managing recurring events, too. Back then there was no calendar module available for ProcessWire, so I wrote my own logic using PW repeater fields in combination with the RRULE PHP library. The interface for a event page looks like this. In this example the event occurs every third tuesday in every month from 4pm - 6pm. I handle the updates of the dates that are displayed in the frontend via a cronjob that runs every night. The date field then gets updated, based on the set of rules that are set for the event: What is handy feature for me is to get an overview of upcoming events. Either in a classic "calendar view" or maybe in a small list overview like here. I list all events within the current week:1 point
-
This is difficult to fix without more of the surrounding code. This isn’t on Github by any chance? If you’re not comfortable with fixing it yourself, you should probably give someone with expertise in PHP or ProcessWire direct access. Also please use the code formatting to post snippets, instead of quotes. Try to see if you can find where $contact is declared in the first 25 lines of structure.php. Apparently it’s a Boolean, but is expected to be a Page.1 point
-
Parts of it, at least, yes! I've had for a very long time on my todo list a related idea; basically something like the https://translate.wordpress.org/ portal for WordPress. Though I didn't honestly even consider using pre-built solutions. I don't say this lightly, but this is an area where WordPress is way ahead ProcessWire, in my opinion. Just a few reasons why I think their system is so good: It's a shared, easy to use, web-based, non-developer friendly system. Whether I'm a contributor or someone wanting to install a translation for a plugin, I don't need to figure out how author of plugin x wants to handle their translations, and I also don't need to know or figure out how to create a GitHub account, open pull requests, etc. This point alone is a major benefit and lowers the bar for contributing ? Just for the record, many plugins still bundle translations with the source code. So yes, both systems can co-exist. Anyone can contribute without core/theme/plugin author(s) having to do anything. I for one have access to Finnish translations for a few plugins. Permissions are handed down by general translation editors for the locale, which is much easier than having to contact individual plugin authors who may or may not be around and active, and may or may not be interested in bundling some language they have no personal interest in. Once a locale has enough coverage, it automatically becomes installable via the admin and via WP-CLI. Personally I use WP-CLI for managing site translations, as it makes things a lot easier to automate. Installing and updating translations is literally one command away, which — as someone who manages quite a few sites — is a big bonus. This, of course, only works for plugins that have been translated this way. Not all have. It's not a perfect world. Finally, the system handles versions properly. For an example if I wanted to submit a correction for the Finnish translations for ProcessWire version 3.0.184, there's no easy way to do that. Maintainer of the Finnish translations repository could set up separate branches or some other way to handle it, but this would be a case by case thing, and there's no guarantee that the language pack maintainer is interested in such a thing. In the past I've also contributed via Launchpad, which (at least back then) was essentially the same thing for free / open source software, and worked really well in my opinion. Anyway, I'm still interested in this idea, but have not had time to pursue it myself. Maybe one day, unless someone else solves it properly before me ? In my opinion it is a viable idea, but there are many questions to answer, and quite a few of those are not straightforward. Also I believe that in order to gain traction this would need to be somehow integrated into our current translation tool(s) and it shouldn't be too opinionated (for an example I personally would prefer it to not update anything completely automatically, as was suggested here earlier.)1 point
-
I hope so! It is always sad to think about those (used to be) active members who no longer visit the forum.0 points