Skip to content

Images

Images are an integral part of any website. They can serve as decoration and convey information.

In Ibexa DXP, you can reuse them, normalize their file names, generate different size variations, resize images programmatically, or even define placeholders for missing ones.

Images from DAM systems

If your installation is connected to a DAM system, you can use images directly from a DAM system in your content.

Specific DAM configuration depends on the system that the installation uses.

Reuse images

You can store images in the media library as independent Content items of a generic Image Content Type to reuse them across the system. You do this by uploading images to an ImageAsset Field Type.

For an ImageAsset field to be reused, you must publish it. Only then is notification triggered, which states that an image has been published under the Location and can now be reused. After you establish a media library, you can create Relations between the image Content item and the main Content item that uses it.

Normalizing image file names

If you use image files with unprintable UTF-8 characters in file names, you may come across a problem with images not displaying. Run the following command to normalize image file names:

1
php bin/console ibexa:images:normalize-paths

Next, clear the cache:

1
php bin/console cache:clear

and run the following:

1
php bin/console liip:imagine:cache:remove

Configuring image variations

With image variations (image aliases) you can define and use different versions of the same image. You generate variations based on filters that modify aspects such as size and proportions, quality or effects.

Image variations are generated with LiipImagineBundle, by using the underlying Imagine library from avalanche123.  The LiipImagineBundle bundle supports GD (default), Imagick or Gmagick PHP extensions, and enables you to define flexible filters in PHP.  Image files are stored by using the IOService, and are completely independent from the Image Field Type. They are generated only once and cleared on demand, for example, on content removal).

LiipImagineBundle only works on image blobs, so no command line tool is needed. For more information, see the bundle's documentation.

Code injection in image EXIF

EXIF metadata of an image may contain for example, HTML, JavaScript, or PHP code.  Ibexa DXP is itself does not parse EXIF metadata, but third-party bundles must be secured against this eventuality. Images must be treated like any other user-submitted data - make sure that metadata is properly escaped before use.

Image URL resolution

You can use LiipImagine's liip:imagine:cache:resolve command to resolve the path to image variations that are generated from the original image, with one or more paths as arguments. Paths to repository images must be relative to the var/<site>/storage/images directory, for example: 7/4/2/0/247-1-eng-GB/test.jpg. For more information, see LiipImagineBundle documentation.

Resizing images

You can resize all original images of a chosen Content Type with the following command.

1
php bin/console ibexa:images:resize-original <Field identifier> <Content Type identifier>  -f <variation name>

You must provide the command with:

  • identifier of the image Content Type
  • identifier of the Field that you want to affect
  • name of the image variation to apply to the images

For example:

1
php bin/console ibexa:images:resize-original image photo -f small_image

You can also pass two additional parameters:

  • iteration-count is the number of images to be recreated in a single iteration, to reduce memory use. The default value is 25.
  • user is the identifier of a User with proper permission who will perform the operation (read, versionread, edit and publish). The default value is admin.

Caution

The resize-original command publishes a new version of each Content item it modifies.

Generating placeholder images

With a placeholder generator you can download or generate placeholder images for any missing image. It proves useful when you are working on an existing database and are unable to download uploaded images to your local development environment, due to, for example, a large size of files.

If the original image cannot be resolved, the PlaceholderAliasGenerator::getVariation method generates a placeholder by delegating it to the implementation of the PlaceholderProvider interface, and saves it under the original path.

In Ibexa DXP, there are two implementations of the PlaceholderProvider interface:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?php declare(strict_types=1);

namespace Ibexa\Bundle\Core\Imagine;

use Ibexa\Core\FieldType\Image\Value as ImageValue;

interface PlaceholderProvider
{
    /**
     * Provides a placeholder image path for a given Image FieldType value.
     *
     * @param \Ibexa\Core\FieldType\Image\Value $value
     * @param array $options
     *
     * @return string Path to placeholder
     */
    public function getPlaceholder(ImageValue $value, array $options = []): string;
}

GenericProvider

