Jump to content

eelkenet

Members
  • Posts

    111
  • Joined

  • Last visited

  • Days Won

    3

Everything posted by eelkenet

  1. After enabling WebP support you may notice it can take a long time for ProcessWire to create WebP copies of all images and their variations. For instance, on a site I work on (with over 10k images), it was taking about 1 second per image, ie. more than 3 hours in total.. ? If you are comfortable around the command-line, you can use the cwebp program and this bash script to speed things up drastically. I have built upon this script and got things to work in combination with xargs and find, making it rather powerful for PW's purposes. 1. Save this script as 'convert-webp.sh' (or download the Gist from Github), and follow instructions ######################################################################################################### # # Fast Recursive Images to WebP converter # Customized for ProcessWire CMS/CMF <https://www.processwire.com> # # Author: Eelke Feenstra <dev@eelke.net> # Version: 001 # Based upon: https://github.com/onadrog/bash-webp-converter # # Quick & dirty script to add webp versions to all PNG / JPG / JPEG files inside your PW assets folder # # 1. Set this script to executable: # $ chmod +x convert-webp.sh # # 2. Check if cwebp is installed: # $ cwebp -version # If it is not, install: # $ brew install webp # Or follow instructions https://developers.google.com/speed/webp/download # and change $executable to cwebp's full path # # 3. Run the script directly on a folder: # $ ./convert-webp.sh /path/to/your/folder # ######################################################################################################### # Configuration executable="cwebp" # update this to reflect your installation! quality=90 # change to desired WebP quality ######################################################################################################### converted=0 skipped=0 echo "Entering $1" for file in $1/* do name="${file%.*}" echo "FILE: $file" echo "NAME: $name" # Skip the folder itself.. if [ "$name" = "./." ]; then echo "SKIP: $name" continue; fi if [[ $(file --mime-type -b $name.webp) == image/webp ]]; then echo "FOUND: $name.webp, skipping.." skipped=$((skipped+1)) elif [[ $(file --mime-type -b $file) == image/*g ]]; then echo "NOT FOUND: $name.webp" newfile(){ echo "$file" | sed -r 's/(\.[a-z0-9]*$)/.webp/' } $executable -q $quality "$file" -short -o "$(newfile)" converted=$((converted+1)) fi done echo "Converted $converted, Skipped $skipped" 2. Run to create webp versions of all jpg/jpeg/png images in a given folder (for example the site's homepage) $ ./convert-webp.sh /path/to/processwire/site/assets/files/1 3. And in order to create WebP copies of ALL images inside the /site/assets/files folder, you can run this script. It searches for all directories inside the files directory, and passes these to the convert-webp.sh script using xargs. $ find /path/to/processwire/site/assets/files -maxdepth 2 -type d | xargs -I '{}' ./convert-webp.sh '{}' Tested both on MacOS 12 and Debian
  2. Update: The solution is to upgrade (paid) to MAMP PRO 6, this version comes with WebP support. The upgrade costs a lot less than all the time I've spent chasing this bug.. ? Edit: it took really long to convert all images, and I figured out a faster way using the command line. Read more in the tutorial.
  3. @horst Oh my, I'm sorry for never replying to you! I think I have finally found the origin of my and @AndZyk's situation: MAMP PRO on MacOS (5.7) simply does not have WebP support enabled. I still don't know why this creates all the new copies, because it seems that all moving parts seem to be aware that webp is in fact not enabled. With this basic check: <?php namespace ProcessWire; include "index.php"; error_reporting(E_ALL); ?> <pre> <h3>Check directly using gd_info()</h3> <?php $gd_info = gd_info(); $webpSupport = isset($gd_info['WebP Support']) ? $gd_info['WebP Support'] : false; print_r($gd_info); echo $webpSupport ? "\nWebP is supported" : "\nWebP is NOT supported"; ?> <hr> <h3>ImageSizerEngineIMagick</h3> <?php $imagick = $modules->get("ImageSizerEngineIMagick"); $supported = $imagick->supported("webp"); echo $supported ? "WebP is supported" : "WebP is NOT supported"; echo "\nFormats: " . print_r($imagick->getSupportedFormats(), true); ?> <hr> <h3>ImageSizerEngineGD</h3> <?php $gd = new ImageSizerEngineGD(); $supported = $gd->supported("webp"); echo ($supported ? "WebP is supported" : "WebP is NOT supported"); echo "\nFormats: " . print_r($gd->getSupportedFormats(), true); ?> </pre> I get these results on my MAMP PRO machine: Check directly using gd_info() Array ( [GD Version] => bundled (2.1.0 compatible) [FreeType Support] => 1 [FreeType Linkage] => with freetype [GIF Read Support] => 1 [GIF Create Support] => 1 [JPEG Support] => 1 [PNG Support] => 1 [WBMP Support] => 1 [XPM Support] => [XBM Support] => 1 [WebP Support] => [BMP Support] => 1 [TGA Read Support] => 1 [JIS-mapped Japanese Font Support] => ) WebP is NOT supported ImageSizerEngineIMagick WebP is NOT supported Formats: Array ( [source] => Array ( [0] => JPG [1] => JPEG [2] => PNG24 [3] => PNG [4] => GIF [5] => GIF87 ) [target] => Array ( [0] => JPG [1] => JPEG [2] => PNG24 [3] => PNG [4] => GIF [5] => GIF87 ) ) ImageSizerEngineGD WebP is NOT supported Formats: Array ( [source] => Array ( [0] => JPG [1] => JPEG [2] => PNG [3] => GIF ) [target] => Array ( [0] => JPG [1] => JPEG [2] => PNG [3] => GIF ) ) And these on the online host: Check directly using gd_info() Array ( [GD Version] => 2.2.5 [FreeType Support] => 1 [FreeType Linkage] => with freetype [GIF Read Support] => 1 [GIF Create Support] => 1 [JPEG Support] => 1 [PNG Support] => 1 [WBMP Support] => 1 [XPM Support] => 1 [XBM Support] => 1 [WebP Support] => 1 [BMP Support] => 1 [TGA Read Support] => 1 [JIS-mapped Japanese Font Support] => ) WebP is supported ImageSizerEngineIMagick WebP is supported Formats: Array ( [source] => Array ( [0] => JPG [1] => JPEG [2] => PNG24 [3] => PNG [4] => GIF [5] => GIF87 ) [target] => Array ( [0] => JPG [1] => JPEG [2] => PNG24 [3] => PNG [4] => GIF [5] => GIF87 [6] => WEBP ) ) ImageSizerEngineGD WebP is supported Formats: Array ( [source] => Array ( [0] => JPG [1] => JPEG [2] => PNG [3] => GIF ) [target] => Array ( [0] => JPG [1] => JPEG [2] => PNG [3] => GIF [4] => WEBP ) ) I have reached out to the MAMP folks for info on how I could go about enabling it here. The only related issues on StackOverflow etc are pointing in the wrong direction it seems (homebrew etc).
  4. Hi @Pete, could the minimum character count for the search on the forum please be reduced to 3 instead of (what I suspect) 4? It's really impractical to not be able to search for words like 'api', or 'get', while you know for sure that there must be many results for those words.
  5. Note: if you want to force a download but don't want to change the downloaded filename, you should omit the value of the download attribute like so: <a href="yourfile.pdf" download>link</a>
  6. Hi @joshua, that works - sort of. When setting the user language inside a template we still only get the default language, as your hook gets called earlier. However, what does work is hooking into the same Page::render method with a lower priority number, so our code gets an earlier execution. Great! In ready.php we now have: <?php namespace ProcessWire; // Hook into Page::render, just before PrivacyWire's hook // Which uses the default priority of 100 wire()->addHookBefore('Page::render', function (HookEvent $evt) { // Only apply to front-end pages $page = $evt->object; if ( $page->template == 'admin' || // exclude admin pages $page->template == 'form-builder' // exclude from form-builder iframe ) { return; } $user = wire()->user; $languages = wire()->languages; // Get and sanitize the ?lang= get param $lang = strtolower(wire()->input->get("lang", "text")); // Set appropriate language if ($lang === "en") { $user->language = $languages->get("default"); } else { $user->language = $languages->get("de"); } }, ['priority' => 99]);
  7. Hi, I've got a site that uses multi-language URLs based on a query-param, ie. https://example.com/some/page?lang=de. The site exists of a Vue.js app on top of a very basic HTML skeleton generated by PW, and we don't use the LanguageSupportPageNames module at all (so no example.com/de & example.com/en ). In order to set the initial skeleton HTML (such as page title etc), which gets rendered before the Vue app has booted, I set my $user->language based on the $input->get('lang') value. After the Vue app has booted, it takes over control and loads the remaining data in the appropriate language through a multilingual API endpoint. Next to the Vue app, we also use PrivacyWire. However, due to the way PrivacyWire has been built, it looks at the $users language BEFORE any of this takes place (in its ready() function). As a result, I can't get the correct language content inside the PrivacyWire popups, it always shows the default language content instead. So probably I need to hook into the getPrivacyWireConfigObject method, and overrule the PrivacyWire::lang variable with the language that I get from my $input->get('lang')? Or would there be another, easier approach? Any pointers would be most welcome! Thanks.
  8. Today I again bumped into an issue I had last year with creating WebP images: Enabling WebP on another project by setting imageSizerOptions' webpAdd to true immediately resulted in the same behaviour as I had last year: the server simply keeps on creating new variations of images, while they already exist. WebP images are created correctly by the way. Seems to be a bug, can anybody else confirm? I am using PW Master (3.0.165) on MAMP Pro (MacOS 11.2, PHP 7.4.2)
  9. Great addition @ryan! Quick question: will ProCache be able to cache (the result from) these URL hooks?
  10. Hi @Sebi, I ran into a bug: Currently the Router::handleError() function incorrectly reports HTTP 500 errors that result from methods that should have remained silent, because they are called with the error control operator '@'. As the PHP documentation on error control operators puts it: I bumped into this while working on a project with some semi-broken images, where the APP API module kept throwing 500's from PW's ImageInspector class. Exif issues can be rather difficult to handle sometimes.. The solution is simple: just add a check error_reporting() before the handleError method, for instance public static function handleError($errNo, $errStr, $errFile, $errLine) { if (error_reporting()) { $return = new \StdClass(); $return->error = 'Internal Server Error'; $return->devmessage = [ 'message' => $errStr, 'location' => $errFile, 'line' => $errLine ]; self::displayOrLogError($return, 500); } }
  11. Something I'm missing in the new Modules page are the requirements for installing, such as PW version and other required modules.
  12. For what it is worth, I use this intermediate solution to use ProCache in combination with the RestAPI module: https://processwire.com/talk/topic/20006-module-restapi/?do=findComment&comment=186881. Which gives me: 1. site.url/rest-api => live data 2. site.url/api => served by ProCache Of course this would only be useful for (static) pages that don't require any authentication or whatsoever.
  13. Removing $config->imageSizerOptions from config.php solved the issue, which lead me to test its settings. Turns out specifically setting webpAdd to false fixed it!
  14. Thank you for you insightful reply @Robin S! I've decided to step away from the FieldstePage method, and just creating simple fields with longer names. Takes some time to copy over all existing data, but it makes it all a lot simpler to deal with.
  15. Hi, I'm working on a process module which hooks after ProcessPageEdit::buildForm. // Inside the module's init() method: wire()->addHookAfter('ProcessPageEdit::buildForm', $this, 'renderFieldsAccordingToStop'); Using a lot of variables, the script decides then which fields to render and which ones to hide. It also dynamically sets the 'required' flag to only the visible fields, making sure the proper warnings get shown but only when these fields are visible. It's all quite straight-forward, when it comes to the regular InputFields: /** * Render the form, and hide all the fields that are not part of the currently active step * Note: These fields are hidden with the css class "hideme", as hiding them with collapse logic gave issues in combination with the dynamic requirements * * @param mixed $event * @return void */ public function renderFieldsAccordingToStop(HookEvent $event) { $return = $event->return; // Get the page that is currently being loaded $page = $event->object->getPage(); $input = wire("input"); $form = $event->arguments(0); $step = $this->getProgress($page); // Add some class to hide field when needed $html = "<style>.hideme{display: none;}</style>"; $form->prependMarkup($html); // Get all the fields inside the form $inputfields = $form->getAll()->not("id=submit_save"); // Set all inputfields according to step foreach ($inputfields as $key => $inputfield) { // Run some logic to decide if the field should be invisible (using CSS to hide, as the inputfield->collapse gave issues) if ( ... ) { $inputfield->wrapClass("hideme"); } // Else make sure to require it. else { $inputfield->required(true); } } } This works fine for most field. However, this does not work for fields that are embedded inside a FieldsetPage. The FieldsetPage itself does get set to required correctly, but the fields inside are not, and as a result a user can now skip these fields while I wish them to be required (but only when visible). I've tried a bunch of different techniques, but I can't get the fields inside of the FieldsetPage to become required. In fact, I can't figure out how to address these from inside the hook at all. The furthest I've gotten is access the names of the fields inside this FieldsetPage: $field = $inputfield->hasField; if ($field->type == "FieldtypeFieldsetPage") { // Get the inner fields foreach ($field->repeaterFields as $f) { $repeaterFieldName = wire()->fields->get($f); $subfieldName = "{$field}->{$repeaterFieldName}"; self::log("The current field inside the FieldsetPage is: $subfieldName"); //--> logs "myfieldset->myfield" } } But how on earth do I use that information to update the settings of the Inputfield for myfieldset->myfield? Especially how do I set the required property, but also in a more of a general question. (In addition, I also tried hooking into the Inputfield::render but to no avail. ) Thanks in advance!
  16. Thanks for your replies guys! Here is the output when outputting all the debugInfo inside the JSON. What seems interesting to me is that every variation seems to refer to itself as the original? That could be something maybe? The weird thing is, I use this exact same technique on a lot of sites and never had this issue. So I would guess it's some configuration setting somewhere that might be off.
  17. Hey everyone, I have a weird one. Somehow my image variations keep being regenerated, instead of served directly. The variations that are requested are in fact already present, but still get overwritten by (identical!) files upon page load, making everything rather tedious and slow. Permissions from config.php are set to default: <?php $config->chmodDir = '0755'; // permission for directories created by ProcessWire $config->chmodFile = '0644'; // permission for files created by ProcessWire ?> Files before loading the page, notice the time of the requested variations is 14:14: Same folder after loading the page just now (14:24): Some background: I'm currently implementing my standard technique (using the RestApi module to output json) on an older but updated site (3.0.148), running on PHP 7.2.22 (MAMP Pro), while still in (local) development. My image parser function is pretty basic: <?php public static function parseImage(PageImage $image) { $settings = [ 'sharpening' => 'medium', 'cropping' => false, "quality" => 95 ]; $sizes = [ "L" => [ "width" => 1500, "height" => 1500, "settings" => $settings, ], "M" => [ "width" => 750, "height" => 750, "settings" => $settings, ], "S" => [ "width" => 320, "height" => 320, "settings" => $settings, ], ]; // get orientation if ($image->width > $image->height) { $orientation = 'landscape'; } else { $orientation = 'portrait'; } $resulting_image = [ 'name' => $image->name, 'description' => self::parseLanguageField($image->description), "focus" => "{$image->focus()["left"]}% {$image->focus()["top"]}%", 'original' => [ 'src' => $image->url, 'width' => $image->width, 'height' => $image->height, 'orientation' => $orientation, ], ]; foreach ($sizes as $sizeName => $size) { $img = $image->size($size["width"], $size["height"], $size["settings"]); $resulting_image[$sizeName] = $img->url; } return $resulting_image; } ?> With (for the image above) the following output once rendered as JSON: "image": { "name": "cirkels_oo.jpg", "description": [], "focus": "50% 50%", "original": { "src": "/site/assets/files/2823/cirkels_oo.jpg", "width": 300, "height": 240, "orientation": "landscape" }, "L": "/site/assets/files/2823/cirkels_oo.1500x1500.jpg", "M": "/site/assets/files/2823/cirkels_oo.750x750.jpg", "S": "/site/assets/files/2823/cirkels_oo.320x320.jpg" }, Any ideas on what might be going on? Probably missing something very obvious here.. EDIT: I tried explicitly including 'forceNew' => false in the settings, but that gave the same result
  18. Ran into this problem today, thanks for asking this question 3,5 years ago @heldercervantes. Would have been hard to debug if you hadn't.
  19. Have you tried this? https://processwire.com/api/ref/pagefiles/delete/
  20. Did you check the database to see if the table is in fact still there?
  21. It depends completely on the payment provider you choose, there are several modules available, but they all require some hands-on programming. http://modules.processwire.com/search/?q=payment Also often payment providers require a callback-endpoint at your site to process the result of a payment action. If all goes well, it will call this endpoint while the user is waiting for a bit during the payment process. After your system processes and acknowledges the payment (for instance it changes the status of the user to 'registered'), the payment provide will then forward the user back to your site. But.. it can also be the case that this callback happens minutes, hours or even days later in case of a hiccup with the banking system for instance. And, in case of a credit-card payment, it can in fact even change the state of a payment a month later — should the user formally dispute the charge. So, the flow of registration -> payment -> access is not as 1-2-3 as it may seem. You will need to consider all the possible outcomes of your registration process. You will be able to read more about the exact information your payment provider sends and expects in return in their documentation, as this varies wildly. Another thing to consider is that you cannot depend on any $session variable during the callback function, as the payment provider has its own session. So you will probably have to pass the user-id to the payment provider while setting up the payment, which it in return should include during its callback to your server and which you can then use to look up the user. One other thing though, just in case you hadn't thought about this: you should never email users their passwords. With the exception of a single-use, time-constrained temporary password. For that purpose you can use the PasswordForceChange module.
  22. Thank you once again @Robin S, that is very helpful indeed. The other settings were mostly the result of me fiddling around trying to get the field back to working ?‍??⁉️☠️ I think it would be very helpful if this was clearly explained in the formatText's notes, as it is easy to overlook.
  23. So, this is a weird one. I initially thought this was a "CKEditor inside of Repeater Matrix"-problem, but for some reason a certain set of settings makes the CKEditor crash upon loading. I've tried on multiple systems, multiple browsers, and both on 3.0.123 (master) and the latest 3.0.142-beta. I'm probably making a simple mistake here, but couldn't figure it out. I've worked around the issue for now, but leaving this here for other to take a look at. To replicate: Import the following field, add it to a page, and try to edit. { "simple_body": { "id": 224, "name": "simple_body", "label": "Rich text with limited options", "flags": 0, "type": "FieldtypeTextareaLanguage", "inputfieldClass": "InputfieldCKEditor", "contentType": 1, "htmlOptions": [ 2 ], "minlength": 0, "maxlength": 0, "showCount": 0, "rows": 10, "toolbar": "NumberedList, BulletedList, PWLink, Unlink, SpecialChar, Sourcedialog", "inlineMode": 0, "useACF": 1, "usePurifier": 1, "toggles": [ 2, 4 ], "formatTags": "p;ul;li;a", "extraPlugins": [ "pwlink", "sourcedialog" ], "removePlugins": "image,magicline", "langBlankInherit": 0, "collapsed": 0, "textformatters": [ "TextformatterEntities" ], "showIf": "", "themeOffset": "", "themeBorder": "", "themeColor": "", "columnWidth": 100, "required": "", "requiredAttr": "", "requiredIf": "", "imageFields": "", "extraAllowedContent": "", "contentsCss": "", "contentsInlineCss": "", "stylesSet": "", "customOptions": "", "plugin_sourcedialog": "" } }
  24. Thank you very much @Robin S, I have been able to fix the issue! ?
×
×
  • Create New...