diceline-chartmagnifierquestion-marktwitter-whiteTwitter_Logo_Blue

Today I Learned

How to set srcset and sizes attributes for responsive images

The default src attribute acts as fallback for a default image size for browsers that have no support for srcset:

<img src="/images/image.jpg">

The srcset attribute gives the browser a bunch of different sizes of the same image to choose from.

The name of the image doesn't matter, you could name them image-small, image-medium and so on, but the 375w part should match the real width of the image:

<img
    src="/images/image.jpg"
    srcset="/images/image-375.jpg 375w,
            /images/image-768.jpg 768w,
            /images/image-1440.jpg 1440w,
            /images/image-1920.jpg 1920w,
            /images/image-2560.jpg 2560w"
>

Adding the sizes attribute specifies what image size to choose at a specific breakpoint and above (min-width) or below (max-width) it:

<img
    src="/images/image.jpg"
    srcset="/images/image-375.jpg 375w,
            /images/image-768.jpg 768w,
            /images/image-1440.jpg 1440w,
            /images/image-1920.jpg 1920w,
            /images/image-2560.jpg 2560w"
    sizes="(min-width: 1920px) 1400px, (min-width: 1600px) 1000px, (min-width: 768px) 700px, 300px"
>

One thing to note here is that the browser will first calculate the device width and then look at the sizes attribute and choose the first matching condition.

If you set for example sizes="(max-width: 1920px) 1500px, (max-width: 768px) 600px, 300px", and the device width is 640px, the first matching condition will be max-width: 1920px, therefore it will choose the 1500px image width suggestion, rather than the 600 you most probably expected. The same can happen with the min-width if the order is not right.

Notice also that we are asking the browser to choose a 1000px wide image between 1600px and 1919px, but in the srcset sources, there's no image at 1000px. Here, the browser will choose the image with the exact size, if there is one, or the closest one if not, but higher in size than specified, in this case, 1000px. Therefore, the 1440px will be selected.

It can take a while to grasp how this works, but it's worth taking the time to understand it.

Postman Automatic Auth Token

Postman can run JavaScript commands when executing a request. These commands can be placed in the "Tests" tab inside a request. For example, to automatically set a token as a collection variable, the following commands are used:

var auth = pm.response.headers.get("Authorization");
pm.collectionVariables.set('token', auth.split(" ")[1]);

The Docs regarding Postman scripting can be found here:

https://learning.postman.com/docs/writing-scripts/script-references/postman-sandbox-api-reference/

Automate the creation and deletion of EC2 snapshots via AWS CLI 2

We're heavily using AWS and we're scripting everything we can. As AWS CLI 2 came out recently we needed to update our scripts.

This script creates a new snapshot and deletes all snapshots older than 2 weeks, for a specific volume.

create-snapshot-and-cleanup.sh

#!/bin/bash

DESCRIPTION="example.com"
VOLUME="vol-xxxxxxxxxx"

SNAPSHOT_AGE=$(date +%Y-%m-%d --date '2 weeks ago')
TODAY=$(date +%d-%m-%Y)

echo "Creating new snapshot of volume $VOLUME."
aws ec2 create-snapshot --output text --description "$DESCRIPTION - AutoSnapshot $TODAY" --volume-id $VOLUME >> /dev/null

echo "Deleting snapshots older than: $SNAPSHOT_AGE"

snapshots=$(aws ec2 describe-snapshots --output text --filters Name=volume-id,Values=$VOLUME --query "Snapshots[?StartTime<'$SNAPSHOT_AGE'].SnapshotId")

echo "Snapshots sheduled for deletion: $snapshots"

for snapshot in $snapshots; do
  echo "Deleting $snapshot ..."
  aws ec2 delete-snapshot --snapshot-id $snapshot
done

Check composer platform requirements

If you ever need to check what php version do composer packages and their dependencies require, there's a special command just for that.

composer check-platform-reqs

The output should look something like below. Maybe without the failed steps.

ext-dom        20031129                                                success  
ext-fileinfo    7.3.19                                                  success  
ext-filter      7.3.19                                                  success  
ext-json       1.7.0                                                   success  
ext-libxml     7.3.19                                                  success  
ext-mbstring   7.3.19                                                  success  
ext-openssl    7.3.19                                                  success  
ext-pcre       7.3.19                                                  success  
ext-Phar       7.3.19                                                  success  
ext-SimpleXML  7.3.19                                                  success  
ext-tokenizer  7.3.19                                                  success  
ext-xml        7.3.19                                                  success  
ext-xmlwriter  7.3.19                                                  success  
lib-pcre       10.32                                                   success  
php            7.3.19    league/commonmark requires php (^7.4 || ^8.0) failed   
php            7.3.19    league/config requires php (^7.4 || ^8.0)      failed   