The GenericProvider package generates placeholders with basic information about the original image (see example 1).

Placeholder image GenericProvider

Placeholder GenericProvider

Option Default value Description Required?
fontpath n/a Path to the font file (*.ttf). Yes
text "IMAGE PLACEHOLDER %width%x%height%\n(%id%)" Text which will be displayed in the image placeholder. %width%, %height%, %id% in it will be replaced with width, height and ID of the original image.
fontsize 20 Size of the font in the image placeholder.
foreground #000000 Foreground color of the placeholder.
secondary #CCCCCC Secondary color of the placeholder.
background #EEEEEE Background color of the placeholder.

RemoteProvider

With the RemoteProvider you can download placeholders from:

Placeholder RemoteProvider - placekitten.com

Option Default value Description
url_pattern '' URL pattern. %width%, %height%, %id% in it will be replaced with width, height and ID of the original image.
timeout 5 Period of time before timeout, measured in seconds.

Semantic configuration

Placeholder generation can be configured for each binary_handler under the ibexa.image_placeholder configuration key:

1
2
3
4
5
6
ibexa:
    # ...
    image_placeholder:
        <BINARY_HANDLER_NAME>:
            provider: <PROVIDER TYPE>
            options:  <CONFIGURATION>

If there is no configuration assigned to the binary_handler, the placeholder generation is disabled.

Configuration examples:

Example 1 - placeholders with basic information about original image

1
2
3
4
5
6
7
8
9
ibexa:
    image_placeholder:
        default:
            provider: generic
            options:
                fontpath: '%kernel.project_dir%/src/Resources/font/font.ttf'
                background: '#EEEEEE'
                foreground: '#FF0000'
                text: 'MISSING IMAGE %%width%%x%%height%%'

Example 2 - placeholders from remote source

1
2
3
4
5
6
ibexa:
    image_placeholder:
        default:
            provider: remote
            options:
                url_pattern: 'https://placekitten.com/%%width%%/%%height%%'

Example 3 - placeholders from live version of a site

1
2
3
4
5
6
ibexa:
    image_placeholder:
        default:
            provider: remote
            options:
                url_pattern: 'http://example.com/var/site/storage/%%id%%'

Support for SVG images

You cannot store SVG images in Ibexa DXP by using the Image or ImageAsset Field Type. However, you can work things around by relying on the File Field Type and implementing a custom extension that lets you display and download files in your templates.

Caution

SVG images may contain JavaScript, so they may introduce XSS or other security vulnerabilities. Make sure end users are not allowed to upload SVG images, and be restrictive about which editors are allowed to do so.

First, enable adding SVG files to content by removing them from the blacklist of allowed MIME types.

To do it, overwrite ibexa.site_access.config.default.io.file_storage.file_type_blacklist defined in Core/Resources/config/default_settings.yml so that svg is removed from the blacklist. You can do it per SiteAccess or SiteAccess group by using SiteAccess-aware configuration.

Then, add a download route to the config/routes.yaml file:

1
2
3
app.svg_download:
    path: /asset/download/{contentId}/{fieldIdentifier}/{filename}
    defaults: { _controller: App\Controller\SvgController::downloadSvgAction }

It points to a custom controller that handles the downloading of the SVG file. The controller's definition (that you place in the config/services.yaml file under services key) and implementation are as follows:

1
2
3
4
5
6
7
8
services:
    # ...
    App\Controller\SvgController:
        public: true
        arguments:
            - '@ibexa.api.service.content'
            - '@ibexa.field_type.ezbinaryfile.io_service'
            - '@Ibexa\Core\Helper\TranslationHelper'
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<?php

declare(strict_types=1);

namespace App\Controller;

use Ibexa\Contracts\Core\Repository\ContentService;
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
use Ibexa\Core\Helper\TranslationHelper;
use Ibexa\Core\IO\IOServiceInterface;
use Ibexa\Core\MVC\Symfony\Controller\Controller;
use InvalidArgumentException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

class SvgController extends Controller
{
    private const CONTENT_TYPE_HEADER = 'image/svg+xml';

