John Svensson: How to dynamically create image styles derivatives - Part 1

Planet Drupal - 11. Juni 2018 - 14:45

Three months ago I wrote an article on how to Create Image Styles and Effects programmatically and today we're following up on that article but introducing on how we can do that dynamically.

So, essentially what we would like to do is that we display an image, where we can adjust the way the image is outputted, given a height, width or aspect ratio etc.

Please bear in mind that all code provided in this article are experimental and does not yet cover things like access control, etc in this part.

Let's take a look at the service Unsplash.com. Its basically a free image bank with high quality images submitted by awesome freelancers and professionals that you can use for free.

Image by Eric Ward

The URL for the image above is the following:

https://images.unsplash.com/photo-1499365094259-713ae26508c5?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=26d4766855746c603e3d42aaec633144&auto=format&fit=crop&w=500&q=60

The parts we're actually interested in are: &auto=format&fit=crop&w=500&q=60 we can adjust them as we like and the image is displayed differently, i.e. changing the width of the earlier image to a smaller one:

Alright, that's what we would like to do in Drupal 8. This article will be very iteratively, we'll rewrite the same code over and over until we get what we want. We'll notice issues and problems that we will deal with through out the article.

Prepare an environment to work in

We'll use a fresh Drupal 8.6.x installation.

To quickly scaffold some boilerplate code I'm going to use Drupal Console.

First let's create a custom module where we can put our code and logic in:

$ vendor/bin/drupal generate:module

I'll name the module dynamic_image_viewer

dynamic_image_viewer.info.yml

name: 'Dynamic Image Viewer' type: module description: 'View an image dynamically' core: 8.x package: 'Custom'

Next we need some images to work with, we'll use the core Media module for that. So let's enable that module:

vendor/bin/drupal module:install media

Now we can add some images. Go to Content >> Media >> Add media.

Implementing a Controller to display the image

The first step is to create a controller that will render the Media image to the browser. Again we'll use Drupal Console for a controller scaffold: vendor/bin/drupal generate:controller

We'll create a route on /image/{media} where Media will accept an media ID that due to Drupals parameter upcasting will give us a media instance in the controller method arguments. Doing this, if a invalid media ID is passed in the URL a 404 page is shown for us. Neat!

So we'll modify the generated controller slightly to this:

src/Controller/ImageController.php

<?php namespace Drupal\dynamic_image_viewer\Controller; use Drupal\Core\Controller\ControllerBase; use Drupal\media\MediaInterface; /** * Class ImageController. */ class ImageController extends ControllerBase { /** * Show an image. * * @param MediaInterface $media * * @return array */ public function show(MediaInterface $media) { return [ '#type' => 'markup', '#markup' => $media->id(), ]; } }

And the routing file looks like this: dynamic_image_viewer.routing.yml

dynamic_image_viewer.image_controller_show: path: '/image/{media}' defaults: _controller: '\Drupal\dynamic_image_viewer\Controller\ImageController::show' _title: 'show' requirements: _permission: 'access content'

If we install the module, vendor/bin/drupal module:install dynamic_image_viewer and hit the URL /image/1 we should see a page with the ID being outputted.

Render the original image

Ok. Currently nothing is rendered, so what we'll do is that we render the uploaded original image first.

To serve the file we'll use BinaryFileResponse. So let's update the ImageController::show method.

We'll also import the class in the top of the file:

use Symfony\Component\HttpFoundation\BinaryFileResponse;

/** * Show an image. * * @param MediaInterface $media * * @return BinaryFileResponse */ public function show(MediaInterface $media) { $file = $media->field_media_image->entity; $uri = $file->getFileUri(); $headers = file_get_content_headers($file); $response = new BinaryFileResponse($uri, 200, $headers); return $response; }

So what we do here is that we grab the File entity from the field_media_image field on the Media image bundle. We get the URI and, using the file_get_content_headers we get the proper headers. Finally we serve the file back with the proper headers to the viewer.

And if we hit the URL again:

