View source
<?php
declare (strict_types=1);
namespace Drupal\bg_image_formatter\Plugin\Field\FieldFormatter;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Ajax\AjaxHelperTrait;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\image\Plugin\Field\FieldFormatter\ImageFormatter;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
class BgImageFormatter extends ImageFormatter {
use AjaxHelperTrait;
protected $renderer;
protected $request;
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, AccountInterface $current_user, EntityStorageInterface $image_style_storage, RendererInterface $renderer, Request $request) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings, $current_user, $image_style_storage);
$this->renderer = $renderer;
$this->request = $request;
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['label'], $configuration['view_mode'], $configuration['third_party_settings'], $container
->get('current_user'), $container
->get('entity_type.manager')
->getStorage('image_style'), $container
->get('renderer'), $container
->get('request_stack')
->getCurrentRequest());
}
public static function defaultSettings() {
return [
'image_style' => '',
'css_settings' => [
'bg_image_selector' => 'body',
'bg_image_color' => '#FFFFFF',
'bg_image_x' => 'left',
'bg_image_y' => 'top',
'bg_image_attachment' => 'scroll',
'bg_image_repeat' => 'no-repeat',
'bg_image_background_size' => '',
'bg_image_background_size_ie8' => 0,
'bg_image_gradient' => '',
'bg_image_media_query' => 'all',
'bg_image_important' => 1,
'bg_image_z_index' => 'auto',
'bg_image_path_format' => 'absolute',
],
];
}
public function getBackgroundImageCss($image_path, array $css_settings = [], $image_style = NULL) {
$defaults = self::defaultSettings();
$css_settings += $defaults['css_settings'];
$selector = Xss::filter($css_settings['bg_image_selector']);
$bg_color = Xss::filter($css_settings['bg_image_color']);
$bg_x = Xss::filter($css_settings['bg_image_x']);
$bg_y = Xss::filter($css_settings['bg_image_y']);
$attachment = $css_settings['bg_image_attachment'];
$repeat = $css_settings['bg_image_repeat'];
$important = $css_settings['bg_image_important'];
$background_size = Xss::filter($css_settings['bg_image_background_size']);
$background_size_ie8 = $css_settings['bg_image_background_size_ie8'];
$background_gradient = !empty($css_settings['bg_image_gradient']) ? $css_settings['bg_image_gradient'] . ',' : '';
$media_query = isset($css_settings['bg_image_media_query']) ? Xss::filter($css_settings['bg_image_media_query']) : NULL;
$z_index = Xss::filter($css_settings['bg_image_z_index']);
if ($important) {
$important = '!important';
}
else {
$important = '';
}
$bg_size = '';
$ie_bg_size = '';
if ($background_size) {
$bg_size = sprintf('background-size: %s %s;', $background_size, $important);
$bg_size .= sprintf('-webkit-background-size: %s %s;', $background_size, $important);
$bg_size .= sprintf('-moz-background-size: %s %s;', $background_size, $important);
$bg_size .= sprintf('-o-background-size: %s %s;', $background_size, $important);
if ($background_size === 'cover' && $background_size_ie8) {
$ie_bg_size = sprintf("filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%s', sizingMethod='scale');", $image_path);
$ie_bg_size .= sprintf("-ms-filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%s', sizingMethod='scale');", $image_path);
}
}
if ($selector && $image_path) {
$style = sprintf('%s {', $selector);
if ($bg_color) {
$style .= sprintf('background-color: %s %s;', $bg_color, $important);
}
$style .= sprintf("background-image: %s url('%s') %s;", $background_gradient, $image_path, $important);
if ($repeat) {
$style .= sprintf('background-repeat: %s %s;', $repeat, $important);
}
if ($attachment) {
$style .= sprintf('background-attachment: %s %s;', $attachment, $important);
}
if ($bg_x && $bg_y) {
$style .= sprintf('background-position: %s %s %s;', $bg_x, $bg_y, $important);
}
if ($z_index) {
$style .= sprintf('z-index: %s;', $z_index);
}
$style .= $bg_size;
$style .= $background_size_ie8 ? $ie_bg_size : '';
$style .= '}';
return [
'data' => $style,
'media' => !empty($media_query) ? $media_query : 'all',
'group' => CSS_THEME,
];
}
return [];
}
public function settingsForm(array $form, FormStateInterface $form_state) {
$element = [];
$settings = $this
->getSettings();
$element['image_style'] = [
'#title' => $this
->t('Image style'),
'#type' => 'select',
'#default_value' => $settings['image_style'],
'#empty_option' => $this
->t('None (original image)'),
'#options' => image_style_options(),
'#description' => $this
->t('Select <a href="@href_image_style">the image style</a> to apply on images', [
'@href_image_style' => Url::fromRoute('image.style_add')
->toString(),
]),
];
$element['css_settings'] = [
'#type' => 'fieldset',
'#title' => $this
->t('Default CSS Settings'),
'#description' => $this
->t('Default CSS settings for outputting the background property.
These settings will be concatenated to form a complete css statementthat uses the "background"
property. For more information on the css background property see
http://www.w3schools.com/css/css_background.asp"'),
];
$element['css_settings']['bg_image_selector'] = [
'#type' => 'textarea',
'#title' => $this
->t('Selector(s)'),
'#description' => $this
->t('A valid CSS selector that will be used to apply the background image. One per line.
If the field is a multivalue field, the first line will be applied to the first value,
the second to the second value... and so on. Tokens are supported.'),
'#default_value' => $settings['css_settings']['bg_image_selector'],
];
if (isset($form['#entity_type'])) {
$element['css_settings']['token_help'] = [
'#theme' => 'token_tree_link',
'#token_types' => [
'user',
$form['#entity_type'],
],
];
}
else {
$element['css_settings']['token_help'] = [
'#theme' => 'token_tree_link',
];
}
$element['css_settings']['bg_image_z_index'] = [
'#type' => 'textfield',
'#title' => $this
->t('Z Index'),
'#description' => $this
->t('The z-index property specifies the stack order of an element. An element with greater stack order is
always in front of an element with a lower stack order. Note: z-index only works on positioned
elements (position:absolute, position:relative, or position:fixed)'),
'#default_value' => $settings['css_settings']['bg_image_z_index'],
];
$element['css_settings']['bg_image_color'] = [
'#type' => 'textarea',
'#title' => $this
->t('Color'),
'#description' => $this
->t('The background color formatted as any valid css color format (e.g. hex, rgb, text, hsl)
[<a href="@url">css property: background-color</a>]. One per line. If the field is a multivalue
field, the first line will be applied to the first value, the second to the second value...
and so on.', [
'@url' => 'https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient',
]),
'#default_value' => $settings['css_settings']['bg_image_color'],
];
$element['css_settings']['bg_image_x'] = [
'#type' => 'textfield',
'#title' => $this
->t('Horizontal Alignment'),
'#description' => $this
->t('The horizontal alignment of the background image formatted as any valid css alignment.
[<a href="http://www.w3schools.com/css/pr_background-position.asp">
css property: background-position
</a>]'),
'#default_value' => $settings['css_settings']['bg_image_x'],
];
$element['css_settings']['bg_image_y'] = [
'#type' => 'textfield',
'#title' => $this
->t('Vertical Alignment'),
'#description' => $this
->t('The vertical alignment of the background image formatted as any valid css alignment.
[<a href="http://www.w3schools.com/css/pr_background-position.asp">
css property: background-position
</a>]'),
'#default_value' => $settings['css_settings']['bg_image_y'],
];
$element['css_settings']['bg_image_attachment'] = [
'#type' => 'radios',
'#title' => $this
->t('Background Attachment'),
'#description' => $this
->t('The attachment setting for the background image.
[<a href="http://www.w3schools.com/css/pr_background-attachment.asp">
css property: background-attachment
</a>]'),
'#options' => [
FALSE => $this
->t('Ignore'),
'scroll' => 'Scroll',
'fixed' => 'Fixed',
],
'#default_value' => $settings['css_settings']['bg_image_attachment'],
];
$element['css_settings']['bg_image_repeat'] = [
'#type' => 'radios',
'#title' => $this
->t('Background Repeat'),
'#description' => $this
->t('Define the repeat settings for the background image.
[<a href="http://www.w3schools.com/css/pr_background-repeat.asp">
css property: background-repeat
</a>]'),
'#options' => [
FALSE => $this
->t('Ignore'),
'no-repeat' => $this
->t('No Repeat'),
'repeat' => $this
->t('Tiled (repeat)'),
'repeat-x' => $this
->t('Repeat Horizontally (repeat-x)'),
'repeat-y' => $this
->t('Repeat Vertically (repeat-y)'),
],
'#default_value' => $settings['css_settings']['bg_image_repeat'],
];
$element['css_settings']['bg_image_background_size'] = [
'#type' => 'textfield',
'#title' => $this
->t('Background Size'),
'#description' => $this
->t('The size of the background (NOTE: CSS3 only. Useful for responsive designs)
[<a href="http://www.w3schools.com/cssref/css3_pr_background-size.asp">
css property: background-size
</a>]'),
'#default_value' => $settings['css_settings']['bg_image_background_size'],
];
$element['css_settings']['bg_image_background_size_ie8'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Add background-size:cover support for ie8'),
'#description' => $this
->t('The background-size css property is only supported on browsers that support CSS3.
However, there is a workaround for IE using Internet Explorer\'s built-in filters
(http://msdn.microsoft.com/en-us/library/ms532969%28v=vs.85%29.aspx).
Check this box to add the filters to the css. Sometimes it works well, sometimes it doesn\'t.
Use at your own risk'),
'#default_value' => $settings['css_settings']['bg_image_background_size_ie8'],
];
$element['css_settings']['bg_image_gradient'] = [
'#type' => 'textfield',
'#title' => $this
->t('Gradient'),
'#description' => $this
->t('Apply this background image gradient css.
Example: linear-gradient(red, yellow)
[<a href="https://www.w3schools.com/css/css3_gradients.asp">Read about gradients</a>]'),
'#default_value' => $settings['css_settings']['bg_image_gradient'],
];
$element['css_settings']['bg_image_media_query'] = [
'#type' => 'textfield',
'#title' => $this
->t('Media Query'),
'#description' => $this
->t('Apply this background image css using a media query. CSS3 Only. Useful for responsive designs.
Example: only screen and (min-width:481px) and (max-width:768px)
[<a href="http://www.w3.org/TR/css3-mediaqueries/">Read about media queries</a>]'),
'#default_value' => $settings['css_settings']['bg_image_media_query'],
];
$element['css_settings']['bg_image_important'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Add "!important" to the background property.'),
'#description' => $this
->t('This can be helpful to override any existing background image or color properties added by the theme.'),
'#default_value' => $settings['css_settings']['bg_image_important'],
];
$element['css_settings']['bg_image_path_format'] = [
'#type' => 'select',
'#title' => $this
->t('Image Path Format'),
'#options' => [
'absolute' => $this
->t('Absolute'),
'relative' => $this
->t('Relative'),
],
'#description' => $this
->t('Defaults to absolute URLs, however relative URLs maybe solve issues with mixed content errors on websites being served on HTTPS.'),
'#default_value' => $settings['css_settings']['bg_image_path_format'],
];
return $element;
}
public function settingsSummary() {
$summary = [];
$settings = $this
->getSettings();
$image_styles = image_style_options(FALSE);
unset($image_styles['']);
if (isset($settings['css_settings']['bg_image_selector'])) {
$summary[] = $this
->t('CSS Selector: @selector', [
'@selector' => $settings['css_settings']['bg_image_selector'],
]);
}
else {
$summary[] = $this
->t('No selector');
}
if (isset($image_styles[$settings['image_style']])) {
$summary[] = $this
->t('URL for image style: @style', [
'@style' => $image_styles[$settings['image_style']],
]);
}
else {
$summary[] = $this
->t('Original image style');
}
return $summary;
}
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
$settings = $this
->getSettings();
$css_settings = $settings['css_settings'];
$selector = $css_settings['bg_image_selector'];
$token_start = mb_strpos($selector, '{{');
if ($token_start !== FALSE) {
$token_length = mb_strpos($selector, '}}') - mb_strpos($selector, '{{') + 2;
$token = mb_substr($selector, mb_strpos($selector, '{{'), $token_length);
$cleaned_token = trim(str_replace([
'{{',
'}}',
], '', $token));
$entity = $items
->getEntity();
if ($entity->{$cleaned_token} && $entity->{$cleaned_token}->value) {
$selector = str_replace($token, $entity->{$cleaned_token}->value, $selector);
$css_settings['bg_image_selector'] = $selector;
}
}
$image_style = $settings['image_style'] ? $settings['image_style'] : NULL;
$path_format = $css_settings['bg_image_path_format'];
$selectors = explode(\PHP_EOL, trim($css_settings['bg_image_selector']));
$colors = explode(\PHP_EOL, trim($css_settings['bg_image_color']));
$selectors = array_map(static function ($value) {
return trim($value, ',');
}, $selectors);
$colors = array_filter(array_map('trim', $colors));
$files = $this
->getEntitiesToView($items, $langcode);
if (empty($files)) {
return $elements;
}
$token_data = [
'user' => \Drupal::currentUser(),
$items
->getEntity()
->getEntityTypeId() => $items
->getEntity(),
];
foreach ($selectors as &$selector) {
$selector = \Drupal::token()
->replace($selector, $token_data);
}
$elements[0] = [];
foreach ($files as $delta => $file) {
$css_settings['bg_image_selector'] = $selectors[$delta % \count($selectors)];
if ($colors) {
$css_settings['bg_image_color'] = $colors[$delta % \count($colors)];
}
if ($image_style) {
$style = $this->imageStyleStorage
->load($image_style);
if (!$style) {
throw new \Exception("Image style not found: {$image_style}");
}
$file_url = $style
->buildUrl($file
->getFileUri());
}
else {
$file_url = file_create_url($file
->getFileUri());
}
$image_url = $path_format === 'absolute' ? $file_url : file_url_transform_relative($file_url);
$elements['#attached']['css'] = [
$this
->getBackgroundImageCss($image_url, $css_settings),
];
}
return $elements;
}
}