    private ContentService $contentService;

    private IOServiceInterface $ioService;

    private TranslationHelper $translationHelper;

    public function __construct(
        ContentService $contentService,
        IOServiceInterface $ioService,
        TranslationHelper $translationHelper
    ) {
        $this->contentService = $contentService;
        $this->ioService = $ioService;
        $this->translationHelper = $translationHelper;
    }

    /**
     * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
     * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException
     * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException
     */
    public function downloadSvgAction(
        int $contentId,
        string $fieldIdentifier,
        string $filename,
        Request $request
    ): Response {
        $version = null;

        if ($request->query->has('version')) {
            $version = $request->query->get('version');
        }

        $content = $this->contentService->loadContent($contentId, null, $version);
        $language = $request->query->has('inLanguage') ? $request->query->get('inLanguage') : null;
        $field = $this->translationHelper->getTranslatedField($content, $fieldIdentifier, $language);

        if (!$field instanceof Field) {
            throw new InvalidArgumentException(
                sprintf(
                    "%s field not present in content %d '%s'",
                    $fieldIdentifier,
                    $content->contentInfo->id,
                    $content->contentInfo->name
                )
            );
        }

        $binaryFile = $this->ioService->loadBinaryFile($field->value->id);
        $response = new Response($this->ioService->getFileContents($binaryFile));
        $disposition = $response->headers->makeDisposition(
            ResponseHeaderBag::DISPOSITION_INLINE,
            $filename
        );

        $response->headers->set('Content-Disposition', $disposition);
        $response->headers->set('Content-Type', self::CONTENT_TYPE_HEADER);

        return $response;
    }
}

To be able to use a proper link in your templates, you also need a dedicated Twig extension:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php

declare(strict_types=1);

namespace App\Twig;

use Symfony\Component\Routing\RouterInterface;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class SvgExtension extends AbstractExtension
{
    protected RouterInterface $router;

    /**
     * SvgExtension constructor.
     */
    public function __construct(RouterInterface $router)
    {
        $this->router = $router;
    }

    /**
     * @return \Twig\TwigFunction[]
     */
    public function getFunctions(): array
    {
        return [
            new TwigFunction('ibexa_svg_link', [
                $this,
                'generateLink',
            ]),
        ];
    }

    public function generateLink(int $contentId, string $fieldIdentifier, string $filename): string
    {
        return $this->router->generate('app.svg_download', [
            'contentId' => $contentId,
            'fieldIdentifier' => $fieldIdentifier,
            'filename' => $filename,
        ]);
    }
}

Now you can load SVG files in your templates by using generated links and a newly created Twig helper:

1
2
3
{% set svgField = ibexa_field(content, 'file') %}

<img src="{{ ibexa_svg_link(content.versionInfo.contentInfo.id, 'file', svgField.value.fileName) }}" alt="">

Image optimization

JPEG images are optimized using the ImageMagic library, which is available out of the box.

If you use other formats, such a PNG, SVG, GIF or WEBP, and you use the Image Editor, to prevent images increasing in size when you modify them in the editor, you need to install additional image handling libraries.

Image format Library
JPEG JpegOptim
PNG Either Optipng or Pngquant 2
SVG SVGO 1
GIF Gifsicle
WEBP cwebp

Install these libraries using your package manager, for example:

1
sudo apt-get install optipng

Embedding images in Rich Text

The RichText field allows you to embed other Content items within the field.

Content items that are identified as images are rendered in the Rich Text Field by using a dedicated template.

You can determine Content Types that will be treated as images and rendered. You do this by overriding the ibexa.content_view.image_embed_content_types_identifiers parameter, for example:

1
2
parameters:
    ibexa.content_view.image_embed_content_types_identifiers: [image, photo, banner]

You can set the template that is used when rendering embedded images in the ibexa.default_view_templates.content.embed_image container parameter:

1
2
parameters:
     ibexa.default_view_templates.content.embed_image: '@ibexadesign/content/view/embed/image.html.twig'