Leaderboard
Popular Content
Showing content with the highest reputation on 06/19/2021 in all areas
-
Second Episode: "How can I add a watermark to all pageimages of a site?" first episode "Tutorial how to add a site wide optional watermarking method to all pageimages" second episode "Second Episode: "How can I add a watermark to all pageimages of a site?"" In the first episode we created a function that adds a watermark overlay to our original images. This function was hooked via the name "wm" to the pageimage object. We positioned a big centralized overlay to the original images. This suites right if you or your customer will not use cropped portions of the images somewhere down in the site. So, given the setup of the first episode is fine for a sites images concept, one can add a simple security layer to protect the original (unwatermarked) images. (Read on here how to secure the original images and how to keep them accessible / viewable in the admin for logged in users.) So in the second episode we want to support sites that need a more individual handling of images, including different output sizes and different cropped parts from an original image. Also we want to show a watermark in the bottom right corner in a 200px width, regardless of the width of the image variation. 1) Setup our site with a new overlay image 1.1) Create a new PNG image of 400 px width and 140 px height. Give it a half transparent background-color (40 - 60 percent opacity) and add some none transparent text over it. The text should be more then in the double size as you want to see it in the output on your site. For example like this one: 1.2) Upload it into the centralized image field (called setting_watermark in the previous episode) 2) Lets update our watermark function in site/ready.php 2.1) We now cannot any longer watermark the original images at first, because we don't know the exact final output dimensions and also not the final (maybe cropped) parts of the variations. Therefore we will work with the pageimage objects as usual and add our call to the watermark method as LAST action to each pageimage that should get a watermark overlay. $image = $page->images->first(); $variation = $image->crop('some settings here')->width(800)->wm(); ?> <img src='<?=$variation->url?>' alt='<?=$variation->description?>' /> So we will rewrite our function to suite our new needs. The first part can be taken mainly unchanged from the first episode: /** * Example Pageimage Hook for watermarking functionality, * adds a new method to Pageimage objetcs named wm. * Used in a small tutorial series in the ProcessWire forums * https://processwire.com/talk/topic/25752-tutorial-how-to-add-a-site-wide-optional-watermarking-method-to-all-pageimages/ * * @version 0.0.2 * */ $this->addHook('Pageimage::wm', function(HookEvent $event) { // get the image out of the event object $finalImage = $event->object; // this is no longer the $originalImage from episode 1 !! // access the field of the transparent watermark overlay image in a robust way, // regardless if it is set to 1 image or multiple images, and if it has changed // in this regard since we wrote this hook function $watermarkImages = wire()->pages->get('id=1')->getUnformatted('setting_watermark'); // check if the imagefield exists and it at least contain one image, than fetch it if(!$watermarkImages || 0 == count($watermarkImages)) { // return the unmodified original image to do not break our front end site $event->return = $finalImage; // inform the admin about the missing watermark overlay, (or do something other useful) wireMail('admin@example.com', 'noreply@example.com', 'MISSING SITEWIDE WATERMARK OVERLAY IMAGE'); wireLog('errors', 'MISSING SITEWIDE WATERMARK OVERLAY IMAGE'); // stop further execution return; } 2.2) Now lets have a short stop and think over. What do we want to achieve? We want create watermarked image variations and protect the unwatermarked images. Unwatermarked images are the original images and the intermediate variations that are created before we add an overlay to them. So in this situation, when we would following the regular image processing chain, we would create a variation with the resized and / or cropped version and after that, a watermarked variation, that we want to send to the browsers: original image: basename.jpg intermediate: basename.800x0.jpg watermarked: basename.800x0.-pim2-wm.jpg This would let all intermediate images unsafe in public scope, for everyone who knows a bit about the PW image processing routines. So the best solution would be to create the intermediate variations, watermark them and restore with the intermediate filename, not a filename that reflects that different processing step. This sounds fine, and indeed it is, with only one thing that we have to solve later: How can we detect if the current version is a watermarked (final) variation, or a unwatermarked "real" intermediate variation? 2.3) Following we rewrite the function from the first episode. First we have to check the incoming image, mainly its dimensions and if fits fine with the watermark sizes: // as we will overwrite this image with a watermarked version, // we have to make sure that this really is a variation and NOT an original image $isOriginal = null == $finalImage->getOriginal(); if($isOriginal) { // throw an exception, as this must be a bug in our code throw new WireException('Called a watermark method for final variation images on a original image!'); return; } // inspect the given image $ii = new ImageInspector(); $info = (object) $ii->inspect($finalImage->filename)['info']; // our preferred watermark width is 200px, so lets check if it fits into the variation if(200 >= $info->width) { // does not fit, is it a portrait oriented image with more then 300 px height? if(200 < $info->height) { // ok, we have to rotate our watermark -90 degree to make it fit to the bottom right $pngAlphaImage = $watermarkImages->first()->width(200)->pim2Load('rot270')->rotate(270)->pimSave(); } else { // the variation is to small for useful watermarking, we will leave and output it as is $event->return = $finalImage; return; } } else { // the width fits, so get a watermark image of 200px $pngAlphaImage = $watermarkImages->first()->width(200); } // now lets apply the watermark to our final variation, // this has to be done a bit different than we have done in the first episode. // The difference is, that we previously created another intermediate variation. // Now we want to avoid this, as it would make our watermarking processing nearly useless // in regard of protecting the unwatermarked images // we invoke the Pageimage Manipulator in a way that let us "overwrite" the loaded image, // what is not possible within its default processing $im = new \PageImageManipulator02(); $im = $im->imLoad($finalImage->filename, ['targetFilename' => $finalImage->filename])->setTargetFilename($finalImage->filename); $filename = $im->watermarkLogo($pngAlphaImage, 'SE', 0)->save(); $success = $finalImage->filename == $filename; // the imLoad()->save() process returns the filename of the processed image on success // $watermarkedImage = $finalImage; // this just should visualize what has happen in the black box :-) // With the previous episode, we had to return the watermarked variation and therefore had to replace the event object with it: // $event->return = $watermarkedImage; // But here, in fact we have overwritten the discfile that belongs to our incoming image object, // so there is no NEW image object that we have to change with the initial one! $event->return = $finalImage; }); 2.4) So, now we nearly have all together, but not how we can detect if a intermediate variation already has become a watermarked (final) version. Therefore the watermarking process is constantly executed again, with every view call now! So, what options are available to store the variations state for read / write in the processing chain? Into the DB? (has to be implemented first, and has to be cleared whenever an image and or its variations gets removed from the system) Into a sidecar file? (has to be implemented first, and also has to be cleared whenever an image and or its variations gets removed from the system) Into the variation file itself? (can be done via IPTC-data, but only for JPEGs) We also have since version 3.0.164 the possibility to store additional information for images & files into the DB. But this is only useful for the original image (or the complete group of original image and all its variation files), and not for individual single variation files. For this example we will go with the IPTC-data and take a simple helper class, that encapsulates this processes for us. For simplicity in this example, we also copy its code into the ready.php file. 2.5) Now we add the tracking into our function from above: ... // as we will overwrite this image with a watermarked version, // we have to make sure that this really is a variation and NOT an original image $isOriginal = null == $finalImage->getOriginal(); if($isOriginal) { // throw an exception, as this must be a bug in our code throw new WireException('Called a watermark method for final variation images on a original image!'); return; } // also we have to check if it already got watermarked, what we cannot get from the pageimage objects hirarchy // (that also is reflected in the filenames), as we have or will obfuscate(d) it: // read from IPTC tag if it is already processed $iptc = new hnReadWriteIptc($finalImage->filename); $isWatermarked = (bool)$iptc->readTag('isWatermarked'); if(!$isWatermarked) { ... So, if it is NOT watermarked, we will do it now with the code from our already built function. The only addition is to store the successful processing into the IPTC data, so that it is known with the next view call: ... // write our successful processing into our dedicated IPTC field to avoid repetitive processing $iptc->writeTag('isWatermarked', '1'); $iptc->writeIptcIntoFile(); } $event->return = $finalImage; }); 3) Our final code in the site/ready.php looks like this now: <?php namespace ProcessWire; /** * Example Pageimage Hook for watermarking functionality, * adds a new method to Pageimage objetcs named wm. * Used in a small tutorial series in the ProcessWire forums * https://processwire.com/talk/topic/25752-tutorial-how-to-add-a-site-wide-optional-watermarking-method-to-all-pageimages/ * * @version 0.0.2 * */ $this->addHook('Pageimage::wm', function(HookEvent $event) { // get the image out of the event object $finalImage = $event->object; // this is no longer the $originalImage from episode 1 !! // access the field of the transparent watermark overlay image in a robust way, // regardless if it is set to 1 image or multiple images, and if it has changed // in this regard since we wrote this hook function $watermarkImages = wire()->pages->get('id=1')->getUnformatted('setting_watermark'); // check if the imagefield exists and it at least contain one image, than fetch it if(!$watermarkImages || 0 == count($watermarkImages)) { // return the unmodified original image to do not break our front end site $event->return = $finalImage; // inform the admin about the missing watermark overlay, (or do something other useful) wireMail('admin@example.com', 'noreply@example.com', 'MISSING SITEWIDE WATERMARK OVERLAY IMAGE'); wireLog('errors', 'MISSING SITEWIDE WATERMARK OVERLAY IMAGE'); // stop further execution return; } // as we will overwrite this image with a watermarked version, // we have to make sure that this really is a variation and NOT an original image $isOriginal = null == $finalImage->getOriginal(); if($isOriginal) { // throw an exception, as this must be a bug in our code throw new WireException('Called a watermark method for final variation images on a original image!'); return; } // also we have to check if it already got watermarked, what we cannot get from the pageimage objects hirarchy // (that also reflects in the filenames), as we have or will obfuscate(d) them: // read from IPTC tag if it is already processed $iptc = new hnReadWriteIptc($finalImage->filename); $isWatermarked = (bool)$iptc->readTag('isWatermarked'); if(!$isWatermarked) { // inspect the given image $ii = new ImageInspector(); $info = (object) $ii->inspect($finalImage->filename)['info']; // our preferred watermark width is 200px, so lets check if it fits into the variation if(200 >= $info->width) { // does not fit, is it a portrait oriented image with more then 300 px height? if(200 < $info->height) { // ok, we have to rotate our watermark -90 degree to make it fit to the bottom right $pngAlphaImage = $watermarkImages->first()->width(200)->pim2Load('rot270')->rotate(270)->pimSave(); } else { // the variation is to small for useful watermarking, we will leave and output // it as is $event->return = $finalImage; return; } } else { // the width fits, get a watermark image of 200px $pngAlphaImage = $watermarkImages->first()->width(200); } // now lets apply the watermark to our final variation, // this has to be done a bit different than we have done in the first episode. // The difference is, that we previously created a intermediate variation. // Now we want to avoid this, as it would make our watermarking processing nearly useless // in regard of protecting the unwatermarked images // we invoke the Pageimage Manipulator in a way that lets us overwrite the loaded image, // what is not possible within its default behave $im = new \PageImageManipulator02(); $im = $im->imLoad($finalImage->filename, ['targetFilename' => $finalImage->filename])->setTargetFilename($finalImage->filename); $filename = $im->watermarkLogo($pngAlphaImage, 'SE', 0)->save(); $success = $finalImage->filename == $filename; // the imLoad()->save() process returns the filename of the processed image on success // $watermarkedImage = $finalImage; // this just should visualize what has happen in the black box :-) // to return the watermarked variation, // we have to replace the event object with our watermark variation // $event->return = $watermarkedImage; // But infact we have overwritten the discfile that belongs to our image object, // so there is no NEW imageobject that we have to change with the initial one // write our successful processing into our dedicated IPTC field to avoid repetitive processing $iptc->writeTag('isWatermarked', '1'); $iptc->writeIptcIntoFile(); } $event->return = $finalImage; }); Now we can call our watermarked images like this: $image = $page->images->first(); $variation = $image->width(800)->wm(); ?> <img src='<?=$variation->url?>' alt='<?=$variation->description?>' /> Thanks for reading! ?6 points
-
I'm posting this as an update to an earlier post created by @Hari KT: https://processwire.com/talk/topic/4958-composer-support-for-processwire/. Though that approach still (kind of) works (as does the one detailed in https://github.com/wireframe-framework/processwire-composer-installer), thanks to @d'Hinnisdaël there's now a better alternative: the official composer/installers project ? An example repository implementing the things detailed in this post: GitHub repository: https://github.com/teppokoivula/HelloWorld Packagist entry: https://packagist.org/packages/teppokoivula/hello-world As a module author, how do I make my module installable via Composer? 1) Add a composer.json file to your module's directory. Here's an example: { "name": "vendor-name/module-name", "type": "processwire-module", "license": "MIT", "extra": { "installer-name": "ModuleName" }, "require": { "composer/installers": "~1.0" } } The composer.json file explained: "name" consists of two parts: your vendor (author) name, and the name of the package (module). These can (but don't have to) be the same as your GitHub or BitBucket user and repository names. Please note that this value should be all lowercase! That's the syntax expected by both Packagist and Composer. "type" should be "processwire-module". You may have seen "pw-module" used by other packages; that's the value used by third party installers, you don't need to worry about that now. "license" should specify the license your module is published under. See Composer help for expected syntax. It's technically fine to leave this out, but it's always a good idea to let users know how they're allowed to use your code. "installer-name" under "extra" should specify the expected directory name for your module. Usually this is the same as your module's name. If you leave this out, the package part of the "name" value will be used instead (which may be just fine, though I'd recommend always filling in this value). "require" includes Composer dependencies of your module. The key part here is "composer/installers" — without this Composer won't know that your module requires said installer, and it may not be installable at all, so be sure to add this row. 2) Submit your project to Packagist: https://packagist.org/packages/submit. You will need an account for this step. It's free and very easy to register, and you can automatically connect it with your GitHub account. Connecting with GitHub also makes it easier to auto-update package versions from GitHub repository. 3) Recommended but not absolutely necessary: add tags to your module's Git repository. It's recommended that when you push a new version of your module to GitHub or BitBucket, you also add a matching tag: if you push version 0.0.3 (or version "3", following the old school ProcessWire version number format), you should also add tag 0.0.3 (or "v0.0.3" if you want to be verbose) to GitHub/BitBucket. (This step is not strictly speaking necessary, but it does make things easier for users installing your module, and makes much easier to track which version of the module is currently installed via Composer. It requires additional step when publishing a new version of the module, but please consider doing it anyway!) 4) Also recommended but not absolutely necessary: configure Packagist to auto-update based on GitHub/BitBucket. Follow the instructions here: https://packagist.org/about#how-to-update-packages. This step ensures that once you push a new version of your module, Packagist automatically updates stored information without you logging in and hitting the "update" button manually. (This step may not be necessary if you've already allowed Packagist access to your GitHub account.) ... and that's it. Congratulations, your module is now installable via Composer! As a module user, how do install a module via Composer? Go to your site's root directory and type on the command-line "composer install vendor-name/module-name". You can look up the correct details from Packagist, or the module author may have included them in the support forum thread. Obviously this only works for those modules that have implemented Composer installer support as outlined in this tutorial. Note: if you're using a "non-standard" directory structure for ProcessWire — you've moved the root of the project outside the public web root, or something along those lines — check out the custom install paths part of the composer/installers README. The "installer-paths" setting allows you to manually specify a custom install path for the "processwire-module" package type.5 points
-
ProcessWire 3.0.180 contains 20 commits containing various minor new features, issue resolutions and pull requests. While there's no single major feature to write a big blog post around, combined there are a lot of worthwhile and useful updates so this version is definitely worth updating to. More details can be found in the dev branch commit log and at ProcessWire Weekly (issue #370 covered an addition to our $files API var). Yesterday the forums were running a little slow because we had our yearly DDOS’er pay the site a visit once again (remember last time?), and from an apparently unlimited supply of IP addresses around the world this time. We shut down the forums to users that weren't logged in while the load was high. Actually, it does this automatically now. We also updated the forums from using memcached to AWS Redis, which should also help as a nice upgrade for the forums. Big thanks to @Pete and @Jan V. for setting it up and keeping everything running smoothly. I'll keep it short today because it's supposed to rain here all weekend, so I'm going to spend some time outside while I can. Thanks for reading and have a great weekend!2 points
-
1 point
-
Retrieve, collect and store geolocation data from external geocoding services. Under the hood, the module uses the great PHP Library geocoder-php by William Durand and Tobias Nyholm and adds some processwire magic. Download/Install Github: https://github.com/neuerituale/FieldtypeGeocoder Module directory: https://processwire.com/modules/fieldtype-geocoder/ Composer: composer require nr/fieldtypegeocoder v.1.0.1 - Add support for GraphQL-Module by dadish v.1.0.2 - Support Repeater v.1.0.3 - Add support for bitwise status search. v.1.0.4 - Fix php 8.2 warning #3 - Update composer file - Update leaflet version - Add short access to coordinates1 point
-
Hi there, I'm starting a small series of tutorials here. The first one(s) deal with the topic "How can I add a watermark to all pageimages of a site?" first episode "Tutorial how to add a site wide optional watermarking method to all pageimages" second episode "Second Episode: "How can I add a watermark to all pageimages of a site?"" This is targeted to all, - beginners, intermediate, pros, - but mainly to interested PW lovers. The fictitious starting situation could be that a website owner has been creating beautiful photos for many years and would like to show them on his site in higher output sizes. A reference to real people or events does not exist, or would be "purely coincidental". Could someone please tell @Rob(AU) about this. ? This first episode shows a really straight and forward solution that could be an answer to the question "How can I get things be done?". We will create a PHP function that will give back a watermarked variation of every pageimage we put into it. We don't want bother our self to much with coding or image manipulation stuff. So for this first example we will setup our site with a single centralized image field that can contain one PNG image that should be used as our alpha transparency overlay for all watermarked image variations. Additionally to that, we install a PW third party module that does the image manipulation stuff. I will use the Page Image Manipulation module for this, because I know it very well. There are also *other good solutions in the PW ecosystem and if you would like you can add and use every existing PHP library that can assist you, or write your own individual low level code for that. But that's not our topic here. We want to get things done. Lets start! 1) Create a single centralized image field somewhere on a settings page if you have one. If your site doesn't has one, because you haven't had a need for that til now, the homepage may be a good place too. If you go with the homepage, but for other situations too, it may be a good idea to separate the overlay image input field from other content you have on that page. 1.1) Create an image field that should contain one file, and only of type PNG 1.2) Create a fieldset tab 1.3) Add both to the template of your choice 2) Add the Page Image Manipulator module to your site ... 2.1) ... by going to your modules section in the admin, select the tab "New" and add the Modules Classname under "Add Module From Directory": PageImageManipulator 2.2) Download and install it. 2.3) Go to the modules site tab and scroll down to the position where you find the entries for a Pageimage Manipulator 1 and a Pageimage Manipulator 2. There you can savely uninstall the legacy version 1 that is only needed for PW version prior to 2.6. 3) Provide a PNG overlay image. For this example we want use a 2000 x 2000 px transparent PNG image with our watermark sign or watermark text. If you don't have one ready but have an image manipulation program on your local machine at hand that excepts photoshop actions, you can download one here for fast creation: photoshop_action_PW-png-creations.zip 3.1) Create a new image 2000px x 2000px with a transparent background 3.2) Add some meaningful text at a position you like. If you want to use the PS action, don't forget to rasterize all vector text before starting it. 3.3) Regardless if you used the PS action or created some nice overlay in another way, you should set the opacity of the final image to something between 20 and 40 percent. You may have to test this out for a production version. But now we want to get things done. Save the final version and ... 3.4) Upload it into the centralized image field in your site! 4) Create a PHP function that encapsulates the watermark overlay process and outputs a finished pageimage as a variation. To get a go here without messing up our site, we can create a test.php file as sibling of PWs index.php. We will *boostrap PW there and get full access to the API site this way: <?php namespace ProcessWire; include __DIR__ . '/index.php'; function ourWatermarkTestfunction(Pageimage $image) { return $watermarkedImage; } Let the file operating in the PW namespace, boostrap the PW API and our barebone function. We want to pass in a pageimage and we want to get out a pageimage that is a watermarked variation of the source pageimage. If we want to test what we have so far, we would fetch us an image from a page we know it has one and pass it into the function: <?php namespace ProcessWire; include __DIR__ . '/index.php'; function ourWatermarkTestfunction(Pageimage $originalImage) { $watermarkedImage = $originalImage; return $watermarkedImage; } $image = $pages->get('[your selector here to a well known page that has an images field called images]')->images->first(); $variation = ourWatermarkTestfunction($image); $variation = $variation->width(800); echo "<img src='{$variation->url}' alt='{$variation->description}' />"; We tweaked our function to pass us the input image as output. And we echo out it as a HTML image. If this is working as expected, we can start to do code the main part of our function. We will use the module Pageimage Manipulator for that and referring to its description page, when we scroll down to the method called watermarkLogo, it tells us how we can use it. watermarkLogo($pngAlphaImage, $position='center', $padding=2) It want to have one mandatory param, the transparent PNG overlay image. Optionally it can take params for positioning the overlay. For our test we will use the defaults and as they are optional arguments, we don't need to call them. Referring to the PIM2 API, we have to start the process by calling pim2Load($prefix, $param2=optional, $param3=optional) And at the end we have to call pimSave() Putting this together we will get this: function ourWatermarkTestfunction(Pageimage $originalImage) { $watermarkedImage = $originalImage->pim2Load('wm')->watermarkLogo($pngAlphaImage)->pimSave(); return $watermarkedImage; } So, this will produce an error as we have not specified what $pngAlphaImage is. It should be our site wide overlay image that we have stored at a well known place, for example on our homepage. Also we want to apply a check if we really get a Pageimage from there, to avoid unhandled errors if for whatever reason the image gets deleted by accident at some time in the future. function ourWatermarkTestfunction(Pageimage $originalImage) { // get the field of the transparent watermark overlay image in a robust way, // regardless if it is set to 1 image or multiple images, and if it gets changed or not in the future $watermarkImages = wire()->pages->get('id=1')->getUnformatted('setting_watermark'); if(!$watermarkImages) { // return the unmodified original image to do not break our front end site return $originalImage; } // check if it at least contain one image, than fetch it if(0 == count($watermarkImages)) { // return the unmodified original image to do not break our front end site return $originalImage; } $pngAlphaImage = $watermarkImages->first(); // is the same as wire()->pages->get('id=1')->getUnformatted('setting_watermark')->first() // now let the module process our watermarking and save it into a variation $watermarkedImage = $originalImage->pim2Load('wm')->watermarkLogo($pngAlphaImage)->pimSave(); // return the watermarked variation return $watermarkedImage; } Lets have a visually check if all went well: $image = $pages->get('[your selector here to a well known page that has an images field called images]')->images->first(); $variation = ourWatermarkTestfunction($image); $variation = $variation->width(800); ?> <img src='<?=$variation->url?>' alt='<?=$variation->description?>' /> Now we should see our watermarked variation in the same dimensions as the original is. That's fine and it's time to leave the test and implement that into our custom site API. We will add a hook to the Pageimage object that we want call 'wm' and that does exactly what we have created here. 5) Open your site/ready.php and add the following hook into it: <?php namespace ProcessWire; $this->addHook('Pageimage::wm', function(HookEvent $event) { }); Now we can call on every Pageimage the new method wm: $image->wm->width(500) to get a watermarked and 500px width resized variation out. Ok, first we have to add our functions body into it. And we slightly have to change it a bit, because now it became a anonymous function of a HookEvent. Therefore it has not a own name anymore, and the input is not a Pageimage in the first time, but a HookEvent. (But in the end, it is a Pageimage, because it is a hooked event of a Pageimage. ?) /** * Example Pageimage Hook for watermarking functionality, * adds a new method to Pageimage objetcs named wm. * Used in a small tutorial series in the ProcessWire forums * https://processwire.com/talk/topic/25752-tutorial-how-to-add-a-site-wide-optional-watermarking-method-to-all-pageimages/ * * @version 0.0.1 * * @var HookEvent */ $this->addHook('Pageimage::wm', function(HookEvent $event) { // get the image out of the event object $originalImage = $event->object; // access the field of the transparent watermark overlay image in a robust way, // regardless if it is set to 1 image or multiple images, and if it has changed // in this regard since we wrote this hook function $watermarkImages = wire()->pages->get('id=1')->getUnformatted('setting_watermark'); // check if the imagefield exists and it at least contain one image, than fetch it if(!$watermarkImages || 0 == count($watermarkImages)) { // return the unmodified original image to do not break our front end site $event->return = $originalImage; // inform the admin about the missing watermark overlay, (or do something other useful) wireMail('admin@example.com', 'noreply@example.com', 'MISSING SITEWIDE WATERMARK OVERLAY IMAGE'); wireLog('errors', 'MISSING SITEWIDE WATERMARK OVERLAY IMAGE'); // stop further execution return; } $pngAlphaImage = $watermarkImages->first(); // is the same as wire()->pages->get('id=1')->getUnformatted('setting_watermark')->first() // now let the module process our watermarking and save it into a variation $watermarkedImage = $originalImage->pim2Load('wm')->watermarkLogo($pngAlphaImage)->pimSave(); // to return the watermarked variation, // we have to replace the event object with our watermark variation $event->return = $watermarkedImage; }); Now we can call our watermarked images like this: $image = $page->images->first(); $variation = $image->wm()->width(800); ?> <img src='<?=$variation->url?>' alt='<?=$variation->description?>' /> Now all looks fine in the first line. But there is some room for improvements also in this basic "get the things done" solution. I keep this for one of the next episodes, where we can dive a little bit into image debugging and / or create a more advanced version of the image watermarking function, maybe as a PW module. Thanks for reading! ? TL;DR *pageimage https://processwire.com/api/ref/pageimage/ *boostrap https://processwire.com/docs/front-end/include/ *image modules https://processwire.com/modules/category/photo-video/ *hooks https://processwire.com/docs/modules/hooks/ *hook event__https://processwire.com/api/ref/hook-event/1 point
-
Klippo Eutin - https://www.klippo-eutin.de/ "Is my child safe while walking around town?"... well at least in Eutin (Germany) children of all ages are safe now or at least they can find a spot in case they need help. Our client and partner Deutscher Kinderschutzbund Eutin e.V. (roughly translated to: German Child Protection Association Eutin) started a new initiative to help children finding a safespace while en route through town. No matter what - a missed transportation, low battery, in need of a toilet, or whatever. Certified partners of Klippo Eutin help children of all ages to overcome all kinds of situations challenging their day. Verified in person and visible through special markers at and within the locations and their showrooms so children know how to find help now. Klippo Eutin is our second iteration after Klippo Neumünster for this kind of project and builds a new foundation of even more cities to build this kind of service for children all around Germany and even other countries. Design details Childish, yet... practical with some kind of nerdiness with a lot of details here and there. From logo art to vector backgrounds all around. We love the details. As always. Oh... and yes... the screenshot might already be different to the details you see online as this is one of our rolling-release-development projects - whenever there is an update necessary it will be developed and released. For the good cause. Yet... there is more planned and coming in the future. Technical details Google Map Marker PageHitCounter PrivacyWire Changelog Verified URL (Pro) ProCache (Pro) (Eigenwerbung [Advertising upcoming]: you know cities that might be interested, let us know... we already have a solution ready to start in your area, almost worldwide, and even better: we can provide contacts for Germany to get this kind of project started!) The team behind this: Muskaat for the technical part (yes, I'm part of Muskaat) https://www.muskaat.de/ Polimorf for the design part https://www.polimorf.de/ I hope you'll enjoy this site as much as we do!1 point
-
A serializer module for ProcessWire Pages. This module will add a new method to all pages, called serializer(), which returns JSON. https://github.com/Luis85/FlowtiPageSerializer ## Dependecies symfony/serializer symfony/property-access ## Requirements ProcessWire 3.x Composer ## Installation cd site/modules git clone git@github.com:Luis85/FlowtiPageSerializer.git cd FlowtiPageSerializer composer install ## Usage $page->serialize() will return the serialized Page Object as a JSON string representation containing all accessable fields calling $page->serialize('field_name') returns the Page Object with just this field $page->serialize(['field1', 'field2']) will return the choosen fields This is my initial commit, just cobbled together a wrapper around the symfony/serializer component which i plan to use for data serialization and deserialization. The module can just serialize a given page and limit its output a field string or an array of strings. Supports only json output right now. Supports only 1 dimension of data, so no reference titles / .dot syntax. Can someone please enlighten me how to setup a proper composer setup for PW Module Development btw?1 point
-
Shouldn't it be enough to associate the styles with the block page's id and make sure cloning a page also clones its associated styles? If you assign a unique id (pgrid_id) to each block but keep that around after cloning, you won't be able to edit the cloned page's styles without it affecting the original page's styles. Or maybe I'm missing a part of the picture here.1 point
-
Could you explain what your goal is? I mean — you're already handling dependencies via Composer, so that's a good start at least ? If you want the module itself to be installable via Composer, the approach I'd currently recommend is detailed here: https://github.com/wireframe-framework/processwire-composer-installer. Change the "type" in your composer.json to "pw-module" and add wireframe-framework/processwire-composer-installer to your requires and that's just about it. In case you were wondering, ProcessWire doesn't currently have a way to handle Composer dependencies when a module is installed via Admin. Module with dependencies will either a) need to be installed the regular way and then the user has to run composer install manually, or b) installed via Composer (see processwire-composer-installer) in which case dependencies are automatically handled. ... or you could bundle all dependencies in the Git repository itself. Somewhat crude approach perhaps, but also the easiest one for most users of your module.1 point
-
If you can build enough discipline in the way you write conditionals, you can avoid this by always putting the constant part first: if ('xx' == $page->template->name) { So if you accidentally miss an equals sign and do an assignment, you get an error from the runtime as you are trying to assign to a constant string literal. I've tried to build this habit over the years and sometimes remember to do this - but I'm not there yet.1 point
-
https://github.com/chrisbennett-Bene/AdminThemeTweaker Inspired by @bernhard's excellent work on the new customisable LESS CSS getting rolled into the core soon, I thought I would offer up the module for beta testing, if it is of interest to anyone. It takes a different approach to admin styling, basically using the Cascade part of CSS to over-ride default UiKit values. Values are stored in ModuleConfig Module creates a separate AdminThemeTweaker Folder at root, so it can link to AdminThemeTweaker.php as CSS AdminThemeTweaker.php reads the module values, constructs the CSS variables then includes the CSS framework Can be switched on and off with a click. Uninstall removes everything, thanks to bernhard's wonderful remove dir & contents function. It won't touch your core. It won't care if stuff is upgraded. You won't need to compile anything and you don't need to touch CSS unless you want to. It won't do much at all apart from read some values from your module config, work out the right CSS variables to use (auto contrast based on selected backgrounds) and throw it on your screen. You can configure a lot of stuff, leave it as it comes (dark and curvy), change two main colors (background and content background) or delve deep to configure custom margins, height of mastheads, and all manner of silly stuff I never use. Have been developing it for somewhere around 2 years now. It has been (and will continue to be) constantly tweaked over that time, as I click on something and find something else to do. That said, it is pretty solid and has been in constant use as my sole Admin styling option for all of those 2 years. If nothing else, it would be great if it can provide any assistance to @bernhard or other contributor's who may be looking to solve some of the quirkier UiKit behavior. Has (in my opinion) more robust and predictable handling of hidden Inputfields, data-colwidths and showIf wrappers. I am very keen to help out with that stuff in any way I can, though LESS (and any css frameworks/tools basically) are not my go. I love CSS variables and banging-rocks-together, no-dependency CSS you can write with notepad.1 point