Jump to content

Robin S

Members
  • Posts

    4,791
  • Joined

  • Days Won

    303

Everything posted by Robin S

  1. A module for generating and serving AVIF files:
  2. 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/
  3. It's working reliably on a few sites I've tested on but what you're saying makes sense. So you could add the button with PHP in a hook and not in the JS as demonstrated below. I also had a thought that Page Edit might be opened in a modal in some other circumstances (I think ListerPro has an option for this) where the "Save + Close" button wouldn't work or wouldn't be wanted. So I've updated the code in the first post and in the code below so that a "quick_edit" URL parameter is added for identification purposes. admin-custom.js $(document).ready(function() { const $body = $('body'); const is_modal = $body.hasClass('modal') || $body.hasClass('pw-iframe'); 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'); }); }); } }); Additional hook for /site/ready.php $wire->addHookAfter('ProcessPageEdit::buildForm', function(HookEvent $event) { /* @var InputfieldWrapper $form */ $form = $event->return; // Return if Page Edit is not in a modal window if(!$event->wire()->config->modal) return; // Return if quick_edit GET parameter is not present if(!$this->wire()->input->get('quick_edit')) return; $save_button = $form->getChildByName('submit_save'); if(!$save_button) return; /** @var InputfieldSubmit $f */ $f = $event->wire()->modules->get('InputfieldSubmit'); $f->id = 'save-and-close'; $f->name = 'submit_save'; $f->value = 'Save'; $f->text = 'Save + Close'; $f->addClass('uk-hidden', 'wrapClass'); $form->insertAfter($f, $save_button); });
  4. Yes, it does show, but only in the modal and not in Page Edit itself, which is the intention. The way that modal buttons work is that the modal script looks for buttons that are actually within the page that is being displayed in the modal iframe and then it constructs new buttons for the bottom of the modal. The newly constructed buttons don't carry over things like the uk-hidden class so they are not hidden. But because the button I'm adding is actually within the Page Edit interface (right next to the normal Save button) I never want that button to be shown in normal page editing because "close" wouldn't make sense there. The check for is_modal should also prevent the Save + Close button being displayed when it isn't wanted so the uk-hidden is really just optional but it's a sort of "belt and suspenders" approach just to be on the safe side. 🙂
  5. 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:
  6. The issue is fixed now in a commit on the dev branch: https://github.com/processwire/processwire/commit/3e90cb74faa46b63ff9b5920d1c4e5b971aac591
  7. A module that adds support for finding and sorting pages by depth in a PageFinder selector:
  8. This module fulfills a need that's perhaps not common but there have been some requests for it in the past. There's an open request in the requests repo so the features might get added to the core at some point but for now here's a module for anyone who needs it. PageFinder Depth Adds the ability to find and sort pages by the depth of the page relative to the home page. The module requires that the core PagePaths module is installed. Depth of a page in this case means the same thing as "number of parents", so a page that is directly under the home page has a depth of 1, and a child of that page has a depth of 2, and so on. If you already have a Page object you can get its depth with $page->numParents() but the result of this isn't searchable in a PageFinder selector. Installing this module allows you to use selectors like this: $items = $pages->find("depth=2"); $items = $pages->find("template=basic-page, depth>1, depth<4"); $items = $pages->find("template=product, sort=depth"); The keyword "depth" is configurable in the module settings so you can change it to something different if you already have a field named "depth" that the default keyword would clash with. Limitations OR-group selectors are not supported for depth. Searching by depth in an existing PageArray This module only adds features for PageFinder selectors and doesn't automatically add any depth property to Page objects in memory, but you can search within a PageArray using numParents to achieve the same thing, e.g. $items = $my_pagearray->find("template=basic-page, numParents>1, numParents<4"); https://github.com/Toutouwai/PageFinderDepth
  9. I find that the "number" input type is an enhancement for fields that are only to contain whole numbers. It lets you use the spinner arrows at the right of the input or your keyboard arrow keys to increment the number up and down, and it avoids invalid decimal or text values from being entered into the field. The PW admin uses number inputs for InputfieldInteger in some places but these are not used consistently and I regularly find myself trying and failing to increment values in an integer field that isn't using input type number. For example, in the "Rows" setting of a textarea field. With a simple hook in /site/ready.php you can ensure that all integer fields use input type number: $wire->addHookAfter('InputfieldInteger::renderReadyHook', function(HookEvent $event) { $event->return->type = 'number'; });
  10. Version 0.3.11 is released, which adds an option to apply reduced opacity and strikethrough text styling to hidden and unpublished pages in the breadcrumb menu itself (this styling is always applied within the breadcrumb dropdowns).
  11. If I was the vendor of a product that currently has 77% market penetration I think I would keep going on. It is the most popular JavaScript library by a staggering margin: With so many happy customers it's fantastic news that jQuery continues to be updated and supported, and I don't see how its use in the PW core disadvantages users in any way.
  12. Yeah, I've noticed that too and I think it's a bug. I normally select the "except when superuser" option and it does work then but this shouldn't be necessary. I opened an issue here: https://github.com/processwire/processwire-issues/issues/1879
  13. I could probably add an option to support this but I can see it introducing worse issues than it solves. It would make it impossible to save any attribute value where that value has been deliberately set at a value that also happens to be the default. What if the user wants to create a tag where the values should be [[mortgage_calculator default_amount="70000" default_rate="4" default_type="repayment" default_term="25"]]? If all of those attributes get stripped out and you later change the defaults then the tag produces an unintended result. I think the scenario where you have attributes whose default value you intend to later change is a fairly rare one, and in that scenario you'd be better to not configure a default within the Hanna tag but rather use some other means (e.g. inputfield description or notes text in the dialog) to communicate to the user that there's a fallback value that's used when no value is specified and this fallback is subject to change. That way you can distinguish between when a user has chosen a value or not. But if you still want the feature and can live with the side-effects then let me know and I'll look at adding it.
  14. If you like that approach you might like to try the Repeater Depth Helper module which is a development from that earlier idea. It gives you the depth structure for the whole repeater field from one method and also enforces a couple of depth rules to keep things sane.
  15. Page Edit Per User takes the approach of first checking if PW says the user can edit a page, and if the answer is "yes" it returns early and doesn't apply any logic after that. So it only ever adds edit access and never removes it. For your scenario I think you'll need to approach it differently and give the role the necessary permissions to edit, create and add children for all pages having the templates in question. Then in your hook remove the edit/add permission for individual pages according to your test for when the user is not allowed to edit or add children. You do that in hooks to Page::editable and Page:addable by setting the event return to false. And in contrast to Page Edit Per User you'll only need to apply your hooks to users with a particular role. So your init method might look like this: public function init() { if($this->wire()->user->hasRole('limited')) { $this->addHookAfter('Page::editable', $this, 'hookPageEditable'); $this->addHookAfter('Page::addable', $this, 'hookPageAddable'); } }
  16. Robin S

    CVE-2023-24676

    Yes, exactly. Everything in the vulnerability description after "we require a user account with administrative privileges and the application must have debug mode activated" seems redundant to me. At that point it's game over. Rather than following all the subsequent steps you could simply install Tracy Debugger and start executing arbitrary PHP code in the Console panel. It's up to every superuser to ensure that malicious users cannot get an account with administrator privileges so I don't understand how this vulnerability description adds anything beyond that basic point.
  17. Right, makes sense. You could change to a CKEditor field as this keeps full absolute URLs if you paste them in. But I actually think this is a good use case for a hook like you are already using, or a custom textformatter module. That way you can use all the normal features of the pwlink plugin and the string replacement is dead simple.
  18. @Roadwolf, HTML Purifier doesn't include irc:// in its allowed URI schemes. PW does provide some ability to hook into the HTML Purifier configuration via MarkupHTMLPurifier::initConfig but in the case of adding a scheme this seems difficult to achieve. The MarkupHTMLPurifier::initConfig method is only called when there is no cached configuration for HTML Purifier and yet the cached config doesn't seem to include the relevant "URI.AllowedSchemes" setting. So you'd need to be continuously clearing the cached config. Plus you would also need to include a custom class to validate the irc:// scheme so it's available to HTML Purifier. All in all it would be a lot easier just to use a Hanna code for IRC links when you need them, e.g. [[irc_link uri="irc://irc.efnet.org/mIRC" text="My link text"]]. In the Hanna code you would use the uri and text attributes to output the link.
  19. Using full absolute URLs for internal website links is liable to cause trouble down the line. For example, if the website domain changes you have to update every internal link. Instead you could add a <base> element to your _main.php.
  20. Thanks. Maybe that would be been a viable alternative if I had investigated it at the start, although I think there would be a lot of challenges (for example, MarkupAdminDataTable doesn't provide the necessary hooks). But I've been using this module privately for some years now and I think it does the job just fine so I don't want to reinvent it all now.
  21. I received a suggestion from @monollonom that it would make more sense if the module appeared under the Access menu rather than the Setup menu. I agree so I've changed to this in v0.1.5. If you've installed < v0.1.5 and you'd like to have the module under Access then there a couple of ways to do this. First update to v0.1.5 and then either... Uninstall the module, and then reinstall the module so the page will be automatically created under Access during install. Or move the "Template Access" page within the Admin branch from under Setup to be under Access.
  22. Template Access A Process module that provides an editable overview of roles that can access each template. The module makes it quick and easy to see which templates have access control enabled (personally I like to ensure I've enabled it for every template) and to set the appropriate access per template. Usage The Template Access page under the Access menu shows access information for all non-system templates in a table. You can filter the table rows by template name if needed. Click an icon in the table to toggle its state. The changes are applied once you click the "Save" button. Sometimes icons cannot be toggled because of logical rules described below. When an icon cannot be toggled the cursor changes to "not-allowed" when the icon is hovered and if the icon is clicked an explanatory alert appears. A role must have edit access before it can be granted create access. If the guest role has view access for a template then all roles have view access for that template. https://github.com/Toutouwai/ProcessTemplateAccess https://processwire.com/modules/process-template-access/
  23. Thanks for the report, should be fixed in v0.1.4.
  24. Glad you like it! That would really be a different purpose than what this module is for. So that idea would be for a different module rather than a feature of this module. Although it wouldn't work exactly as you describe, as it happens I have another module that's 90% finished and just waiting for a core issue to be resolved. This module is designed to make it easy to copy or move images from one field to another, so it could be used together with Media Lister to copy an image, just with one or two more steps. I'll come back and explain more once the module is released. But as a general thing, are you sure you want to be duplicating image files across the site like this? If you have images that need to be regularly reused on different pages you could look at an "image reference" solution instead, so the image file stays in a single place and is referenced on other pages. My Select Images module provides for a basic solution, but if you need more power you could look at a "page per image" approach which is used in modules such as Media Manager and Visual Page Selector by @kongondo
×
×
  • Create New...