Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 02/24/2019 in all areas

  1. Apparently "instanceof" doesn't allow constants, strings, or expressions, while is_a() happily accepts all of those. This is news to me, and I'm not entirely sure of the behind the scenes logic, but it probably has something to do with instanceof being a language construct (those sometimes behave a little bit unexpectedly) – or it might simply be a performance optimization. After all instanceof is much better in terms of performance than is_a(). Use variables or literal class names instead – or if you really have to, use the workaround provided by @tpr above, i.e. store the constant value to a variable before use ?
    3 points
  2. Same here, but without "\ProcessWire\" the output is 0: $className = '\FieldtypeEmail'; // or $className = 'FieldtypeEmail';
    2 points
  3. Hello there, I've started using ProcessWire at work a while ago and I have been really enjoying building modular, clean and fast sites based on the CMS (at work, I usually post as @schwarzdesign). While building my first couple of websites with ProcessWire, I have written some useful helper functions for repetitive tasks. In this post I want to showcase and explain a particular function that generates a responsive image tag based on an image field, in the hope that some of you will find it useful :) I'll give a short explanation of responsive images and then walk through the different steps involved in generating the necessary markup & image variations. I want to keep this beginner-friendly, so most of you can probably skip over some parts. What are responsive images I want to keep this part short, there's a really good in-depth article about responsive images on MDN if you are interested in the details. The short version is that a responsive image tag is simply an <img>-tag that includes a couple of alternative image sources with different resolutions for the browser to choose from. This way, smaller screens can download the small image variant and save data, whereas high-resolution retina displays can download the extra-large variants for a crisp display experience. This information is contained in two special attributes: srcset - This attribute contains a list of source URLs for this image. For each source, the width of the image in pixels is specified. sizes - This attribute tells the browser how wide a space is available for the image, based on media queries (usually the width of the viewport). This is what a complete responsive image tag may look like: <img srcset="/site/assets/files/1015/happy_sheep_07.300x0.jpg 300w, /site/assets/files/1015/happy_sheep_07.600x0.jpg 600w, /site/assets/files/1015/happy_sheep_07.900x0.jpg 900w, /site/assets/files/1015/happy_sheep_07.1200x0.jpg 1200w, /site/assets/files/1015/happy_sheep_07.1800x0.jpg 1800w, /site/assets/files/1015/happy_sheep_07.2400x0.jpg 2400w" sizes="(min-width: 1140px) 350px, (min-width: 992px) 480px, (min-width: 576px) 540px, 100vw" src="/site/assets/files/1015/happy_sheep_07.1200x0.jpg" alt="One sheep"> This tells the browser that there are six different sources for this image available, ranging from 300px to 2400px wide variants (those are all the same image, just in different resolutions). It also tells the browser how wide the space for the image will be: 350px for viewports >= 1140px 480px for viewports >= 992px 540px for viewports >= 576px 100vw (full viewport width) for smaller viewports The sizes queries are checked in order of appearance and the browser uses the first one that matches. So now, the browser can calculate how large the image needs to be and then select the best fit from the srcset list to download. For browsers that don't support responsive images, a medium-sized variant is included as the normal src-Attribute. This is quite a lot of markup which I don't want to write by hand every time I want to place an image in a ProcessWire template. The helper function will need to generate both the markup and the variations of the original image. Building a reusable responsive image function Let's start with a function that takes two parameters: a Pageimage object and a standard width. Every time you access an image field through the API in a template (e.g. $page->my_image_field), you get a Pageimage object. Let's start with a skeleton for our function: function buildResponsiveImage( Pageimage $img, int $standard_width ): string { $default_img = $img->maxWidth($standard_width); return '<img src="' . $default_img->url() . '" alt="' . $img->description() . '">'; } // usage example echo buildResponsiveImage($page->my_image_field, 1200); This is already enough for a normal img tag (and it will serve as a fallback for older browsers). Now let's start adding to this, trying to keep the function as flexible and reusable as possible. Generating alternate resolutions We want to add a parameter that will allow the caller to specify in what sizes the alternatives should be generated. We could just accept an array parameter that contains the desired sizes as integers. But that is not very extendible, as we'll need to specify those sizes in each function call and change them all if the normal size of the image in the layout changes. Instead, we can use an array of factors; that will allow us to set a reasonable default, and still enable us to manually overwrite it. In the following, the function gets an optional parameter $variant_factor. // get the original image in full size $original_img = $img->getOriginal() ?? $img; // the default image for the src attribute, it wont be upscaled $default_image = $original_img->width($standard_width, ['upscaling' => false]); // the maximum size for our generated images $full_image_width = $original_img->width(); // fill the variant factors with defaults if not set if (empty($variant_factors)) { $variant_factors = [0.25, 0.5, 0.75, 1, 1.5, 2]; } // build the srcset attribute string, and generate the corresponding widths $srcset = []; foreach ($variant_factors as $factor) { // round up, srcset doesn't allow fractions $width = ceil($standard_width * $factor); // we won't upscale images if ($width <= $full_image_width) { $srcset[] = $original_img->width($width)->url() . " {$width}w"; } } $srcset = implode(', ', $srcset); // example usage echo buildResponsiveImage($page->my_image_field, 1200, [0.4, 0.5, 0.6, 0.8, 1, 1.25, 1.5, 2]); Note that for resizing purposes, we want to get the original image through the API first, as we will generate some larger alternatives of the images for retina displays. We also don't want to generate upscaled versions of the image if the original image isn't wide enough, so I added a constraint for that. The great thing about the foreach-loop is that it generates the markup and the images on the server at the same time. When we call $original_img->width($width), ProcessWire automatically generates a variant of the image in that size if it doesn't exist already. So we need to do little work in terms of image manipulation. Generating the sizes attribute markup For this, we could build elaborate abstractions of the normal media queries, but for now, I've kept it very simple. The sizes attribute is defined through another array parameter that contains the media queries as strings in order of appearance. $sizes_attribute = implode(', ', $sizes_queries); The media queries are always separated by commas followed by a space character, so that part can be handled by the function. We'll still need to manually write the media queries when calling the function though, so that is something that can be improved upon. Finetuning & improvements This is what the function looks like now: function buildResponsiveImage( Pageimage $img, int $standard_width, array $sizes_queries, ?array $variant_factors = [] ): string { // get the original image in full size $original_img = $img->getOriginal() ?? $img; // the default image for the src attribute, it wont be upscaled $default_image = $original_img->width($standard_width, ['upscaling' => false]); // the maximum size for our generated images $full_image_width = $original_img->width(); // fill the variant factors with defaults if not set if (empty($variant_factors)) { $variant_factors = [0.25, 0.5, 0.75, 1, 1.5, 2]; } // build the srcset attribute string, and generate the corresponding widths $srcset = []; foreach ($variant_factors as $factor) { // round up, srcset doesn't allow fractions $width = ceil($standard_width * $factor); // we won't upscale images if ($width <= $full_image_width) { $srcset[] = $original_img->width($width)->url() . " {$width}w"; } } $srcset = implode(', ', $srcset); return '<img src="' . $default_img->url() . '" alt="' . $img->description() . '" sizes="' . $sizes_attribute . '" srcset="' . $srcset . '">'; } It contains all the part we need, but there are some optimizations to make. First, we can make the $sizes_queries parameters optional. The sizes attribute default to 100vw (so the browser will always download an image large enough to fill the entire viewport width). This isn't optimal as it wastes bandwidth if the image doesn't fill the viewport, but it's good enough as a fallback. We can also make the width optional. When I have used this function in a project, the image I passed in was oftentimes already resized to the correct size. So we can make $standard_width an optional parameter that defaults to the width of the passed image. if (empty($standard_width)) { $standard_width = $img->width(); } Finally, we want to be able to pass in arbitrary attributes that will be added to the element. For now, we can just add a parameter $attributes that will be an associative array of attribute => value pairs. Then we need to collapse those into html markup. $attr_string = implode( ' ', array_map( function($attr, $value) { return $attr . '="' . $value . '"'; }, array_keys($attributes), $attributes ) ); This will also allow for some cleanup in the way the other attributes are generated, as we can simply add those to the $attributes array along the way. Here's the final version of this function with typehints and PHPDoc. Feel free to use this is your own projects. /** * Builds a responsive image element including different resolutions * of the passed image and optionally a sizes attribute build from * the passed queries. * * @param \Processwire\Pageimage $img The base image. * @param int|null $standard_width The standard width for this image. Use 0 or NULL to use the inherent size of the passed image. * @param array|null $attributes Optional array of html attributes. * @param array|null $sizes_queries The full queries and sizes for the sizes attribute. * @param array|null $variant_factors The multiplication factors for the alternate resolutions. * @return string */ function buildResponsiveImage( \Processwire\Pageimage $img, ?int $standard_width = 0, ?array $attributes = [], ?array $sizes_queries = [], ?array $variant_factors = [] ): string { // if $attributes is null, default to an empty array $attributes = $attributes ?? []; // if the standard width is empty, use the inherent width of the image if (empty($standard_width)) { $standard_width = $img->width(); } // get the original image in full size $original_img = $img->getOriginal() ?? $img; // the default image for the src attribute, it wont be // upscaled if the desired width is larger than the original $default_image = $original_img->width($standard_width, ['upscaling' => false]); // we won't create images larger than the original $full_image_width = $original_img->width(); // fill the variant factors with defaults if (empty($variant_factors)) { $variant_factors = [0.25, 0.5, 0.75, 1, 1.5, 2]; } // build the srcset attribute string, and generate the corresponding widths $srcset = []; foreach ($variant_factors as $factor) { // round up, srcset doesn't allow fractions $width = ceil($standard_width * $factor); // we won't upscale images if ($width <= $full_image_width) { $srcset[] = $original_img->width($width)->url() . " {$width}w"; } } $attributes['srcset'] = implode(', ', $srcset); // build the sizes attribute string if ($sizes_queries) { $attributes['sizes'] = implode(', ', $sizes_queries); } // add src fallback and alt attribute $attributes['src'] = $default_image->url(); if ($img->description()) { $attriutes['alt'] = $img->description(); } // implode the attributes array to html markup $attr_string = implode(' ', array_map(function($attr, $value) { return $attr . '="' . $value . '"'; }, array_keys($attributes), $attributes)); return "<img ${attr_string}>"; } Example usage with all arguments: echo buildResponsiveImage( $page->testimage, 1200, ['class' => 'img-fluid', 'id' => 'photo'], [ '(min-width: 1140px) 350px', '(min-width: 992px) 480px', '(min-width: 576px) 540px', '100vw' ], [0.4, 0.5, 0.6, 0.8, 1, 1.25, 1.5, 2] ); Result: <img class="img-fluid" id="photo" srcset="/site/assets/files/1/sean-pierce-1053024-unsplash.480x0.jpg 480w, /site/assets/files/1/sean-pierce-1053024-unsplash.600x0.jpg 600w, /site/assets/files/1/sean-pierce-1053024-unsplash.720x0.jpg 720w, /site/assets/files/1/sean-pierce-1053024-unsplash.960x0.jpg 960w, /site/assets/files/1/sean-pierce-1053024-unsplash.1200x0.jpg 1200w, /site/assets/files/1/sean-pierce-1053024-unsplash.1500x0.jpg 1500w, /site/assets/files/1/sean-pierce-1053024-unsplash.1800x0.jpg 1800w, /site/assets/files/1/sean-pierce-1053024-unsplash.2400x0.jpg 2400w" sizes="(min-width: 1140px) 350px, (min-width: 992px) 480px, (min-width: 576px) 540px, 100vw" src="/site/assets/files/1/sean-pierce-1053024-unsplash.1200x0.jpg" alt="by Sean Pierce"> Now this is actually too much functionality for one function; also, some of the code will be exactly the same for other, similar helper functions. If some of you are interested, I'll write a second part on how to split this into multiple smaller helper functions with some ideas on how to build upon it. But this has gotten long enough, so yeah, I hope this will be helpful or interesting to some of you :) Also, if you recognized any problems with this approach, or can point out some possible improvements, let me know. Thanks for reading!
    1 point
  4. I know you need to check a constant or variable, but if you enter things directly, you don't need the namespace. I guess it's to do with quoting FieldtypeEmail when assigning to a variable or const that causes the problem.
    1 point
  5. Thanks. That works! I was playing around a bit more with it, and during that I managed to completely trash Tracy (it was gone all of a sudden), had massive problems with PW's file compiler and Tracy Cache, plus PHP memory limits and whatnot... Most probably not related to mPDF or RockPDF at all, I guess I was using wireRenderFile() wrong... Here's a little example (generates a 24-pages PDF with limited rights, meta-data, and custom font in approx. 3 seconds while in debug mode) <? @ini_set('memory_limit', '8192M'); // just to be on the safe side @set_time_limit(600); // ditto $pdf = $modules->get('RockPdf'); $pdf->settings([ 'mode' => 'utf-8', 'defaultheaderfontsize' => 9, 'defaultheaderfontstyle' => 'I', 'defaultheaderline' => 0, 'SHYlang' => 'de', // doesn't work 'hyphens' => 'auto', // doesn't work - I have this also in my CSS 'margin-top' => 20, // doesn't seem to work either, for some reason 'fontdata' => [ "inter" => [ 'R' => "Inter-Regular.ttf", 'B' => "Inter-Black.ttf", 'I' => "Inter-Italic.ttf", 'BI' => "Inter-BlackItalic.ttf", ]], 'default_font' => 'inter' ]); $pdf->set('SetHeader', 'Offerte Steuerrechner Kanton Zürich 2019, Test AG, ' . date('H:i:s')); $pdf->set('SetFooter', '{PAGENO}'); $pdf->set('SetTitle', 'Offerte Steuerrechner Kanton Zürich 2019'); $pdf->set('SetAuthor', 'Me Myself and I'); $pdf->set('SetCreator', 'Test AG'); $pdf->set('SetSubject', 'Offerte Steuerrechner'); $pdf->set('SetKeywords', 'Steuerrechner, Offerte, Angebot, Test AG, Frontend-Entwicklung'); $pdf->set('SetProtection', array('copy', 'print', 'print-highres')); $myOffer = 11568; // each offer has a parent page with meta-data, and "chapters" as children. We pass this to the template on the next line $str = wireRenderFile("/home/mysitecom/www/dev.test/site/templates/offer-index-dev.php", array("offer_index_page_id" => $myOffer)); $fn = $pages->get($myOffer)->name; // will be used to generate filename.pdf $pdf->write($str); d($pdf->save("/home/mysitecom/www/dev.test/site/assets/files/$myOffer/$fn.pdf")); // generate pdf in PW default file assets folder // in offer-index-dev.php, I have this at the top if(!isset($offer_index_page_id)) { return $this->halt(); exit(); } if(isset($offer_index_page_id)) { $offer_index = $pages->get($offer_index_page_id); } // I then use $offer_index->title etc. instead of $page->title further below Not exactly rocket science, but maybe somebody else can use bits of it as first baby-steps ?
    1 point
  6. @Robin S Thanks! I made two tests - everything works just fine now (didn't test yet with specifying templates using [[abc]], but no doubt that works as well)
    1 point
  7. @dragan, I just pushed an update to the repo (no version number change) that adds the "Source" button to the CKEditor field to make it easier to check/report any issues with source code.
    1 point
  8. Good thing is that PHP is so widespread, and hence, also well-documented. You should be able to quickly find a solution if a certain function behaves now different than before. There's plenty of migration guides - here's just one example: http://php.net/manual/en/migration70.php
    1 point
  9. If I understand you correctly, you have added a new image field to your template for the featured image and you need the url for the src attribute. ? Lets assume the name of the new image field is 'featured_image' you will get the url of this image by using $page->featured_image->url So the code of the blog-post.php could look like this: <?php $img = $page->featured_image; if($img) { $img = $img->width(600); echo "<p class='uk-text-center'><img src='$img->url' alt='$img->description'></p>"; } ?> Image is an object, so you have to grab the different values (attributes) of the image by using the arrows (->) followed by the name of the attribute (size, url,...). You will find more information about getting image values here If this was not the information you are asking for please post your code for better understanding. Best regards
    1 point
  10. @bernhard Servus Bernhard, I was playing around with RockPDF today... I was wondering if you maybe disabled some mPDF functions (intentionally or not)... I tried e.g. setting header styles or use hyphenation, but those don't seem to work. // either of these returns // Exception: Call to undefined method Mpdf\Mpdf::defaultheaderfontsize() on line: 50 in /home/mysite/www/dev.mysite/site/modules/RockPdf/RockPdf.module.php $pdf->defaultheaderfontsize=9; $pdf->defaultheaderfontstyle='I'; // same here: Exception: Call to undefined method Mpdf\Mpdf::SHYlang() $pdf->SHYlang = 'de'; These functions are straight from the official docs... oh, and btw: How do I adjust the default download path and filename? (using something else than "output.pdf") d'oh: $pdf->save('foo.pdf');
    1 point
  11. Working fine here on PHP 7.2. However, I'd still do what @dragan suggested. Upgrades have a tendency to go awry :-). Also note that despite your best efforts to test locally, things may not always work as they should in your remote install. Often times it is because your host changed a setting in the server during the upgrade and worse, didn't even tell you about it! You'd be left in the dark blaming PHP 7 or some other module or ProcessWire only to realise a couple of emails later that actually, none of these were to blame ? and the culprit was a seemingly unrelated server setting. Hopefully, this doesn't happen to you...I'm not scaremongering; just thought you should be aware ?.
    1 point
  12. Hi @gottberg There is a bit of work to get images, as the way you are doing return Images Objects and not array. You can have a look at this file to see the "complexity" to retrieve those objects : https://github.com/microcipcip/processvue/blob/master/site-processvue/templates/inc/pagefields.php#L236 Or you can build your own images array before assigning it to the API answer : See updated ImageHelper.php file at the bottom of this post. /* route : 'page' => [ ['GET', '{id:\d+}', Example::class, 'getPage', ["auth" => false]] ] */ // get page route answer function public static function getPage($data) { $data = RestApiHelper::checkAndSanitizeRequiredParameters($data, ['id|int']); $response = new \StdClass(); $p = wire('pages')->get($data->id); if(!$p ->id) throw new \Exception('Page not found'); $response->id = $p->id; $response->name = $p->name; $response->title = $p->title; $response->body = $p->body; $response->sidebar = $p->sidebar; $response->summary = $p->summary; // our own images array $images = array(); foreach ($p->images as $image) { array_push($images, array( $image->name => array( 'url' => $image->url, 'filename' => $image->filename, 'width' => $image->width, 'height' => $image->height // ... ) ) ); } $response->images = $images; return $response; } Result : --- Edit : The image class helper : <?php namespace ProcessWire; /** * InputfieldImage Helper * Use for single image * * Usage : * $image is your ProcessWire PageImage * return ImageHelper::get($image, [400, 800, 1200, 2000, 2500]); */ class ImageHelper { public static function get ($image, $widths = [400, 800, 1200]) { $response = new \StdClass(); $response->focus = ["x" => $image->focus['left'], "y" => $image->focus['top']]; $response->urls = []; $response->description = $image->description; $response->name = $image->basename; $response->width = $image->width; $response->height = $image->height; foreach ($widths as $width) { $croppedImage = $image->width($width); $url = new \StdClass(); $url->url = $croppedImage->httpUrl; $url->width = $croppedImage->width; $url->height = $croppedImage->height; $url->ratio = $croppedImage->height / $croppedImage->width; array_push($response->urls, $url); } return $response; } } /** * InputfieldImage Helper * Use for multiple images * * Usage : * $images is your ProcessWire PageImages * return ImagesHelper::get($images, [400, 800, 1200, 2000, 2500]); */ class ImagesHelper { public static function get($images, $widths = [400, 800, 1200]) { $response = new \StdClass(); $response->images = []; $img = new \StdClass(); // our own images array $imagesArr = array(); foreach ($images as $image) { $img->focus = ["x" => $image->focus['left'], "y" => $image->focus['top']]; $img->urls = []; $img->description = $image->description; $img->name = $image->basename; $img->width = $image->width; $img->height = $image->height; foreach ($widths as $width) { $croppedImage = $image->width($width); $url = new \StdClass(); $url->url = $croppedImage->httpUrl; $url->width = $croppedImage->width; $url->height = $croppedImage->height; $url->ratio = $croppedImage->height / $croppedImage->width; array_push($img->urls, $url); } array_push($response->images, $img); } return $response; } }
    1 point
  13. Render UIKit list Here is another function to create various types of lists corresponding to https://getuikit.com/docs/list /** * Render a uikit list * * @param array $listitems All list items as an array, you can also use html inside the array (fe links) * @param array $options Optionally specify different options to list. * @return string * */ function ukList($listitems = array(), $options = array()) { if(count($listitems) == 0) return; $defaults = array( 'divider' => false, 'bullet' => false, 'striped' => false, 'large' => false, 'icon' => '', // icon to display before list items 'class' => '' // add custom class to list item ); $options = _ukMergeOptions($defaults, $options); $out = ''; $classes = array(); if($options['class']) $classes = explode(' ', $options['class']); if($options['divider']) $classes[] = 'uk-list-divider'; if($options['bullet']) $classes[] = 'uk-list-bullet'; if($options['striped']) $classes[] = 'uk-list-striped'; if($options['large']) $classes[] = 'uk-list-large'; if(count($classes)) { $class = ' ' . implode(' ', $classes); } else { $class = ''; } $out .= '<ul class="uk-list'.$class.'">'; foreach($listitems as $listitem) { $out .= '<li>'; $icon = (!empty($options['icon'])) ? ukIcon($options['icon']).' ' : ''; $out .= $icon.$listitem.'</li>'; } $out .= '</ul>'; return $out; } Usage examples: echo ukList($page->children); //creates a simple unstyled list of all child pages ids echo ukList($page->children, $options = array('icon' => 'heart', 'striped' => true, 'large' => true)); //creates a striped list, with heart icon in front and a larger margin
    1 point
  14. To make this more real world code. It's best practice to better check if there really is children and images if ($page->child->id) { // at least one child page viewable (access), access save foreach($page->children as $selectedthumbs) { if(!count($selectedthumbs->selectedimages) continue; // no image found continue foreach $firstimage = $selectedthumbs->selectedimages->first(); $thumb = $firstimage->size(100, 100); echo "<a href='{$selectedthumbs->url}'><img src='{$thumb->url}' alt='{$firstimage->description}'></a>"; } } You can also use the fast check if($page->numChildren) {... But it's not access aware and also includes unpublished pages. BUT it's faster than for example if($page->children()->count()){ .. If you have 1 million children (cite Ryan) it would matter and children->count() example would get slow. So what if I numChildren() returns true but there's no page viewable by the visitor? It doesn't matter if you only output something inside the foreach(...), because there you use $page->children which does only return pages published and viewable pages. But this could leave you with empty UL's: if ($page->numChildren) { echo "<ul>"; // this may output even if the following foreach doesn't find children foreach($page->children("limit=25") as $child) { echo "<a>$child->title</li>"; } echo "</ul>"; // this also } End of lesson.
    1 point
×
×
  • Create New...