Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 08/30/2024 in all areas

  1. This week we introduce a new module named Custom Fields. This module provides a way to rapidly build out ProcessWire fields that contain any number of subfields/properties within them. No matter how simple or complex your needs are, Custom Fields makes your job faster and easier. Not only does this post introduce Custom Fields, but also documents how to use them and includes numerous examples— https://processwire.com/blog/posts/custom-fields-module/
    8 points
  2. 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; } 😎
    5 points
  3. I'm working on RockCalendar which will be released soon if everything goes smoothly 😎 I think it should be stable enough next month. If you want to get notified you can subscribe to the Rock Monthly Newsletter. The module uses https://fullcalendar.io/ for the calendar interface and when clicking an event it will open the page editor in a pw modal: Opening the modal was a bit of a challenge, because PW unfortunately does not offer a good JS API to open modals. It only offers markup-based options to control the behaviour of the modal - at least as far as I could find out. This is a problem, because all events in the calendar get loaded by fullcalendar and I only have the eventClick() callback where I need to fire my action. My solution is to create a fake element in the dom and trigger a click on that element: calendar.on("eventClick", (info) => { // create a fake link element in body let link = document.createElement("a"); let $link = $(link); $link.attr( "href", ProcessWire.config.urls.admin + "page/edit/?id=" + info.event.id ); $link.addClass("pw-modal"); $link.attr("data-autoclose", ""); $link.attr("data-buttons", "button.ui-button[type=submit]"); $link.on("click", pwModalOpenEvent); $(document).on("pw-modal-closed", this.refresh.bind(this)); $link.click(); $link.remove(); }); Maybe that helps anyone else looking for a solution to a similar problem. If anybody knows a better way how to do it, please let me know! PS: If you have input (feature requests) for the development of RockCalendar please let me know: https://processwire.com/talk/topic/30355-request-for-input-what-features-should-a-pw-calendar-module-have/
    3 points
  4. For a current project I have the need to show and manage events. First, I thought I'd just show events as a list. The benefits of this approach are that it is very easy to do and it works well on desktop and mobile. Then I had a chat with @gebeer and we concluded that a real calendar interface is far more intuitive for the average user. I've built such a calendar once on an ancient site (https://www.mistelbach-mustangs.at/events/), but it was a lot of work and so I didn't do it again for any other project. Then I thought let's use google calendars for managing events. That would have the benefit that we get a lot for free (the whole gui for managing events, the data storage, recurring events, etc). But it also comes with downsides: First, clients need to create an account for their calendar on a third party. We will hopefully have many clients on that project, so instructing them to create an account somewhere else and then connecting both apps together is a lot of overhead. Second, events handled by google have a totally different data structure than PW pages. What if I wanted to display those events? What if I wanted to add image galleries to those events? What if I wanted to have event descriptions powered by RockPageBuilder? What if I wanted the best performance one can get using the awesome ProCache? These things are very easy to do when we are dealing with PW pages, but they are hard if we talk to a third party api like the one from google. So I decided to build RockCalendar. A module where a FullCalendar shows PW pages and all the tedious things are already built in (like managing events, creating events, resizing events, managing locales, etc). I've made good progress over the last few days and I think I'll have something to share next month, so I thought I'd ask for feedback what the community thinks would make sense for such a module? Have you ever had the need for such a module? If yes, what would be the key features that you'd want to see in such a module? What would be nice to have? What would be questions that come up in your head? I can't promise, but I'll try my best to incorporate the community's feedback and suggestions into the RockCalendar module to make it as useful and versatile as possible.
    2 points
  5. 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/AltTextGpt
    2 points
  6. This looks fantastic. I'm at my computer right now working on a ProcessWire site, hop over to the forums, see this, immediately useful. Awesome work, thanks!
    2 points
  7. I can give a quick reply about the Geffen calendar, but most of what there is to know about it is in that article I wrote. How the show performances (events) came to exist wasn't a concern as that was managed with a 3rd party service, obtained via an API and stored as child pages of a show. The main takeaway from that project was that we needed to simply display upcoming shows in a monthly calendar format (large and small) and when I originally developed the site, I leaned on FullCalendar. However that was total overkill and difficult to maintain because FullCalendar is more than just a way to display events in a calendar format, but an interactive component with dragging and dropping like what you'd get in a real calendaring system (Google Calendar, etc.). It's also built with complex JS which I like to avoid. I just needed an easy way to display information with simple AJAX loading between months. To solve those, CSS Grid (ie, "display:grid") and HTMX fit perfectly with some straight-forward PHP code to build it. One extra improvement that can be made to display a large and small calendar in different contexts while using the same HTML is to use container queries, but because browser support for it a couple years ago was still very low, I avoided it.
    2 points
  8. I did something similar using image tags. I placed all the photos in an image gallery and assigned the 'cover' tag to the desired photo to be used as the cover. Then: $image = $page->images->find("tags=mytag")->first(); My client actually loved this approach because we created new tags, and they can now assign them according to the position where the photo will be used
    2 points
  9. Calendaring and/or events management is a large undertaking, unless feature requirements are kept extremely minimal on purpose. The hardest, but often most requested aspects of a calendaring solution would likely already have discussions in the Recurme module's discussion topic, and Ryan's Date Range Fields (ProField) topic...so, essentially, recurrence (RRULEs), and how to handle them. I remember you even broke down the various problematic ways of attempting to solve searching, or retrieving, for events with, I think, a Fieldtype module, where the problems were with recurrence and ranges. For storing recurrence of events, there are also two potential scenarios, architecturally: Create individual unique entries (PW pages, in this case) for each event that is part of a series - it can then be modified independently even if mildly associated to the original group. Create a single entry that stores metadata about the recurrence to extrapolate virtual entries from the original. Both options have their pros and cons, but I'm not familiar enough with the research behind them to understand what would be best, or if there's even a hybrid approach. I'd imagine the first would be far easier to implement and maintain. Related to RRULE would also be vCalendar/iCal/ICS support. Best suggestion: If feature suggestions are calendar/event adjacent, but don't actually belong to calendaring, do not support it. I was just thinking how iCal is used to help customers remind themselves about events, but more often customers don't know how that technology works, so offering an email reminder for the event could be a feature suggestion. However, emailing could easily be handled by a developer that is taking advantage of your module and expanding on it, so although emailing a reminder about an event is related to an events calendar, emailing itself is not an integral part of an event calendar. 😉 @Jonathan Lahijani might have some suggestions too, with his use of one with the Geffen Playhouse. His solution was different, but issues he dealt with may be of use. Good luck, Bernhard!! 💪
    2 points
  10. Done: restructure install.php to do away with warnings by poljpocket · Pull Request #300 · processwire/processwire (github.com)
    2 points
  11. Hard to know exactly what to suggest as many different scenarios could affect responsiveness. (Re-?)Rendering images could easily slow things down, as could page reference properties that aren't loaded until called/necessary. Depending on your setup, using findRaw() or findMany() might have a positive impact, as would your autojoin; if you think an autojoin might only be necessary for your one scenario, you could look into findJoin() and whether it accomplishes the same end-result on that page. For a ProcessWire (Pro) module assist, there's always ProfilerPro, as well.
    2 points
  12. Add Image URLs A module for ProcessWire CMS/CMF. Allows images/files to be added to Image/File fields by pasting URLs or using the API. Installation Install the Add Image URLs module. Configuration You can add MIME type > file extension mappings in the module config. These mappings are used when validating URLs to files that do not have file extensions. Usage A "Paste URLs" button will be added to all Image and File fields. Use the button to show a textarea where URLs may be pasted, one per line. Images/files are added when the page is saved. A Pagefiles::addFromUrl method is also added to the API to achieve the same result. The argument of this method is expected to be either: a URL: "https://domain.com/image.jpg" an array of URLs: ["https://domain.com/image1.jpg", "https://domain.com/image2.jpg"] Example: // Get unformatted value of File/Image field to be sure that it's an instance of Pagefiles $page->getUnformatted('file_field')->addFromUrl("https://domain.com/path-to-file.ext"); // No need to call $page->save() as it's already done in the method Should you have an issue using the method, please have a look at the "errors" log to check if something was wrong with your URL(s). WebP conversion The core InputfieldImage does not support images in WebP format. But if you have the WebP To Jpg module installed (v0.2.0 or newer) then any WebP images you add via Add Image URLs will be automatically converted to JPG format. https://github.com/Toutouwai/AddImageUrls https://modules.processwire.com/modules/add-image-urls/
    1 point
  13. Logs JSON Viewer Formats JSON data in ProcessLogger for improved readability. Because log files can only contain strings, it's a common practice to use json_encode() to convert an array to a string when you want to save the data to a log file. But the resulting JSON is not as readable as it could be when viewing the log in ProcessLogger. The Logs JSON Viewer module uses the json-viewer library to improve the readability of JSON data in ProcessLogger and add some useful features. Before: After: Configuration You can set the config options for json-viewer in a textarea field. See the json-viewer readme for information about the options. There is also an option to set the width of the column that contains the JSON data. This setting exists because otherwise the column jumps around in an inconsistent and distracting way when changing from log to log or between paginations. Features You can switch the view of the JSON data between formatted and unformatted using the toggle button at the bottom of the data. The viewer has a number of useful features such as: Progressively expand or collapse levels in the data. View the count of child items and the data type of each item. Search for a string within the data. Copy all or part of the data to the clipboard (requires the HTTPS protocol). https://github.com/Toutouwai/LogsJsonViewer https://processwire.com/modules/logs-json-viewer/
    1 point
  14. @ryan, thanks for creating this cool module! A couple of questions: 1. Do the subfields have an ID of some sort separate to the name, such that a subfield can be renamed without losing the existing data stored for the subfield? 2. What happens to stored data if a subfield is removed from the defining file? Does the existing data for the subfield persist in the database, or does the fieldtype detect the removal and do some cleanup to remove the stored data? Also, I think there's a typo in the blog post. // multi-selection foreach($page->custom_field->countries as $value) { $label = $page->field->label('countries', $value); echo "<li>$value: $label</li>"; // i.e. "USA: United States" } ...should be... // multi-selection foreach($page->custom_field->countries as $value) { $label = $page->custom_field->label('countries', $value); echo "<li>$value: $label</li>"; // i.e. "USA: United States" }
    1 point
  15. @Jim Bailie They are defined just in a PHP (or JSON) file, so exporting (or importing) the definitions would be just a matter of copying the file from one system to another.
    1 point
  16. Wow. This is really something. Thanks @ryan! I didn't read the the entire post, but are these or will these Custom Field "maps" be exportable from an existing install?
    1 point
  17. Oh excellent, thanks! I’ve been asking myself this, but been too lazy to actually figure it out instead of just changing the table manually for project-specific modules 😄
    1 point
  18. No need for multi-day events, so that wasn't even something I thought about. That's actually something I would imagine would make the CSS Grid approach potentially unusable if you need to span the event over multiple days, but at the same time support for spanning was one thing that made FullCalendar difficult to style for my simple needs, at least in v3. As for display order/stacking, that was another issue that I had to hack around with FullCalendar. For example, if Geffen had a day with an events schedule like this (ordered by time): Show A: 2:00pm Show B: 3:00pm Show A: 4:00pm Show B: 5:00pm They would actually want it to be displayed like this (ie, grouped by show, then time): Show A: 2:00 pm 4:00 pm Show B: 3:00 pm 5:00 pm That's not the "natural" way it would work in a pure calendar sense (which orders strictly by time), so with FullCalendar I had to basically do-away with how it normally puts in an event and inject custom HTML, which kind of defeats the purpose of using a calendar library. I believe when they switched from v3 to v4, they changed too much of how it worked internally and I didn't want to redo that work-around to be compatible with v4, so that's when I gave the whole approach a re-think (we launched in 2019 with FullCalendar, then I re-did it in HTMX in 2022).
    1 point
  19. 1 point
  20. When I need to load a fair amount of data I do custom MySQL queries. findRaw() is very fast too but I don't like to parse its results. Just be careful to sanitize data where needed (all texts at least). The downside is that it takes much more development time. But I become better in MySQL stuff. 😁
    1 point
  21. I've done a video about DDEV, but it doesn't go through setting up ddev. Just follow the instructions on the ddev website. For this you need to tell tracy debugger where the files live on your local file system. This is because PW + Tracy run inside the container and inside the container your files live in /var/www/html. So if you click on a debugging link in tracy then your OS tries to find the file /var/www/html/foo.php and obviously can't find it. // tracy config for ddev development $config->tracy = [ 'outputMode' => 'development', 'guestForceDevelopmentLocal' => true, 'forceIsLocal' => true, 'localRootPath' => '/path/to/your/project/', 'numLogEntries' => 100, // for RockMigrations 'editor' => 'cursor://file/%file:%line', ]; The important part for you is the "localRootPath" setting.
    1 point
  22. Sorry, my bad. Yes 😀. I'll post an example later today. Sorry, still working on docs.
    1 point
  23. @BrendonKoz Thanks for your reponse, yes this would absolutely be the reasonable solution in most cases, but the structure I have here is weird and it seemed relevant to have 2 separate fields at the time I created it. Maybe in hindsight I still should have gone for this solution, but it's just a personal project so I can't be bothered to change everything now, I'll have it learned for next time :^) @dynweb Thank you very much this is what I was looking for. Here's what I ended up doing, it works perfectly if ($cover_image) { $images = $images->prepend($item->cover_image); } if ($cover_video) { $videos = $videos->prepend($item->cover_video); }
    1 point
  24. Alternatively, you can just use different logic on your interactive procedure. I do something similar to this with just a single field, where the first image (manually sorted) in an images (Multiple Images field) is used as the primary/cover photo. All of the rest are placed into the standard gallery. The API makes that easy with the WireArray class methods of first() (only retrieve the first when using the "cover photo" as a portfolio thumbnail) and shift() (if you wanted to separate the first/cover photo from the rest of the group in a template). Images are files, so you can use the same logic for file fields. If you don't want to modify your template(s) and would prefer to keep your logic flow intact, what dynweb mentions should work too. 🙂
    1 point
  25. Pageimages::add() should do what you are looking for.
    1 point
  26. So I stumbled over the request to allow limiting templates to be used only once under every parent page in this thread and found that this would actually come in handy (also in a site I've built). The code can be found on github and soon also in the module repo. After installation, you'll find a new checkbox "Only once per parent" in the family tab when editing a template.
    1 point
  27. WebP to JPG Converts WebP images to JPG format on upload. This allows the converted image to be used in ProcessWire image fields, seeing as WebP is not supported as a source image format. Config You can set the quality (0 – 100) to be used for the JPG conversion in the module config. Usage You must set your image fields to allow the webp file extension, otherwise the upload of WebP images will be blocked before this module has a chance to convert the images to JPG format. https://github.com/Toutouwai/WebpToJpg https://processwire.com/modules/webp-to-jpg/
    1 point
  28. Very old indeed, but still useful... and still working ? Who said plugins needed to be updated every week ? Oups... wrong forum... ? I’ll open an issue on Github.
    1 point
  29. Sure! It's fine to have a diverse community, as long as people aren't using the current global warming situation to convince others to commit suicide or avoid having kids, for example. Making people afraid of partially real or made-up problems, just so those causing the fear can act like they're saving you, is a sneaky way to control you. So, fear not, but be skeptical and read the opinions of others as well (eg.: https://angusmac.net/category/climate-models/), and not just what the mainstream is trying to sell so hard. Those in power are not there because they want what's best for you. If they wanted that, they would not be in power in the first place.
    1 point
  30. Hi. https://github.com/processwire/processwire/blob/master/wire/core/Templates.php#L303 So it looks like cloning of the template doesn't clone fields context for template fieldgroup. You can do it by api like $sourceFieldgroup = $fieldgroups->get('service'); $newFieldgroup = $fieldgroups->get('test'); foreach ($sourceFieldgroup as $field) { if($sourceFieldgroup->hasFieldContext($field) && $newFieldgroup->hasField($field)) { $contextArray = $sourceFieldgroup->getFieldContextArray($field->id); $newFieldgroup->setFieldContextArray($field->id, $contextArray); } } $newFieldgroup->saveContext(); Probably there is better way but it worked for my case.
    1 point
  31. I figured it out, hopefully this might be useful to others as well: // Remove current thumbnail first. $currentThumb = $page->image_thumb->first(); $page->image_thumb->delete($currentThumb); // Get the image we want to copy. $original = $page->image_front; $file = $original->getFile($original); // Clone that image and append it to the image_thumb field. $new = $original->clone($file, array('action' => 'append', 'pagefiles' => $page->image_thumb)); $page->save(); I noticed that this does not work if the filename of the file you are trying to clone ends with -0 (e.g. myfile-0.jpg). I think that is a bug which I'll post to GitHub.
    1 point
  32. maybe this one: https://modules.processwire.com/modules/frontend-user/ (https://www.google.at/search?q=site:processwire.com+frontend+login) Maybe you have good reasons to do this in the frontend but using the admin in ProcessWire might be simpler than you think: https://processwire.com/blog/posts/building-custom-admin-pages-with-process-modules/ Staying in the pw admin has the benefit that you have all the tools available: access control, collapsible fields, hooks etc.
    1 point
×
×
  • Create New...