Before we continue, we should note some things that we'll get back to later:

  • What if the media ID is not a Media image?
  • The user can still access the media even if its unpublished.
  • What about cache?
Let's make a hard-coded image derivative

To modify the image, we'll create a new instance of ImageStyle and add an image effect.

Let's update the ImageController::show method again:

/** * Show an image. * * @param MediaInterface $media * * @return BinaryFileResponse */ public function show(MediaInterface $media) { $file = $media->field_media_image->entity; $image_uri = $file->getFileUri(); $image_style = ImageStyle::create([ 'name' => uniqid(), // @TODO This will create a new image derivative on each request. ]); $image_style->addImageEffect([ 'id' => 'image_scale_and_crop', 'weight' => 0, 'data' => [ 'width' => 600, 'height' => 500, ], ]); $derivative_uri = $image_style->buildUri($image_uri); $success = file_exists($derivative_uri) || $image_style->createDerivative($image_uri, $derivative_uri); $response = new BinaryFileResponse($derivative_uri, 200); return $response; }

So what we do here is that we create a new ImageStyle entity, but we don't save it. We give it a unique name (but we'll change that soon) and then add we add an image effect that scale and crops the image to a width of 600 and height 500.
And then we build the derivate uri and if the file exists already, we'll serve it and if not we'll create a derivative of it.

There is one big problem here. Since we use a unique id as name of the image style we'll generate a new derivative on each request which means that the same image will be re-generated over and over. To solve it for now, we could just change the

$image_style = ImageStyle::create([ 'name' => uniqid(), // @TODO This will create a new image derivative on each request.

to a constant value, but I left it for that reason intentionally. The reason is that I want to explicitily tell us that we need to do something about that and here is how:

If we look back at the URI from Unsplash earlier &auto=format&fit=crop&w=500&q=60, these different keys are telling the code to derive the image in a certain way.

We'll use the provided keys and combine them some how in to a fitting name for the image style. For instance, we could just take the values and join them with a underscore.

Like so:

format_crop_500_60 and we'll have a unique string. If the user enters the same URL with the same parameters we'll be able to find the already existing derivative or if its another image, we'll create a derivative for it.

You'll also notice that I removed the $headers = file_get_content_headers($file); it is because those headers are not the correct ones for ur derivatives, we'll add them back soon.

Dynamic width and height values

On our second iteration of the code we'll now add the width and height parameters, and we'll also change the name of the image style to be dynamic.

Again, we'll update ImageController::show

We'll also import a class by adding use Symfony\Component\HttpFoundation\Request; in the top of the file.

/** * Show an image. * * @param Request $request * @param MediaInterface $media * * @return BinaryFileResponse */ public function show(Request $request, MediaInterface $media) { $query = $request->query; $width = (int) $query->get('width', 500); $height = (int) $query->get('height', 500); // We'll create the image style name from the provided values. $image_style_id = sprintf('%d_%d', $width, $height); $file = $media->field_media_image->entity; $image_uri = $file->getFileUri(); $image_style = ImageStyle::create([ 'name' => $image_style_id, ]); $image_style->addImageEffect([ 'id' => 'image_scale_and_crop', 'weight' => 0, 'data' => [ 'width' => $width, 'height' => $height, ], ]); // ... Rest of code

First we updated the method signature and injected the current request. Next, we'll get the width and height parameters if they exist and if not we fallback to something. We'll build an image style name of these dynamic values. With this we updated the name of the ImageStyle instance we create which makes sure that we can load the same derivative if the user hits the same URL. Finally we updated the width and height in the image effect.

Let's add the proper headers back

Here is the updated ImageController::show and current file:

src/Controller/ImageController.php

<?php namespace Drupal\dynamic_image_viewer\Controller; use Drupal\Core\Controller\ControllerBase; use Drupal\media\MediaInterface; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Drupal\image\Entity\ImageStyle; use Symfony\Component\HttpFoundation\Request; use Drupal\Core\Image\ImageFactory; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Class ImageController. */ class ImageController extends ControllerBase { /** * The image factory. * * @var \Drupal\Core\Image\ImageFactory */ protected $imageFactory; /** * Constructs a ImageController object. * * @param \Drupal\Core\Image\ImageFactory $image_factory * The image factory. */ public function __construct(ImageFactory $image_factory) { $this->imageFactory = $image_factory; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('image.factory') ); } /** * Show an image. * * @param Request $request * @param MediaInterface $media * * @return BinaryFileResponse */ public function show(Request $request, MediaInterface $media) { $query = $request->query; $width = (int) $query->get('width', 500); $height = (int) $query->get('height', 500); $image_style_id = sprintf('%d_%d', $width, $height); $file = $media->field_media_image->entity; $image_uri = $file->getFileUri(); $image_style = ImageStyle::create([ 'name' => $image_style_id, ]); $image_style->addImageEffect([ 'id' => 'image_scale_and_crop', 'weight' => 0, 'data' => [ 'width' => $width, 'height' => $height, ], ]); $derivative_uri = $image_style->buildUri($image_uri); $success = file_exists($derivative_uri) || $image_style->createDerivative($image_uri, $derivative_uri); $headers = []; $image = $this->imageFactory->get($derivative_uri); $uri = $image->getSource(); $headers += [ 'Content-Type' => $image->getMimeType(), 'Content-Length' => $image->getFileSize(), ]; $response = new BinaryFileResponse($uri, 200, $headers); return $response; } }

First we added a new dependency to our controller \Drupal\Core\Image\ImageFactory which allows us to construct an Image instance, where we can get meta data from the image, but also gives us a unified interface to apply things to our image. For instance, we could desaturate the image by doing $image->desaturate(); and then resave the file. Fow now we're only using it to retrieve the meta data. We'll take advantage of that in the next part, when we refactor some of the written code and add more flexibility to what we can dynamically output.

If we hit the url and add both the width and height parameters we'll get something like this:

In the up coming article we'll take a better look at what we have, what we miss (access control, what if a user hits the same URL at the same time), adding more effects, and exploring the use of the Image and toolkit APIs more in depth.

We'll most likely remove adding image effects through ImageStyles and only use the image style for creating derivates that we can we can later apply changes with the toolkit API.

If you want to continue on your own, take a look at ImageStyleDownloadController.php file in core which contains a lot of code that we can re-use.

OpenSense Labs: Best Drupal 8 Security Practices to Follow

Planet Drupal - 11. Juni 2018 - 13:38
Best Drupal 8 Security Practices to Follow Akshita Mon, 06/11/2018 - 17:08

Even though security remains one of the major concerns for an organization, the implication of new technologies has at the same time broadened and complicated the understanding of the term. 

Security is no more about working in isolation. 

Recent events such as Drupalgeddon 2 in March and other subsequent security releases in April – marked critical – have once again brought the question ‘Is Drupal Secure?’ to the center-table. Drupal is among those few open source projects popular for their security with a dedicated team working on to improve it. However, there are still sometimes when the security of your Drupal website is under the impression of threat. 

Security is a vast area of expertise and it is quickly changing with time. No more is it about one person working in isolation or an expert who can understand all the aspects. 

While the list of do’s and don'ts is extensive and exhaustive to keep up with the threats, vulnerabilities and mitigation strategies, here are the top seven Drupal security practices to follow in order to keep up the health of your website. 

And Aristotle once said...

The aim of the wise is not to secure pleasure but, to avoid pain. Seven Drupal 8 Security Practices Securing the Server-side Hosting Environment

Before starting off with the general security hacks and tips, you need to secure your server-side hosting environment. Here are some points to keep in mind before moving to securing your core. 

  1. Protect the server: Only a limited number of users must be allowed to access your server. One of the key points is to add a basic layer by restricting the access to server login details. Once the authentication is set up, it is easier to monitor server access and restricting file access usage. This can help you detect unusual activities.
     
  2. Hide the server signature: Server Signature needs to be hidden as it reveals an important piece of information about the server and operating system. It can let a hacker know if you are using Apache or Linux - information which can be utilized as a vulnerability used to hack the server. In order to keep the server secure from possible vulnerabilities, you need to hide the server signature. 
     
  3. Enable port wise security - Since the applications use the port numbers, it is important to keep certain port numbers hidden from general access. 
Securing the Drupal Core
  • Keep your Core Updated
    A key practice, keeping the core updated will always be the first when listing healthy security practices. And this was the first lesson we learned from the Drupalgeddon2. Always look out for core updates (include the minor releases as well) unless security is not on your agenda. In all of its advisories, the Drupal Security Team asks for updating the core version of the system. 

    If you fall a long ways behind the latest update, you are opening yourself to vulnerabilities. Since history tells us that hackers target the older versions.

    Look out for core updates. Follow the Drupal security team @drupalsecurity on Twitter. Get quick updates and announcements from the team through the emails and security newsletter. You can also follow Security Group in order to contribute and stay part of the security discussions. 

    Another important point to note here is when updating the core - ALWAYS keep a backup of your site's database and codebase. We will discuss this security practice later in the article. 
     
  • Security by Design
    As a matter of fact, every stakeholder wants security to be a simple concept, sadly it isn’t. One of the biggest misconceptions here would be that investing a hefty sum post development would ensure a secure system. However, it is never the case. 

    The best practice to follow is at the architectural level when the website is being designed. 

    Security by Design ensures that designing the software up by the ground to be secured in order to minimize the impact of a vulnerability when discovered. Pacing up your security from the foundation - is the key. It implies following the best security practices at the architectural level instead after building the website. 

    When the foundation of the design remains secure regardless of a reasonable approach adopted later, you can tackle the issues easily. A uniform methodology needs to be adopted to protect the assets from the threats. 

    Once the requirements have been collected, the architecture can be laid out and other elements can be discussed later like trusted execution environment, secure boot, secure software update among others.
"The key to security is eternal vigilance"
  • Use Additional Security Module
    When covering security, there is nothing as better than equipping yourself with more and more. To keep the walls up high, you can use the additional security modules like security kit, captcha, and paranoia. Drupal Security Review can be used as a checklist to test and check for many of the easy-to-make mistakes making your site vulnerable.  

    You can also look out for a List of Must Have Security Modules to prevent you from becoming the next victim of a potential cyber attack. 
     
    • Security kit
      SecKit provides Drupal with various security-hardening options. This lets you mitigate the risks of exploitation of different web application vulnerabilities such as cross-site scripting (XSS), Cross-site request forgery, SSL, Clickjacking and other. 
       
    • Captcha
      A CAPTCHA is a reaction test put in the web structures to eliminate entry by robots. It helps in protecting your website’s contact and sign up forms asking you to prove your credibility as a human with a bizarre sequence of characters, symbols, numbers and sometimes images.

      Often they thwart you from accessing the content. Quite the irony, their purpose is contrary to what we reckon about them.

    • Paranoia 
      Paranoia helps you identify all the vulnerable issues/ places from where a potential leak is possible. It alerts the admin when an attacker tries to evaluate the PHP script via the website interface. 

      It helps in blocking the permission for the user to grant PHP visibility and creation of input formats that use PHP filter.

      It also prevents the website to grant risky permission, mostly the ones that involve leaking the script information.  

    • But Use only Security Team Approved Modules 
      Your site probably uses a number of contributed modules, although that’s not an issue. Using the stable and approved modules is where the key lies. This is especially worth noting for contrib modules which are more susceptible to vulnerability. 

      Always look out for the green batch when downloading a contrib module. Rest, as the advisory reads, Use it at your own risk! An example of security team approved module with a green batch An example of a vulnerable module
    Backing Up - In Case of a Mishappening
    • Keep Up your Backup
      Catastrophes never come invited. While all seems perfect, you might wake up to find out that your website has been taken down by some psychotic hacker. Although it is an unforeseen event, you can definitely arm up yourself.

      As an administrator, you have to be prepared for all of such uninvited events. They can be controlled and the damage minimized by strengthening security, frequent backups, installing updates in a timely manner.  

      We cannot stop disasters but we can arm ourselves with better security and backups. Hosting by Acquia cloud or Pantheon provide automated daily backups of your site’s database, files, and code plus single-click restoration if something goes wrong. 

      You can also use the Backup and Migrate Module or Demo Module because unlike life your Drupal website has the option to go back for some changes. 
    User-Side Security
    • Follow a Standard Practice with a Strong Password Policy
      Passwords are used at both admin and user level, therefore strong and secure passwords are important for your website. When I say strong password should be used I have nothing against short and easy passwords. Easy should never imply less efficient

       A string like Mypassword123 will prove acceptable but is obviously weak and can easily be brute-forced.

      The best practice? Your password should provide realistic strength in terms of measurement and complexity. A password must only be allowed as long as it proves to be of high enough entropy with a combination of characters, alphabets - uppercase and lowercase, symbols, and numbers.

      Start checking passwords on explicit rules and amount of varying character types to be used (symbols, numbers, uppercase letters, etc). 

      Password Strength - a Drupal module - classifies the expected brute-force time for the summed entropy of common underlying patterns in the password. Patterns that can be detected in passwords include words that are found in a dictionary of common words, common first and last names or common passwords. 
    Your password can make the difference between a vulnerable and a hard-to-hack Drupal site.

    While there will always be some new thing to add to the list, you can be sure that this list comprises of the core practices which need to follow. The protocol for communication needs to be clear and well documented. Properly documented procedures are important, as third-party services can often be manipulated.

    In need of a security update or services? Drop a mail at hello@opensenselabs.com and let us help you out. 

    Site builders and developers need to keep an eye open for the possible when security releases are announced and apply them quickly, to ensure the site is not compromised. It is good to be consistent and have your reasoning documented so that it is clearly understood.

    blog banner blog image Blog Type Articles Is it a good read ? On

    Lüfterloser Mini-PC und mobiler USB-C-Beamer mit 720p-Auflösung von Asus

    heise online Newsticker - 11. Juni 2018 - 12:00
    Asus zeigt auf der Computex Mini-PCs der Baureihen PN40 und VC66 sowie den LED-Beamer ZenBeam S2 mit 720p-Auflösung, Akku und USB-C-Eingang.

    Motorola Moto G6 Play mit 4000-mAh-Akku im Test

    heise online Newsticker - 11. Juni 2018 - 12:00
    Das Motorola Moto G6 Play ist ein Smartphone für 199 Euro. Es ist das kleinste Mitglied der Moto-G6-Familie – bietet jedoch mit 4000 mAh den größten Akku.

    Google Pay soll noch im Juni in Deutschland starten

    heise online Newsticker - 11. Juni 2018 - 12:00
    Der Bezahldienst Google Pay soll Ende Juni in Deutschland verfügbar werden, berichtet das Handelsblatt. Damit käme Google Apple Pay zuvor.

    SSDs bis 16 TByte und Micro-SD-Karten bis 512 GByte auf der Computex

    heise online Newsticker - 11. Juni 2018 - 12:00
    Viele Hersteller zeigen auf der Computex neue SSDs und MicroSD-Karten. Einige könnten bald in den Läden liegen, auf andere wird man wohl noch warten müssen.

    Bis 5 GBit/s: USB-Adapter für Multigigabit-Ethernet nach Standard NBase-T

    heise online Newsticker - 11. Juni 2018 - 12:00
    Realteks RTL8156 jagt bis zu 2,5 GBit/s über Kupferkabel und verdoppelt so mal eben den LAN-Durchsatz. Aquantia hat sogar schon einen 5-GBit/s-Pfeil im Köcher.

    BlaBlaCar: Mitfahrdienst lässt Passagiere auf der Route abholen

    heise online Newsticker - 11. Juni 2018 - 12:00
    BlaBlaCar erweitert sein Angebot, um potenzielle Passagiere auf einer Fahrtroute einzusammeln. Autohersteller testen ähnliche Angebote mit eigenen Fahrzeugen.

    Facebook zeigt sich auf der Cebit

    heise online Newsticker - 11. Juni 2018 - 12:00
    Vom 11. bis 15. Juni ist auf der Cebit in Hannover auch das größte soziale Netzwerk präsent. Es zeigt sich dabei unter anderem als Partner von Unternehmen.

    Amazons WM-Angebote am Freitag: Beamer, 4K-TVs, Blu-Rays

    heise online Newsticker - 11. Juni 2018 - 11:30
    Am Freitag der Amazon-WM-Woche sind Fernseher und Beamer reduziert. Wir haben die wenigen wirklich guten Deals herausgesucht und zeigen, wo der Kauf lohnt.

    Google stellt Ethik-Regeln für die Entwicklung künstlicher Intelligenz auf

    heise online Newsticker - 11. Juni 2018 - 11:30
    Die Kritik an der Entwicklung von KI-Algorithmen für das Militär-Projekt Maven hat Google wachgerüttelt und sich für KI-Projekte Verhaltensregeln auferlegt.

    Server-Fernwartung OpenBMC macht Fortschritte

    heise online Newsticker - 11. Juni 2018 - 11:30
    Aspeed und AMI kooperieren in dem von Facebook und IBM vorangetriebenen Projekt für offene Firmware für Baseboard Management Controller.

    Valuebound: How to highlight search results in Search API Solr View in Drupal 8

    Planet Drupal - 11. Juni 2018 - 11:15

    In Search API, there is a field for a search excerpt that you can use on field views to highlight search results. In this article, I’m going to show you how to enable excerpt and set it using views. Here I’m assuming that you have already set the Search API module and has a Search API Solr view.

    Follow the steps:

    Go to Manage -> Configuration -> Search and Metadata -> Search API.

    In your search API index ‘processors’ tab, enable Highlight Processor as shown below.

    Für Wetterberichte und Klimaforschung: Aeolus Windsatellit nach 16 Jahren fertig

    heise online Newsticker - 11. Juni 2018 - 11:00
    Der Windsatellit Aeolus soll Wetterberichte verbessern und der Klimaforschung helfen. Nach jahrelangen technischen Problemen ist er endlich fertig.

    Alexander Gerst erreicht die ISS: Das Andocken im Livestream

    heise online Newsticker - 11. Juni 2018 - 11:00
    Noch sind Alexander Gerst und zwei weitere Astronauten auf dem Weg zur ISS. Dort sollen sie Freitagnachmittag ankommen. Das kann live mitverfolgt werden.

    #TGIQF - das Quiz: Linux-Distributionen

    heise online Newsticker - 11. Juni 2018 - 10:30
    Es gibt weit mehr Linux-Distributionen als Pinguin-Arten. In unserem Quiz können Sie Überblick beweisen. Tux wünscht viel Erfolg!

    Frankreich: Scharfe Kritik an Gesetzesvorschlag gegen "Fake News"

    heise online Newsticker - 11. Juni 2018 - 10:00
    Was tun gegen gezielte Falschinformationen im Internet? In Frankreich soll ein Gesetz neue Regeln für Wahlkampfzeiten aufstellen. Die Opposition schlägt Alarm.

    NativeScript 4.1: Mehr Performance und mehr Features

    heise online Newsticker - 11. Juni 2018 - 10:00
    Das Framework für mobile Anwendungen steht aktuell in der Version 4.1 bereit. Neben der besseren Performance für Android wird nun auch Angular 6 unterstützt.

    Spur von Leben? – Mars-Rover Curiosity findet viel mehr organisches Material

    heise online Newsticker - 11. Juni 2018 - 9:30
    Der Mars-Rover Curiosity hat bei weiteren Analysen viel mehr organisches Material gefunden als zuvor. Das kann, muss aber nicht biologisch entstanden sein.

    RoboCup-WM 2018: Deutscher Meister rechnet sich Chancen aus

    heise online Newsticker - 11. Juni 2018 - 9:30
    Neuer & Co kicken in Russland, die "Naos" in Kanada: Ein Studententeam aus Leipzig hofft bei der WM der Fußball-Roboter auf einen Titel.