This is useful when your app fails due to unfulfilled dependency requirements.

Automatically add and remove SSH keys from remote hosts

Managing multiple hosts is a pain when using SSH key authentication. There are a lot of solutions out there for managing SSH keys, there's Ansible, Puppet, or other paid solutions.

There's also another option, to use a script to do the job for you.

Here's how

1. Create a targets file containing your hosts and usernames

# Host
hostname.example.com username

2. Create a add-keys.sh file

#!/bin/bash
keys=${1:-*.pub}
echo update ssh-keys: ${keys}

cat targets | grep -vE '^(\s*$|#)' | sed 's/#.*$//g' | while read host user
do
   echo "# Adding public ssh-keys for $user@$host"
   for k in ${keys};
   do
      echo "# Adding public key $k"
      touch ${k%\.pub}
      ssh-copy-id -f -i $k $user@$host
   done
done

3. Create a remove-keys.sh file


#!/bin/bash

keys=${1:-*.pub}
echo update ssh-keys: ${keys}

cat targets | grep -vE '^(\s*$|#)' | sed 's/#.*$//g' | while read host user
do
   echo "# Remove public ssh-keys for $user@$host"
   for k in ${keys};
   do
      echo "# Remove public key $k"
      key=$(<$k)
      ssh $user@$host 'bash -s' <<EOT
sed -i "/$key/d" ~/.ssh/authorized_keys
EOT
      echo "# Key removed"
   done
done

4. Add the public keys you want to add as .pub files in the same folder

5. Run one of the scripts. Done.

How to solve an issue that depends on other people's actions in a big company

Have you ever been in a situation where you're a cog in a big company your work depends on other people's action and you can't get them to do their part so that you can finish your own tasks?

If the answer is yes, you know how frustrating this can be. This happens because the other people involved have their own priorities and you're at the very bottom.

In order to get things done, you have to get to the top of their list and the solution is rather simple:

Create a carefully non-personal, non-emotional email in which you explain the situation, place the person responsibile in the "To:" field and add their direct manager, your own manager, and anyone remotely responsible in the "Cc" field.

I guarantee your problem will be solved in no time. :D

You can even escalate this further up the chain with a follow-up email where you add more managers up the chain in the "Cc:" field.

Don't forget to thank everyone involved after your issue is solved.

How to access refs outside a VueJs template

Sometimes you need to make use of the ref attribute to access a child component in VueJs, like so:

<input ref="someRefName"></input>
this.$refs.someRefName

But what if you need to do the same thing for an html element that's still inside your app but outside of your VueJs template or component? Maybe you've tried it and this.$refs.someRefName comes in undefined.

That's when the $root instance comes in handy. Just add the ref attribute on the target element and access it in your VueJs component like this:

this.$root.$refs.someRefName

That makes VueJs look for that reference in the root instance of your app, and not only inside your component.

Just make sure you don't overuse it. Most of the times, there's a better way to do what you need to do.

How to force a flexbox item to a new row

At some point you might end up with a challenge like this. You've got several items (we'll take an example of 3) laid out with flexbox.

<div class="container">
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
</div>

The items have fixed width and they all fit on one row, but you want the third to jump on the second row.

Like this:

====item====item====
========item========

So how do you make them stack 2 on the first row, and the third on the second row, centered to the middle?

If you try flex-wrap: wrap, depending on the device resolution, it might do the trick or not. If all 3 fit on the same row, they won't wrap.

The solution is to force them by adding a collapsed row (height 0) which takes up the entire width of the container, making it occupy an entire row, thus pushing the 3rd item on the next row. Think of it like a <br> tag.

<div class="container">
  <div class="item"></div>
  <div class="item"></div>
  <div class="break"></div>
  <div class="item"></div>
</div>
.break {
  flex-basis: 100%;
  height: 0;
}

Neat trick, right?

And it can be adapted to other situations as well, not necessarily flexbox.

Getting exif metadata from image base64 in PHP

Exif headers provide useful metadata when it comes to images such as the image's orientation.

If you have used / are using Intervention Image, you might have noticed that some of the pictures you upload are turned from portrait to landscape.

If you upload the image normally, in Laravel you can orientate the image properly like so:

Image::make(Request::file('image'))->orientate();

But if you get the image data as a base 64 string this approach won't work, but the following snippet can be used to get the desired information from the image

$stream_resource = "data://image/jpeg;base64," . $base_64_image_string;
$exif_meta = exif_read_data($stream_resource);

You can now get the image's orientation and act based on it's value

$orientation = $exif_meta["Orientation"];

Also, make sure you have this line in you php.ini file

allow_url_fopen=On

Otherwise you will get

ErrorException: exif_read_data(): Unable to open file in file [filename]