Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation since 02/19/2024 in all areas

  1. On the dev branch this week we have a good collection of issue fixes and feature requests. The dev branch commit log has all the details. One feature added this week which I think could come in very handy is #520 (via @Jonathan Lahijani) which adds the ability to hide individual images in an images field. When an image is hidden, you can see and work with it in the admin, but it gets removed from the field value on the front-end of the site at runtime, effectively hiding it. I know I'll use this a lot, particularly on photo galleries where I may want to remove an image or two from appearing in the grid of photos, but don't necessarily want to delete them. Images can be hidden (or unhidden) from the Actions select of each image, where you'll see a "Hide" option (or an "Unhide" option if the image is already hidden). Hidden images are also dimmed out when viewing the images field in the admin. On the API side, you can hide or unhide images and files using $image->hidden(true) to hide, $image->hidden(false) to unhide, and $image->hidden() to get a true or false as to whether or not the image is hidden. Though this will only be useful on unformatted field values, since hidden images are automatically removed from formatted field values. The same can be used with regular file fields, but we don't currently have a UI/interface for hiding or unhiding items from regular (non-image) file fields. Likely we'll add one soon, but I figured it's likely to get more use with image fields than file fields, so figured we'd start there. More next week. Thanks for reading and have a great weekend!
    30 points
  2. There are a few commits on the dev branch this week, but nothing particularly notable. I had to do some client work this week but also ventured back into the Invoice site profile, which I mentioned quite awhile ago, but hadn't yet released. I'm hoping to finish that up and release it as another site profile option for ProcessWire very soon. Perhaps as soon as next week. I had originally planned on building out that Invoice site profile quite a bit more, but having used it for my own invoices for many months (maybe a year?), I think it's better to keep it simple. Otherwise I'd be making assumptions about what others might need. Even in its relatively simple state, it suits my own needs well. And I think if it gets more complex, then people are less likely to explore and modify it, making it less useful as a site profile. The site profile is also simple enough right now that it doesn't need to be a Pro module or need Pro-module support. Since we don't have a lot of site profiles to choose from, I'd rather keep it free. It is admittedly more of a mini application than a website, which is why I think it might bring some more diversity to the available site profiles. Chances are it won't replace whatever invoice service you might be using, but I think it's still pretty useful, whether using it to create invoices, or just using it to explore more of ProcessWire. More next week. Have a great weekend!
    25 points
  3. This week the new ProcessWire Invoices site profile has been released on GitHub here: Invoice Application Site Profile. This particular profile is much broader in scope than the others I've developed for ProcessWire, so will benefit from more descriptive information about what it includes, how to use it and modify it, and how you might build further from it. That info will all be coming next week in a new blog post. But feel free to download and install the site profile sooner if you'd like. If you are already familiar with ProcessWire, then perhaps most of it will be self explanatory. While this site profile doesn't cover everything that you might do with an invoicing application, it does cover everything that I've needed over the last year of using it with my clients. Though admittedly, I don't have a lot of clients, nor do I send a lot of invoices. Given that, when my existing invoicing service raised the monthly rate from $3/month to $20/month (a year or so ago), that's what motivated me to build this site profile. And it does everything my previous invoicing service did, and in fact does it better. While much of it was built several months ago, major improvements have been made to it over the last couple of weeks, preparing for it for release as a ProcessWire site profile. My hope is that you'll find this site profile easy to work with, and easy to build out further where needed. For instance, I imagine some may want to add in the ability to pay an invoice. It would be relatively simple to add in FormBuilder with its Stripe Processor plugin, or perhaps some other payment solution. But all my clients pay by check, whether physically or digitally, so I've not needed to add payment ability to the application yet. In any case, I hope that you find this site profile useful, and please let me know if you run into any issues with it or have suggestions for future upgrades to it. Thanks for reading and have a great weekend!
    24 points
  4. Hey! I would like to announce that there is a new ProcessWire powered collaboration between me and Jan Ploch 👉 @jploch. When I moved to Hamburg, I contacted him through the forum and we ended up working at the same shared office. Meanwhile we collaborated on some projects and then started our own company (a GbR, in Germany). We named it KONKAT Studio. Now we are publishing our brand new website konkat.studio As you may know, Jan developed a page builder for ProcessWire called PAGEGRID. At first, we were going to develop our website the usual way, but then we decided that we should take PAGEGRID for a spin and beat the hell out of it, to see what we could come up with 😄 We had this idea of three rows scrolling in different directions, and because PAGEGRID has scroll animations built in, we managed to do it all without writing a line of code. We may write a case study soon for those who are interested.
    18 points
  5. The invoices application site profile that I uploaded last week now has a companion blog post: https://processwire.com/blog/posts/invoices-site-profile/ Thanks for reading and have a great weekend!
    16 points
  6. Verify Links Periodically verifies that external links are working and not leading to an error page. How it works The module identifies links on a page when the page is saved and stores the URLs in a database table. For the purposes of this module a "link" is an external URL in any of the following... FieldtypeURL fields, and fields whose Fieldtype extends it (e.g. ProFields Verified URL) URL columns in a ProFields Table field URL subfields in a ProFields Combo field URL subfields in a ProFields Multiplier field ...and external href attributes from <a> tags in any of the following... Textarea fields where the "Content Type" is "Markup/HTML" (e.g. CKEditor and TinyMCE fields) CKEditor and TinyMCE columns in a ProFields Table field CKEditor and TinyMCE subfields in a ProFields Combo field The link URLs stored in the database table are then checked in batches via LazyCron and the response code for each URL is recorded. Configuration On the module config screen you can define settings that determine the link verification rate. You can choose the frequency that the LazyCron task will execute and the number of links that are verified with each LazyCron execution. The description line in this section informs you approximately how often all links in the site will be verified based on the number of links currently detected and the settings you have chosen. The module verifies links using curl_multi_exec which is pretty fast in most cases so if your site has a lot of links you can experiment with increasing the number of links to verify during each LazyCron execution. You can also set the timeout for each link verification and customise the list of user agents if needed. Usage Visit Setup > Verify Links to view a paginated table showing the status of the links that have been identified in your site. The table rows are colour-coded according to the response code: Potentially problematic response = red background Redirect response = orange background OK response = green background Link has not yet been checked = white background Where you see a 403 response code it's recommended to manually verify the link by clicking the URL to see if the page loads or not before treating it as a broken link. That's because some servers have anti-scraping firewalls that issue a 403 Forbidden response to requests from IP ranges that correspond to datacentres rather than to individual ISP customers and this will cause a "false positive" as a broken link. For each link the "Page" column contains a link to edit the page and the "View" column contains a link to view the page on the front-end. You can use the "Column visibility" dropdown to include a "Redirect" column in the table, which shows the redirect URL where this is available. For those who can't wait The module identifies links as pages are saved and verifies links on a LazyCron schedule. If you've installed the module on an existing site and you don't want to wait for this process to happen organically you can use the ProcessWire API to save pages and verify links en masse. // Save all non-admin, non-trashed pages in the site // If your site has a very large number of pages you may need to split this into batches $items = $pages->find("has_parent!=2|7, template!=admin, include=all"); foreach($items as $item) { $item->of(false); $item->save(); } // Verify the given number of links from those that VerifyLinks has identified // Execute this repeatedly until there are no more white rows in the Verify Links table // You can try increasing $number_of_links if you like $vl = $modules->get('VerifyLinks'); $number_of_links = 20; $vl->verifyLinks($number_of_links); Advanced There are hookable methods but most users won't need to bother with these: VerifyLinks::allowForField($field, $page) - Allow link URLs to be extracted from this field on this page? VerifyLinks::isValidLink($url) - Is this a valid link URL to be saved by this module? VerifyLinks::extractHtmlLinks($html) - Extract an array of external link URLs from the supplied HTML string https://github.com/Toutouwai/VerifyLinks https://processwire.com/modules/verify-links/
    16 points
  7. Hello all- FieldtypeFormSelect does what the post title says it does. Lets you create a field to select a form created by the FormBuilder pro module. This type of field (or some variation of it) has probably been done elsewhere but I put this together with a few extra considerations for flexibility and utility. When creating a form select field you can choose what forms will be present and how their names will be shown. Let's go to the pictures: A form select field: Creating fields to choose from specific forms? You have options. You can also create a field that will only include forms that begin, include, or end with a specific string. This allows you to create a field once, then use form names to help group them together and add/remove them from form select elements without editing the field. This is also a pretty simple way to allow end users to create forms that will be selectable without having to ever edit a field configuration. For example, this field will only allow you to choose forms having names enting with "request", so "customer-support-request" and "consultation-request" will be included, but "newsletter-signup" and "call-to-action" won't. You can also choose how the form names will be presented in the select element. They can be shown as they are originally named, as spaced words, or as capitalized/spaced words. So, how does this field work? Form select fields store the ID of the form you select, but it also has a nice trick for working with forms in your markup too. FieldtypeFormSelect makes use of ProcessWire's built-in field rendering to keep things simple. Let's go to the code. <?php $page->select_a_form; // => Outputs the form ID, or null if no form has been selected // You could do this to output your form markup echo $forms->render($page->select_a_form); // Or you can do it this way. If a form has been selected the markup will be output to the page, if no form is selected, the output will be null. echo $page->render('select_a_form'); The fields you create will always be up-to-date with the forms that have been created in FormBuilder. This module also keeps things tidy- if a form is deleted that has been selected in one or more fields, on one or more pages, the values for those fields will be set to null so you won't experience any reference errors to form IDs that no longer exist. My primary use is to have a form select field available for blocks created in the RockPageBuilder module by @bernhard. I wanted each section on the page to contain an option to include a call to action button that can be a link to a page, a link to another URL, or that can open a modal with a call to action form to capture leads and visitor contact information. It's a great way to easily add flexibility and give some extra power to the end user when considering what they want visitors to do when browsing their website. RockPageBuilder is not required, but makes for a useful example! Protip for website designers- in my experience and empirical study of conversion analytics for sites that I've built, buttons located within sections of content on the page captured more leads and outperformed a call-to-action button in the page header, the call-to-action form at the bottom of the page, and forms located on a "Contact Us" page- by far. The true purpose of this field is to get the right forms in the right places quickly and easily without any need to work around markup output strategies or short codes. Contributions on Github are welcome if there's some extra functionality anyone wants to add that makes sense. Please let me know if you run into any bugs as well, when there's some extra usage and testing I'll submit it to the modules directory. Hope you find it useful! https://github.com/SkyLundy/FieldtypeFormSelect
    14 points
  8. Hi Everyone! So I've been working with PW for over ten years now!!! Big thanks to @ryan and everyone in the community (genuinely such a warm community). I've made a few modules in my time as well as tutorials and this was the first that I thought might work as a commercial module: MembersMessaging This module enables you to easily setup a messaging system for your users through your site. Allow your website users to message other users on the site given a user name or similar information. Module uses the notions of threads, messages and users to describe the message relationship -> A thread is a page storing n messages including: time posted, created by user and message text, the user ids in that thread, which users have unread messages in that thread, whether messages are encrypted (and it's salt). User pages have a list of all threads they are apart. In your templates you can add: a compose message form, threads and their messages, thread reply forms, message and threads counts, as well as delete and delete all messages. You can view messages in the admin (unless encrypted set to True) and view message stats and module usage in admin page Members. Purchase here How to install? - Install Processwire - Add MembersMessagingModule folder to modules folder in processwire: /site/modules/ - Login to your site admin and navigate to Modules: yoursite.com/[admin]/module - Configure the module settings: yoursite.com/[admin]/module/edit?name=MembersMessaging` - Follow the instructions below to add messaging to your templates How to use Example usage: $mm = $modules->getModule("MembersMessaging"); echo $mm->execute(); echo $mm->js(); echo $mm->css(); Full api here. Configuration Module allows you to configure whether: to allow new threads to yourself to allow new threads to guest user role to trash or unpublish threads deleted from frontend to use select or textinput for username input to allow an All keyword to signal thread should include all users to change all keyword to something else to notify a user via email they have been sent a new message to set email sender address to change username output from user name field to some other field specified to change default max threads and messages to display to encrypt messages (using basic encrypt strategy that encrypts each message on server before DB save, and is decrypted on request) Roadmap: Available here. ------- I'm not really sure how much interest there would be in this module so I've posted it to GumRoad for now, but will be looking to work on a PW store front if theres any interest in it and other modules - I've got ideas for other modules such as deffered page publishing, image folder GUI, protected field, field dependencies, pages contraits. I'm also available for hire currently to work on sites or modules https://www.benbyford.com
    13 points
  9. I was browsing the requests repo and saw a request from @adrian that reminded me of a PW feature I had forgotten existed: from Page List you can open a page for editing in a modal window by long-clicking on the Edit button. This is quite handy for when you want to make a quick edit to a page. But as the request notes, it would speed things up if there was a "Save + Close" button in the modal. Until the request is actioned in the core I thought I'd try implementing it in some custom code, and it's also an opportunity to show an easy way you can add custom JS and CSS to the ProcessWire admin. The first step is to create the following files at /site/templates/admin-assets/admin-custom.js and /site/templates/admin-assets/admin-custom.css. The CSS is optional. admin-custom.js $(document).ready(function() { const $body = $('body'); const is_modal = $body.hasClass('modal') || $body.hasClass('pw-iframe'); if($body.hasClass('ProcessPageEdit') && is_modal) { const params = new URLSearchParams(window.location.search); const $save_button = $('#submit_save'); if(params.has('quick_edit') && $save_button.length) { $save_button.parent().append('<button type="submit" name="submit_save" value="Save" class="ui-button uk-hidden" id="save-and-close">Save + Close</button>'); } } if($body.hasClass('ProcessPageList') && !is_modal) { $(document).on('ajaxComplete', function() { $('.PageListActionEdit a:not([data-autoclose])').each(function() { $(this).attr('data-autoclose', '#save-and-close').attr('href', $(this).attr('href') + '&quick_edit=1'); }); }); } }); admin-custom.css /* Avoid Tracy debugbar appearing on top of modal */ .ui-widget-overlay.ui-front { z-index:40000; } .ui-dialog { z-index:40001; } /* Place modal buttons on the left rather than the right */ .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float:none; } The second step is to add the following hook to /site/ready.php One good thing about using a hook to AdminTheme::getExtraMarkup to add custom CSS is that the file gets loaded after any CSS files that are loaded by modules or the PW core, so you can override any CSS rules without needing to add extra specificity. // Add custom JS and CSS to admin $wire->addHookAfter('AdminTheme::getExtraMarkup', function(HookEvent $event) { $parts = $event->return; $config = $event->wire()->config; // JS file should exist at /site/templates/admin-assets/admin-custom.js $js_url = $config->urls->templates . 'admin-assets/admin-custom.js'; $modified = filemtime(rtrim($config->paths->root, '/') . $js_url); $parts['head'] .= "<script type='text/javascript' src='$js_url?m=$modified'></script>"; // CSS file should exist at /site/templates/admin-assets/admin-custom.css $css_url = $config->urls->templates . 'admin-assets/admin-custom.css'; $modified = filemtime(rtrim($config->paths->root, '/') . $css_url); $parts['head'] .= "<link rel='stylesheet' href='$css_url?m=$modified'>"; $event->return = $parts; }); The end result:
    12 points
  10. This module won't suit everyone because... It requires what is currently the latest dev version of ProcessWire It requires your server environment have AVIF support Generating AVIF files is slow It offers fewer features than the core provides for WebP It is likely incompatible with the core WebP features so is an either/or prospect ...but it allows for the basic generation and serving of AVIF files until such time as the core provides AVIF features. Auto AVIF Automatically generates AVIF files when image variations are created. The AVIF image format usually provides better compression efficiency than JPG or WebP formats, in many cases producing image files that are significantly smaller in size while also having fewer visible compression artifacts. Requires ProcessWire v3.0.236 or newer. In order to generate AVIF files your environment must have a version of GD or Imagick that supports the AVIF format. If you are using ImageSizerEngineGD (the ProcessWire default) then this means you need PHP 8.1 or newer and an OS that has AVIF support. If you want to use Imagick to generate AVIF files then you must have the core ImageSizerEngineIMagick module installed. The module attempts to detect if your environment supports AVIF and warns you on the module config screen if it finds a problem. Delayed Image Variations Generating AVIF files can be very slow - much slower than creating an equivalent JPG or WebP file. If you want to use this module it's highly recommended that you also install the Delayed Image Variations module so that image variations are created one by one on request rather than all at once before a page renders. Otherwise it's likely that pages with more than a few images will timeout before the AVIF files can be generated. Configuration On the module configuration screen are settings for "Quality (1 – 100)" and "Speed (0 – 9)". These are parameters for the underlying GD and Imagick AVIF generation methods. There is also an option to create AVIF files for existing image variations instead of only new image variations. If you enable this option then all image variations on your site will be recreated the next time they are requested. As per the earlier note, the process of recreating the image variations and the AVIF files is likely to be slow. Usage Just install the module, choose the configuration settings you want, and make the additions to the .htaccess file in the site root described in the next section. How the AVIF files are served The module doesn't have all the features that the ProcessWire core provides for WebP files. It's much simpler and uses .htaccess to serve an AVIF file instead of the original variation file when the visitor's browser supports AVIF and an AVIF file named the same as the variation exists. This may not be compatible with the various approaches the core takes to serving WebP files so you'll want to choose to serve either AVIF files via this module or WebP files via the core but not both. Two additions to the .htaccess file in the site root are needed. 1. Immediately after the RewriteEngine On line: # AutoAvif RewriteCond %{HTTP_ACCEPT} image/avif RewriteCond %{QUERY_STRING} !original=1 RewriteCond %{DOCUMENT_ROOT}/$1.avif -f RewriteRule (.+)\.(jpe?g|png|gif)$ $1.avif [T=image/avif,E=REQUEST_image,L] 2. After the last line: # AutoAvif <IfModule mod_headers.c> Header append Vary Accept env=REQUEST_image </IfModule> <IfModule mod_mime.c> AddType image/avif .avif </IfModule> Opting out of AVIF generation for specific images If you want to prevent an AVIF file being generated and served for a particular image you can hook AutoAvif::allowAvif and set the event return to false. AutoAvif generates an AVIF file when an image variation is being created so the hookable method receives some arguments relating to the resizing of the requested variation. Example: $wire->addHookAfter('AutoAvif::allowAvif', function(HookEvent $event) { $pageimage = $event->arguments(0); // The Pageimage that is being resized $width = $event->arguments(1); // The requested width of the variation $height = $event->arguments(2); // The requested height of the variation $options = $event->arguments(3); // The array of ImageSizer options supplied // You can check things like $pageimage->field, $pageimage->page and $pageimage->ext here... // Don't create an AVIF file if the file extension is PNG if($pageimage->ext === 'png') $event->return = false; }); Deleting an AVIF file If you delete a variation via the "Variations > Delete Checked" option for an image in an Images field then any corresponding AVIF file is also deleted. And if you delete an image then any AVIF files for that image are also deleted. Deleting all AVIF files If needed you can execute this code snippet to delete all AVIF files sitewide. $iterator = new \DirectoryIterator($config->paths->files); foreach($iterator as $dir) { if($dir->isDot() || !$dir->isDir()) continue; $sub_iterator = new \DirectoryIterator($dir->getPathname()); foreach($sub_iterator as $file) { if($file->isDot() || !$file->isFile()) continue; if($file->getExtension() === 'avif') { unlink($file->getPathname()); echo 'Deleted: ' . $file->getFilename() . '<br>'; } } } Saving an original variation file Because requests to images are being rewritten to matching AVIF files where they exist, if you try to save example.500x500.jpg from your browser you will actually save example.500x500.avif. You can prevent the rewrite and load/save the original variation file by adding "original=1" to the query string in the image URL, e.g. example.500x500.jpg?original=1. https://github.com/Toutouwai/AutoAvif https://processwire.com/modules/auto-avif/
    11 points
  11. Hi all, I'm happy to share with you this website we launched two weeks ago for a Swiss movie in pre-production phase. The client wanted to have a blog to document the project and allow users to contribute posts through a form but also with a login to the admin which — once approved — gives them access to more options for their contribution’s content. Right at the beginning they came to us with scientific visualizations as references and thus I finally got to try D3.js. I’ve known about it for a very long time but never got a chance to try it out. It wasn’t easy but in the end I managed to get (what I think is) a pretty satisfying result. And here is a sample of the modules we used: - Fluency ⭐ - TracyDebugger - FormBuilder - RepeaterMatrix - AdminRestrictBranch - FieldtypeOembed - PageRenameOptions - MarkupComponents - Login Magic Link: this is a new one allowing to login with a link sent to the user’s email (inspired by this thread). It’s almost ready to share but I want to create a Process to have an overview of the generated links first Enjoy!
    11 points
  12. Hello ProcessWire community, it's been a while since I last shared a project showcase with you all 🙂 Today, I'm excited to present a recent project we've been working on: The website of the Austrian artist Tanja Boukal - www.boukal.at This project has been an interesting journey, and I'm excited to highlight some of the features and solutions we implemented: First off, I had to make the project run on my local development computer. That was quick and easy thanks to DDEV, where you can easily define the setup in a simple yaml file (eg php7.4, mariadb 10.2, etc) and then update the setup to a current one and see what breaks and then apply all updates 😄 Then, we tackled the challenge of cleaning up everything from the old ProcessWire website (not done by me). The page was quite a mess. I'm not blaming anybody for that, but I guess we all know the problem: The developer has some structure in his/her had and it works great at the beginning. But then the real world kicks in and slightly different needs pop up here and there and quickly the initially planned structure is not sufficient any more. We need a gallery on a page we didn't plan upfront, or we need some additional text above or below some other elements where we don't have Inputfields... So the client ended up creating several pages on the root level to be able to input the desired content and then link wildly to those hidden pages. Actually I think she did a great job, because she got things done without needing help from a developer (which costs money as we all know). During that process and thanks to RockPageBuilder we got rid of many unnecessary templates while providing the client with a lot more flexibility than before 😎 Before the relaunch: After the relaunch (with AdminStyleRock for styling the backend in the client's colors): Not only was the content on the old PW site structured completely different than on the new one, we also had two WordPress blogs that had been around that we wanted to integrate into the new website. Both RockShell and RockFrontend's DOM-Tools where extremely helpful in that process! We even used @FireWire great Fluency module to translate imported blog posts on the fly! This command is simply put into /site/modules/Site/RockShell/Commands/ImportAegean.php and will then instantly be available to RockShell as import:aegean command 😎 And then you can run "rockshell import:aegean", watch it do its work and enjoy 😎 Another pain for the client was that many people in the arts industry still rely on printed information. So she wanted to provide all the information about her work not only on her website but also as downloadable PDFs. On the old website this process was all done manually and whenever she had a new work/catalogue/project to share she had several things to update. Now she only updates that information on one place and RockPdf creates an updated PDF for her - with all entries sorted automatically by date 🚀 As mentioned RockPageBuilder adds a lot of flexibility to the website and makes editing content easier than ever before: But that's not a one-way-road! Where necessary we can still provide a more rigid structure and add custom fields that show up at dedicated places not movable by the client - for example date, cover-picture and teaser-text that should show up on all blog pages at the very top and at the exact same place: After that identical header section the client is free to choose from all available content elements like regular text, downloads or youtube videos (fully gdpr compliant without the client thinking about that). The work section showcases her artworks, projects, exhibitions and catalogues. All are linked to each other with the great ConnectPageFields module. So for example the https://www.boukal.at/work/projects/the-aegean-project/ has several other pages connected and also has its own blog! Ah, every aspect of the website is multilingual, which is also cool and where ProcessWire shines once more - especially with one-click-translations thx to Fluency! Another nice feature is that the page shows indicators for external links: This is a CSS-only solution and quite easy to implement (using LESS syntax): // style external links with icon body > *:not(#tracy-debug):not(.no-icon) { a[href^="http://"]:not([href*="www.boukal.at"]):not(.no-icon):after, a[href^="https://"]:not([href*="www.boukal.at"]):not(.no-icon)::after { content: url("/site/templates/images/external-link.svg"); display: inline-block; position: relative; top: 3px; margin-left: 5px; } } The site has top-notch performance thanks to the brilliant ProCache module and we did do some basic lighthouse optimisations! Hosting is done by me as well and for quality assurance we are monitoring all services with uptime kuma including a monthly report built again with RockPdf 🙂 I find it quite funny that these 6 spikes show loading times of around one second - that's less than the loading time of an average website! All other checks finished within < 100ms (from the same data center). The spikes happen when content is updated and ProCache has to rebuild the static copy of the homepage showing how much of a difference this treasure makes thx to Ryan 🙂 Site statistics are collected using Matomo to provide a great user experience without an annoying cookie banner. Consent for Youtube videos is requested on demand when a video is clicked on. Last but not least all the code is under version control and changes are pushed to the live server simply by doing a git commit (using RockMigrations deployment tools): So once the client requests a change and I'm done with the update I simply do a "git push" and GitHub does the rest and two minutes later the changes are live 😎🧘‍♂️ I hope you enjoyed reading and I hope you like the site as much as we do 🙂 I'm happy to hear what you think and if you find something to improve please let us know! 🤓 PS: If you like what you see and want to push your next project to the next level I'm happy to do consulting on an hourly basis so that you can efficiently pull my 10 years of ProcessWire knowledge into your work 🚀 Let's meet at cal.baumrock.com - always happy to see real faces instead of avatars 🙂
    11 points
  13. Hi everyone! I built this module trying to solve the following issue. Most of the time I use Repeater Matrix types with a few fields wrapped in a fieldset that are for configuring the behaviour/rendering of a specific repeater type, and are not really content related so I had always wanted to have them kind of hidden, but with a small preview of that the options are set (which I've yet to do). https://github.com/elabx/FieldtypeFieldsetPanel
    11 points
  14. Thanks for releasing this Ryan. Although I've been using ProcessWire daily for over a decade at this point, it's nice to compare my version of "The ProcessWire Way" with that of the creator himself and learn any tips and tricks along the way. This is probably going to be very helpful for beginners. When I was researching Tailwind a while ago, the creator (Adam Wathan) spoke about how he made many demo videos of himself replicating websites with his CSS framework so developers could get an idea of how the creator of the tool himself would approach using it. I feel like these site profiles provide a similar and more realistic learning experience in addition to ProcessWire's great documentation.
    9 points
  15. I received so much help from kind people on this forum; it’s now my turn to contribute. 😊 I recently had a need for a customer. They have a long list of articles, and they wanted to find any article corresponding to some criteria. They could use the Pages > find tool, which use pageLister, but then they would have to each time select some filters, which can be quite intimidating when you have many fields. So my idea was to create an admin page with the right filters already set. The customer only needs to use some of them. As I found out, this is much simpler than I first thought. So my tutorial is not rocket science, but my hope is that it helps newbies like me to achieve this... 😊 So first, you need to create a simple custom admin page. Bernhard already made a nice tutorial about this, and it’s quite straightforward (like everything in PW ❤️). Here is the code to create this custom admin page in our situation (thanks to Bernhard) : namespace ProcessWire; class ProcessArticlesList extends Process { // Infos about your module and some settings public static function getModuleinfo() { return [ 'title' => 'Articles Admin Page', 'summary' => 'No need to be afraid of building custom admin pages. ;-)', 'author' => 'Your name goes here', 'version' => 1, // you can set permissions here 'permission' => 'meeting-view', 'permissions' => [ 'meeting-view' => 'View Meetings page', ], // page that you want created to execute this module 'page' => [ // your page will be online at /processwire/articles/ 'name' => 'articles', // page title for this admin-page 'title' => 'Articles', ], ]; } public function ___execute() { // the logic goes here } } So, with this code, you get a new module that will produce a blank page. You have now to create a ProcessArticlesList folder inside that file in the site/modules folder and put your new file inside. Then, activate your module. The ___execute() method will run on page load. It’s supposed to return a string which will be your page content. Now things get quite... simple! 😄 The idea is to run an instance of ProcessPageLister, with custom parameters. You can find all these parameters in /wire/modules/Process/ProcessPageLister/ProcessPageLister.module (there are comments that really help you) and you can also have a look at the API doc about ProcessPageLister. Here is an example that covered my needs : public function ___execute() { // this is to avoid error detection in your IDE /** @var \ProcessPageLister $lister */ // get the module, so that you can use it the way you want. $lister = $this->modules->get("ProcessPageLister"); // from here, the comments in the source file were self explanatory, so I found how to cover my needs // filter all pages with the "meeting" template. This filter is by default, won’t show // in the filter list and cannot be changed. $lister->set('initSelector', "template=meeting"); // Then I set 3 filters, left blank, so that the user has just to fill them – or leave them blank $lister->set('defaultSelector', "title%=, themes.title%=, text%="); // Disallow bookmark creation. In my use case, there was no sense for that. $lister->set('allowBookmarks', false); // let the table be full width $lister->set('responsiveTable', false); // use the custom order defined by the user by moving the pages in the page tree $lister->set('defaultSort', 'sort'); // and just show the configured filter on the page. return $lister->execute(); } As you can see, there are 7 lines of specific code to achieve that. How elegant! 🥰 Hope that it is helpful to someone somehow. Cheers Thomas
    9 points
  16. Fantastic @Robin S - I hadn't been paying attention to AVIF browser support for a while, but it looks like we are finally good to go: https://caniuse.com/avif Just a minor thing but I just added AVIF support reporting in Tracy:
    7 points
  17. Hi everyone. Padloper 009 is ready! The release has been delayed by issues with my site. It is currently offline as I work on upgrading it. I hit a number of issues which I am currently resolving. I am hoping this won't extend beyond two weeks. I request your patience please, thanks. Best.
    6 points
  18. @teppo Not very straightforward, but here's a function that would tell you whether or not a translation exists for the given phrase in the given file, in the current language. Maybe we need a dedicated core function for it? function isTranslated($text, $textdomain) { $translator = wire()->user->language->translator(); $translations = $translator->getTranslations($textdomain); $hash = $translator->getTextHash($text); return isset($translations[$hash]) ? $translations[$hash]['text'] : false; } Example $text1 = 'About Us'; $text2 = isTranslated($text1, '/site/templates/about-us.php'); if($text2 === false) { echo "Not translated: '$text1'"; } else { echo "Translation of '$text1' is '$text2'"; }
    5 points
  19. Just this week had to deal with the same thing on a site with 100+ translatable strings, many views reusing same translations, and three languages. Ended up splitting translations into a (static utility) class that has a string() method that returns the translation. So basically the same thing that Ryan has done here. Biggest difference is that since many of those strings in my case also exist in the admin as field labels, I added a fallback that first checks if a translation for current language exists, and then falls back to the field label if possible. I guess this confirms that I'm not doing anything too silly 😄 ... but on a loosely related note, if anyone has a good idea how to check if a string has been translated, I'd be happy to hear. I'm currently just comparing the source value to the return value and if it has not been changed I'll assume that a translation wasn't found. That's obviously a bit crude. Would be nice to have some way to check if the returned value from the translation method in core is indeed a translated version 🙂
    5 points
  20. Hi, I've created a very simple module, that displays the number of (PagesVersions) versions a page has in the Page List: https://github.com/eelke/ProcessPageListVersionsCounter I expect something like to become part of the PW core as the PagesVersions implementation matures, but in the meantime it could be useful to others. So I'm posting it here.
    5 points
  21. Congratulations to both of you! I wish you all the best as well. Small note: it seems the font you’re using has a (rather common) vertical metrics issue on Firefox leading to your text being cut by the overflow: hidden You can learn more / test your font here: https://vertical-metrics.netlify.app/ and then maybe get in touch to see if this can be fixed by the type foundry
    5 points
  22. As of today, this is now a native feature on the dev branch: https://github.com/processwire/processwire-requests/issues/520#issuecomment-1961794624
    5 points
  23. I have no idea how Tailwind works but when I need something like this I usually set a CSS variable and add them with the style attribute where needed. Something like: <?php namespace ProcessWire; ?> <div style="--color: <?= $page->color ?>"><!-- My content --></div> When I need something more complex, say a CSS animation depending on some of the page parameters (e.g. children count) then I output in a <style> element within my template.
    5 points
  24. So, today I learned that you can use a config-dev.php file which takes precedence over the normal config.php file if it's present. ... unfortunately I discovered this by accidentally uploading a config-dev file to a live site. I'd got two config files for live and staging in my project folder and just renamed them when I wanted to update a setting - I'd coincidently named the file for the staging details as config-dev.php and accidently uploaded it to production. Luckily this just meant that the content from the staging site got displayed which wasn't too out of date so hopefully no one noticed.... Now I look into it, I can see that's it been around for ever and there's been lots of chat about it, but hey, I didn't know about it, so thought I'd stick it in this thread just in case anyone else was a daft as me.
    4 points
  25. We just launched 2 new project in the past sooner this week. The first one is Overline Systems. The following modules were used : AdminStyleRock, Formbuilder, ProCache and the MarkupMenuBuilder. The second is a simple one page : De la Cave au Cellier
    4 points
  26. When searching a datetime field the PW PageFinder can use any string that the PHP strtotime() function understands. So the problem you're describing only relates to limitations on the input where you are putting your search value. InputfieldSelector uses the jQuery UI datepicker for the value input, and this has a constrainInput setting that's true by default. So if you set constrainInput to false you can enter free text into the input and it should just work. You have to set this option after the datepicker is initialised because PW doesn't provide any way of customising the datepicker options before initialisation (I've opened a request for that here: https://github.com/processwire/processwire-requests/issues/523). Here's how you might do it with some custom admin JS: $(document).on('focus', '.InputfieldSelector .InputfieldDatetimeDatepicker', function() { const $el = $(this); // Set constrainInput to false after a brief delay so that the datepicker has had time to initialise setTimeout(function() { if($el.datepicker('option', 'constrainInput')) { $el.datepicker('option', 'constrainInput', false); // Updating the option seems to cause the datepicker to hide so show it again $el.datepicker('show'); } }, 100); }); The result in Pages > Find as an example: Edit: you may know this already but you can add a custom row to InputfieldSelector where you can use any selector clause you like. So if you can trust your editors to get the field name right you could have a row like this: This is the better option if you want to save the value in a Lister bookmark for example, as it seems that InputfieldDatetime will convert a time string to the equivalent timestamp when it reloads so it then loses the "dynamic" aspect.
    4 points
  27. I'll chip in some thoughts here, and this is from a perspective of caring about you @titanium in this situation. Well... they're graphic designers, not web designers. I've lead a web team and trained graphic designers to become web designers because they are not remotely the same. I always take the route of asking questions- a lot of them- because I wouldn't be able to make an accurate recommendation without understanding their needs. So I'd ask about more specifics about their design, view a mockup if they have one, understand what they're asking for because it does sound to me like they're asking for the moon. It's a moment where people reveal their level of expertise, or lack thereof, in the first sentence of their request. Sounds like they want * { position: absolute; } This is why I always operate with this thought in mind: what a client asks for does not have intrinsic correlation with what they need, or even want to begin with. Maybe they actually could achieve what they want using sane tools like @bernhard's builder. I'm sure there are more specifics to your situation and I'm not sure of the relationship you have with this client, but I would be concerned that they're going to end up trying to do something that isn't possible, or is so remotely possible that it's not worth their time, or yours. It might be worthwhile to ask yourself: Is this client expecting something that can't be done that may get you stuck in a position where you can't deliver? You're the expert in this field. Wix, Squarespace, GoDaddy Website Builder (lol), etc. They all fundamentally build in blocks with all of the standard limitations of the web. If they want to be big kid web designers then the onus of finding a tool that lets them do what we do is their job, in my most humblest of opinions.
    4 points
  28. Obviously you should try and educate your client into the advantages of building in PW (and in the difficulties of designing a pixel perfect site that works across all browser and devices) but if they really insist on being able to fiddle then WebFlow is probably the kind of thing they want. Not a PW solution, but it does work well once you get the hang of it. We lost a client of 20 years to it last year 😞
    4 points
  29. Thank you @Stefanowitsch for mentioning RockPageBuilder! However, it appears that what they need might differ from what RockPageBuilder offers. @FireWire eloquently highlighted this distinction recently: So, RockPageBuilder is more about helping developers to build content elements quickly and easily. These blocks enable clients to populate their websites with content seamlessly, without the need to think too much about the technical and visual aspects (because this is our job as web professionals). It's less about designing and more about managing content, which is something I love about ProcessWire and where it really shines. In RockPageBuilder the design is done in code - CSS, Less, Tailwind, UIkit, Bootstrap. You can choose whatever you like best. If you want a no-code page designer there are plenty of options out there, but RockPageBuilder is likely not your first choice 🙂
    4 points
  30. @fruid Use custom page classes. In site/classes: <?php namespace ProcessWire; class UserPage extends User { public function getMessages(): PageArray { return wire()->pages->find('template=message, receiver=' . $this->id); } } Be sure to enable it in site/config.php: $config->usePageClasses = true;
    4 points
  31. Very interesting concept, and works nicely! ... but I have to admit that I spent a few minutes trying to figure out how to actually view any content. Clicked the labels, nothing came up, clicked other labels, lines came up but still nothing else, etc. Would've given up if I hadn't known from the screenshots here that there is more to see. I get it now (you have to click those unlabeled sections at the outermost layer of the wheel; assumed at first that they were just a visual thing since there was nothing on them) but you might want to consider giving those sections some kind of label as well. Or add a help text or something. Just a suggestion 🙂 Anyway, always great to see projects that dare to be different.
    4 points
  32. I'd like to introduce you to my newest employee: Devin
    3 points
  33. Hello @dotnetic, it fits exactly. 🙂 It's what I was looking for, just great, thank you very much for that! I found this post in the morning: How to remove breadcrumbs, modify headline? and would most probably have failed on it .. It feels good while you grow with a project and learn a lot of basic things and have the support of the great processwire- community. And thats motivating too. This is not a given, so thank you all!
    3 points
  34. The database was named by the developer, or even the hosing server. If you have access to the hosting control panel, you might be able to upload files via FTP. In that case you could upload a "test.php" file to the root directory of the site (this directory would contain the "wire" and "site" directories and the "index.php" file) with the following code in it: <?php include("index.php"); echo $pages->get(2)->httpUrl; Than you can open the url http://mywebsite.com/test.php and the answer will show up. I also attached the file so you can download it 👇 test.php
    3 points
  35. Thanks Ryan! We just take the vertical scroll position and convert it to translateX values on the rows, using requestAnimationFrame() to make it smooth. Well, PAGEGRID is doing it 🙂. There was a second <html> added by $config->prependTemplateFile. We forgot to remove it when we moved it from local to a new install in the server 🤣 Thanks for catching this! Thanks Romain! Thanks for catching the problem with the font. For now we added a max-height only for Firefox using @-moz-document url-prefix(). We'll see if there's another version of Neue Haas Grotesk that doesn't have this problem.
    3 points
  36. @diogo @jploch Wow that is awesome! It's completely different than anything I've seen before. It's really fun to scroll through too. I'm curious about the development side, how do you take over the scroll behavior in that way? If I view the source, there are two completely separate <html> documents in the output, how is that possible? 🙂
    3 points
  37. @Gideon So Sorry I posted the wrong link, meant this one: https://github.com/Toutouwai/CustomInputfieldDependencies @Tyssen You should be able to do something like this:
    3 points
  38. With Geffen Playhouse for example, which was launched in 2019 right before custom fields for files/images was introduced, we needed custom fields for images. At the time, the best approach was to use the RepeaterImages module, which uses repeaters and all the functionality that comes with it. This includes the ability enable/disable a repeater item. In a recent update, I wanted to remove that dependency and switch to just a normal images field with custom fields, but the client still wanted enable/disable capability on images, hence my approach to it described in this post. I think about it just like repeaters. There are times when you want a piece of data to exist but not be visible. Without being able to disable an image, you would have to delete it (or do some other weird hack like perhaps add an image tag), which is less than ideal. With enable/disable capability, it brings it more in line with how multi-item fields, like repeaters, work.
    3 points
  39. $d = strtotime('next monday +7 days'); echo date('Y-m-d', $d); Seems to work for me.
    3 points
  40. Thx Robin! While the Save + Close button is there (as expected), the js part makes the edit links all open home page, eg. they all have the link /processwire/page/edit/?id=1&quick_edit=1 I fixed that with: const $links = $('.PageListActionEdit a:not([data-autoclose])'); $links.each(function() { $link = $(this); $link.attr('data-autoclose', '#save-and-close').attr('href', $link.attr('href') + '&quick_edit=1'); }); Just to mention that using uk-hidden class (as a "belt and suspender" approach) would only work in AdminThemeUIkit, leaving out newer (and older) admin themes.
    3 points
  41. A module for generating and serving AVIF files:
    3 points
  42. I should give you guys an early access to our websites in the Beer Garden lol Thank you though for the valuable feedbacks, it’s the kind of things you can have a hard time to see once you’re deep into projects with unconventional design: you get used to it and think it’s “understandable” enough. I went ahead and made the grey text darker and I’ve displayed the title in the center when you hover the outer strands. I also increased the contrast when hovering these. I hope this will do. Please don’t look at my code 🫥
    3 points
  43. Interesting concept. I'm with Bernhard when he suggested making the help text fixed in the middle. Half of the time the instructions are upside down (being in the bottom half of the screen) and moving - so hard to read unless you wait for it to make it back up. Could also do with some more contrast for those of us with slightly compromised sight. Thanks for sharing though.
    3 points
  44. Same experience as Teppo for me. Without his comment I would have not made it to show any content at all. For example when I click on "Full Spectrum" this happens: I really didn't get that I now have to click on one of the outer rings... Maybe you could show a message in these cases in the middle of the circle - something like "Click one of the outer rings to show content" ? Or Just show the first outer circle by default? Hats off for mastering D3.js 😅
    3 points
  45. View at: https://okeowoaderemi.com I switched to Processwire 10 years ago and it's being my goto framework for maintaining my website and also developing for clients. Development I used the moduleTemplateEngineFactory and TemplateEngineTwig, Twig has always been my favourite templating engine, because of how easy it is, to use reusable code and have an easy way to inject content into the layout, it's almost like MasterPages in .NET (For the Oldies)
    3 points
  46. Dear ProcessWire community, Over the last years I created many small–medium websites for classic musicians among other things. I have grown to a point where I cannot support all projects in a timely manner, so I look for some support on code level. (Some websites are oldschool PHP templates, some are headless with a Svelte or Next.js frontend.) Tasks may involved Implementing new screens and features (designs provided by me) Refactoring oldish static fields to more flexible repeater layout Maintenance (Updating ProcessWire, PHP Versions) Requirements Experience with ProcessWire, HTML, Vanilla JS, jQuery, CSS, Sass, node.js build tools, Git, SFTP Ability to estimate tasks, plan and deliver on time Billed by the hour based on estimates Nice to have, but not required Experience with custom ProcessWire Modules, Docker, Svelte, React, Next.js, Typescript, THREE.js, R3F, GDPR complience Eye for design details Germany based / German speaking Please send me a DM if you are interested so we can discuss details.
    2 points
  47. The "1 to 10 of 20" part is something you get from PaginatedArray::getPaginationString() rather than from MarkupPagerNav. So if your paginated results are in a variable named $results you would do something like this: <p>Showing <?= $results->getPaginationString() ?> results</p> And the Previous/Next navigation: unfortunately MarkupPagerNav doesn't provide a direct solution for this. There's an open request for it: https://github.com/processwire/processwire-requests/issues/248 So for now you have to hide the numbered links with CSS (the easy option) or code your own Previous/Next links.
    2 points
  48. Like last week, this week the focus has been on adding feature requests from our processwire-requests repository. Though I'd like to give it another week before bumping up the version number. Rather than repeating all that was added here, please see the dev branch commit log, which covers them all, several with more detailed notes in the commits. The biggest added feature request was likely the API updates for getting/setting multi-language values, but there are several others as well. I was excited to see the new jQuery 4.0.0 release this week, which we'll no doubt be upgrading soon (or once out of beta). Here's a quote from the intro of their new post: Some parts of ProcessWire's API were originally inspired by jQuery. It's always nice to see progress there with new versions, especially a new major version. Thanks for reading and have a great weekend!
    2 points
  49. I eventually tracked down the issue, it related to a historical version of proDrafts. Once draft was unticked front-end editing worked 🙈.
    2 points
  50. Maybe there is something for this already in the API, or elsewhere, but I couldn't find it, so here is my suggestion. The problem is that you can't simply copy the repeater using the API in the usual way: $page2->repeater = $page1->repeater; appears to work for page 2 but messes up page1 where the repeaters get duplicated. Instead you have to copy over each of the field values within each repeater item. So I wrote this. It seems to work, at least in my context. I placed it in init.php. (However, it would be better if the core handled this more intuitively 😁) /** * Copies a repeater field to another page * Operates on the RepeaterPageArray object e.g. $page->repeater->copyTo($page2) * * @param HookEvent $event * */ wire()->addHook('RepeaterPageArray::copyTo', function ($event) { $repeater = $event->object; $repeaterField = $repeater->getForField(); $page = $event->arguments(0); if(!$page->hasField($repeaterField)) { $event->error("Field $repeaterField does not exist on page"); return; } $page->of(false); foreach ($repeater as $item) { $newItem = $page->$repeaterField->getNew(); foreach($item->getFields() as $f) { $newItem->$f = $item->$f; } $newItem->save(); $page->$repeaterField->add($newItem); } $page->save(); });
    2 points
×
×
  • Create New...