Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 09/15/2018 in all areas

  1. The focus this week was on covering the queue of issue reports, and a whole lot of progress was made. Plus some other useful additions can be found ProcessWire 3.0.113. This post covers all the details— https://processwire.com/blog/posts/processwire-3.0.113-core-updates/
    10 points
  2. Various significant updates today. As per @Robin S's suggestion above, the User Bar Page Versions, User Dev Template and Template Path panel features now support automatic swapping out of the files included via `$config->prependTemplateFile` and `$config->appendTemplateFile` to use the same suffix at the replaced template, eg `_init-dev.php` or `_main-dev.php` if you are on the home page and using a home-dev.php template file replacement. Robin also helped me track down a critical bug that was introduced to Page Versions and User Dev Templates some time ago - thanks for that! If you haven't checked out any of these features in Tracy before, they can be very handy for testing changes (especially on a live site). In particular, giving your clients access to choose from a dropdown of template options can be a very easy way to get their feedback on various options. I have also made several improvements to the new Console panel fullscreen editing experience and also updated the way the results pane scrolls when new items are added. Previously it always scrolled to the bottom of the pane. Now it scrolls to the top of the recently added section, so if you are using Clear & Run, then it will alway be at the top, but if you just Run to add new items, it will scroll down to the top of the new items. Hopefully you'll all find this a nice improvement and will help to reduce your scrolling. I recently noticed some nasty issues with the Console panel in Safari, especially the new fullscreen editing mode, but also when trying to manually resize the panel. Everything looks/works great on Chrome, Firefox, and Opera. I'll take another look and see what I can do about getting Safari to play nicely, but sometimes I feel like Safari is the new IE ?
    3 points
  3. Hello All, I've been working on a new Fieldtype + Inputfield combination that handles street addresses. I've been using it now for about 3 months on my latest administration system for a charity and, so far, it seems to be working really well for them. It's based on the meta-data from Google's LibAddressInput project and uses the data feeds from that to build a cache of address formats used by various countries. My initial testing was aided by @Macrura and @adrian - and they were later joined by @mel47 and @BFD Calendar - so a big thank-you to them all. You can access the repository on GitHub and the Module Repository. Here's a few images from the project. First up: config screen. Here's an early version... ...and a more recent update... Here's a UK-based address. The module can integrate with my tag parser, if installed. Note that the output is formatted according to the output meta-data from the feed - which should match the preferred postal preferences of the destination country. Which subfields are required, and their regex expressions (if any) are also pulled from the feed and used as validation rules on the input side. Here's an address in the Netherlands - inputfield layout is currently adjusted on save - if you've configured it to adjust based on the destination country. Hopefully this will eventually be ajax driven. Use of the address preview is configurable and the HTML output can include micro-format data. Address sub-fields can also be used in selectors... Back with inputs, if you prefer a table-based input - you can have it... Format hints (unfortunately, I've found that many users need these)... Let me know if you find any issues or if you have any feature requests. So far, I have this from the previous testers... Allow multi-lingual address input for countries that support a multi-lingual postal system (like Canada.)
    2 points
  4. Should be as simple as this: foreach($page->children as $p) { $p->of(false); $p->street_address->recipient = $p->recipient; $p->street_address->organization = $p->organization; $p->street_address->street_address = $p->street_address; $p->street_address->street_address_2 = $p->street_address_2; $p->street_address->street_address_3 = $p->street_address_3; $p->street_address->locality = $p->locality; $p->street_address->dependent_locality = $p->dependent_locality; $p->street_address->admin_area = $p->admin_area; //state, province, etc $p->street_address->postal_code = $p->postal_code; $p->street_address->country_iso = $p->country_iso; $p->save('street_address'); } I would go to the parent page of all your people or businesses or whatever and run that in Tracy's Console panel and you'll be done! Obviously you want to keep the name of the subfield on the left as I have them, but you may need to change the names of the fields on the right side of the "=" Also, in my example, the name of the field in my system is "street_address" - all occurrences of that would need to be changed to match the name of the field you create using this fieldtype. Does that make sense?
    2 points
  5. Hello @matjazp The metadata for postal formatting of the addresses comes from Google's libaddressinput project. I note that the "SI-" prefix to the Slovenian postal code is actually coming from Google's postal address format metadata for Slovenia. Please reference line 1324 of the formats.php file and you'll see that "...%nSI- %Z..." appears in the format string. This forces the literal "SI- " to appear at the start of a new line, just before the zip/postal code. Essentially this is an upstream library issue and you can open an issue against libaddressinput to attempt to get it resolved, though I note there is an old, closed, issue regarding the prefix to the Slovenian postal code. However, I anticipated situations like this and there is a mechanism built in to the module code you can use to override the upstream format. If you edit your local copy of formats_overrides.php and add the following to the returned array, you will be able to customise the format as you require until Google fixes (or doesn't) the upstream repo... 'SI' => [ 'fmt'=>'%N%n%O%n%A%n%Z %C', // Prevent "SI - " from prefixing the postal code. ], The metadata for Slovenia marks non of the fields as mandatory - so you should find this input works for you as is. In fact, from version 1.0.2, you need to rename the example.formats_overrides.php file to formats_overrides.php as I am currently working on making the installed overrides file persist across upgrades (something it doesn't do quite correctly yet.) Also; just in case you haven't worked out how to suppress the country in the input and output of domestic addresses, you should be able to do so by setting the config options for the street address field as follows... Allowable countries: select just Slovenia. Show destination country field?: Set to "Never". Regarding benefits over a free-form textarea, here are some I can think of... Address subfields can be used in PW selectors: $addresses = $pages->find('address.locality=Paris'); Automatic compliance with layout requirements (including capitalisation and required fields) of the destination countries postal format (where correct in the meta data). Slovenia seems quite relaxed in which fields are required and which should be capitalised. Many European countries require certain fields, and require them to be output capitalised when used as postal addresses. Aids data cleaning on input over a free-form textarea (lots of my users type with caps-lock on, for example!) Automatic zipcode/regex application to subfields. Direct access to subfields, if you want to use them in your own template output other than in a full address label. Assorted format() functions for single/multiline, plain/html output. Automatic inclusion of address microformat metadata to the fields. I did read various UI/UX forums about this issue before I started, many of them came to the conclusion that a free-form input is best from the UX point of view - and I can understand that point of view. This module is my attempt to meld as close to a free-form input experience as I can, with structured data that accepts just about any country's postal code format. I hope that helps. Best wishes, Steve
    2 points
  6. 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
  7. I have my doubts it will work with FB - I know I had quite a few issues getting my Phone field working. I think in general multivalue fields need some tweaking to work. I eventually got Phone almost totally supported though.
    1 point
  8. I think current incompatibility is more likely than a PEBCAK issue. ?
    1 point
  9. @adrian Yes it certainly does. Thank you so much for your support!
    1 point
  10. If you want a GUI you can try RockGrid. It has a batch deletion feature.
    1 point
  11. Of course. For instance when I have a website for a company called "Hello Inc" and this company is located in three addresses I would create a Multiplier field for the textareas. This field is only used for outputting the three addresses on the contact page. This Street Address field would feel a bit too much for the editors to maintain. Also an error is quickly spotted since there are only three addresses. On the other hand I'm prototyping a database which may hold 1000's of companies. I would definitely use this field because I want it to be accessible and queryable. Therefore the data structure is more essential.
    1 point
  12. ? Thanks! This is a difficult one. I've have created so many times different fields and used textareas. Each choice will bite you in the end. It really depends. When you have a several addresses for a company you want to output on a website I would not use this field. When you are building a directory/database of companies or other users I would definitely use this, because you want the data to be structured.
    1 point
  13. Right, I understand now. You are wanting to give a role (let's call the role "manager") the ability to edit roles/permissions and add new roles/permissions. First thing to know is that in doing this you would be going well off the map of what is documented in ProcessWire and straying into some potentially dangerous territory. Normally only superusers manage roles and permissions, and if you decide to deviate from that you'll want to do your own thorough testing. It sounds risky to me and not something to be done lightly. But I took a look at what's needed to enable this and it seems that the steps are... Manage roles 1. Create new permission "role-admin". 2. Give this permission to the manager role. 3. Open the "role" template at Setup > Templates (you'll need to show system templates in the filter section). On the Access tab allow the manager role to "Edit Pages" and "Add Pages". 4. Open the "admin" template, and on the Access tab allow the manager role to "Add Children". Manage permissions 1. Create new permission "permission-admin". 2. Give this permission to the manager role. 3. Open the "permission" template at Setup > Templates (you'll need to show system templates in the filter section). On the Access tab allow the manager role to "Edit Pages" and "Add Pages". 4. Open the "admin" template, and on the Access tab allow the manager role to "Add Children". You can skip this step if you already did it.
    1 point
  14. Hey @Robin S - it didn't, but now it does ? I have also implemented this for the User Bar Page Versions feature and the Template Path panel. These three things do almost the same thing, but for different users in different scenarios. I am going to PM you a copy of this new version to take a look at. I have some other things I am working on and I want to commit altogether.
    1 point
  15. Similar to typing "google" into google. The internets breaks.
    1 point
  16. Not wishing to repeat myself here......
    1 point
×
×
  • Create New...