Skip to content
Snippets Groups Projects
Commit ff7b7db3 authored by Jeffrey Fortune's avatar Jeffrey Fortune
Browse files

first push

parents
No related branches found
Tags 8.x-1.0-beta 8.x-1.0-beta1
No related merge requests found
# Background Image Field
## Dependencies
* Token
* Core: Responsive Images
## Installation
Install as you would any other drupal module. See more information [here](https://www.drupal.org/docs/8/extending-drupal-8/installing-drupal-8-modules).
## Configuration
1. Create responsive image style /admin/config/media/responsive-image-style
* The only responsive image style that will be picked up by the field formatter are the ones that have selected a single image style.
2. Add the field on an entity type such as node, paragraph_item or, custom entity.
### Troubleshooting
If you do not see the background image, please make sure to check that the css selector is actually apart of the HTML. The field will not create the selector you choose it already has to exist for it to work.
If you don't see any available responsive image styles in the managed display setting on the entity type you will most likley need to create one following the outline configurations above.
name: Background Image Field
description: Allows you to add responsive background images based on selctors.
core: 8.x
type: module
package: Field types
dependencies:
- core:responsive_image
- token:token
\ No newline at end of file
<?php
use Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_form_alter().
*
* @param $form
* @param \Drupal\Core\Form\FormStateInterface $form_state
* @param $form_id
*/
function bg_img_field_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// remove the cardinality container from the background image field
if ($form_id === 'field_config_edit_form') {
$field_widget_storage = $form_state->getStorage();
$pluginId = $field_widget_storage['default_value_widget']->getPluginId();
if ($pluginId === 'bg_img_field_widget') {
unset($form["default_value"]);
}
}
}
field.formatter.settings.bg_img_field_formatter:
type: mapping
label: 'Image URL formatter settings'
mapping:
image_style:
type: string
label: 'Image style'
field.widget.settings.bg_img_field_widget:
type: mapping
label: 'Default field settings'
mapping:
css_settings:
type: mapping
label: 'CSS settings'
mapping:
css_selector:
type: string
label: 'CSS selector to attach the background image to.'
css_repeat:
type: string
label: 'CSS property to determine repeat value.'
css_background_size:
type: string
label: 'CSS property to determine background size value.'
css_background_position:
type: string
label: 'CSS property to determine background position value'
\ No newline at end of file
<?php
namespace Drupal\bg_img_field\Component\Render;
use Drupal\Component\Render\MarkupInterface;
class CSSSnippet implements MarkupInterface {
/**
* The string to escape.
*
* @var string
*/
protected $string;
/**
* Constructs an HtmlEscapedText object.
*
* @param $string
* The string to escape. This value will be cast to a string.
*/
public function __construct($string) {
$this->string = (string) $string;
}
/**
* {@inheritdoc}
*/
public function __toString() {
return $this->string;
}
/**
* {@inheritdoc}
*/
public function jsonSerialize() {
return $this->__toString();
}
}
<?php
namespace Drupal\bg_img_field\Plugin\Field\FieldFormatter;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\file\Entity\File;
use Drupal\image\Entity\ImageStyle;
use Drupal\responsive_image\Plugin\Field\FieldFormatter\ResponsiveImageFormatter;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\bg_img_field\Component\Render\CSSSnippet;
/**
* Plugin implementation of the 'image' formatter.
*
* @FieldFormatter(
* id = "bg_img_field_formatter",
* label = @Translation("Background Image Field Widget"),
* field_types = {
* "bg_img_field"
* },
* quickedit = {
* "editor" = "image"
* }
* )
*/
class BgImgFieldFormatter extends ResponsiveImageFormatter implements ContainerFactoryPluginInterface {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$elements = parent::settingsForm($form, $form_state);
// get the options for responsive image styles
$options = $elements['responsive_image_style']['#options'];
// new options array for storing new option values
$new_options = [];
// loop through the options to locate only the ones that are labeled
// image styles. This will eliminate any by size styles.
foreach ($options as $key => $option) {
$storage = $this->responsiveImageStyleStorage->load($key);
$image_style_mappings = $storage->get('image_style_mappings');
if (isset($image_style_mappings[0]) && $image_style_mappings[0]['image_mapping_type']
=== 'image_style') {
$new_options = [$key => $option];
}
}
$elements['responsive_image_style']['#options'] = $new_options;
// remove the image link element.
unset($elements['image_link']);
return $elements;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = [];
$responsive_image_style = $this->responsiveImageStyleStorage->load($this->getSetting('responsive_image_style'));
if ($responsive_image_style) {
$summary[] = t('Responsive image style: @responsive_image_style', ['@responsive_image_style' => $responsive_image_style->label()]);
}
else {
$summary[] = t('Select a responsive image style.');
}
return $summary;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
$entity = $items->getEntity();
// Load the files to render.
$files = [];
foreach ($items->getValue() as $item) {
$files[] = [
'file' => File::load($item['target_id']),
'item' => $item
];
}
// Early opt-out if the field is empty.
if (empty($files)) {
return $elements;
}
return $this->build_element($files, $entity);
}
/**
* Build the inline css style based on a set of files and a selector.
*
* @param $files
* @param \Drupal\Core\Entity\EntityInterface $entity
* The parent entity the field belongs to. Used for token replacement in the
* selector.
*
* @return array
*/
protected function build_element($files, $entity) {
$elements = [];
$css = "";
// Collect cache tags to be added for each item in the field.
$responsive_image_style = $this->responsiveImageStyleStorage->load($this->getSetting('responsive_image_style'));
$image_styles_to_load = [];
$cache_tags = [];
if ($responsive_image_style) {
$cache_tags = Cache::mergeTags($cache_tags, $responsive_image_style->getCacheTags());
$image_styles_to_load = $responsive_image_style->getImageStyleIds();
}
// get image styles
$image_styles = $this->imageStyleStorage->loadMultiple($image_styles_to_load);
foreach ($image_styles as $image_style) {
$cache_tags = Cache::mergeTags($cache_tags, $image_style->getCacheTags());
}
// process the files to get the css markup
foreach ($files as $file) {
$selector = $file['item']['css_selector'];
$selector = \Drupal::token()->replace($selector, [$entity->getEntityTypeId() => $entity], ['clear' => TRUE]);
$css .= $this->generate_background_css(
$file['file'],
$responsive_image_style,
$selector,
$file['item']
);
// attach to head on element to create style tag in the html head.
if (!empty($css)) {
// Use the selector in the id to avoid collisions with multiple background
// formatters on the same page.
$id = 'picture-background-formatter-' . $selector;
$elements['#attached']['html_head'][] = [[
'#tag' => 'style',
'#value' => new CSSSnippet($css),
], $id];
}
}
return $elements;
}
/**
* CSS Generator Helper Function.
*
* @param ImageItem $image
* URI of the field image.
* @param string $responsive_image_style
* Desired picture mapping to generate CSS.
* @param string $selector
* CSS selector to target.
* @param array $options
* CSS options.
* @return string
* Generated background image CSS.
*
*/
protected function generate_background_css($image, $responsive_image_style, $selector, $options) {
$css = "";
$css .= $selector . '{';
$css .= "background-repeat: " . $options['css_repeat'] .";";
$css .= "background-size: " . $options['css_background_size'] .";";
$css .= "background-position: " . $options['css_background_position'] .";";
$css .= '}';
$breakpoints = \Drupal::service('breakpoint.manager')->getBreakpointsByGroup($responsive_image_style->getBreakpointGroup());
foreach (array_reverse($responsive_image_style->getKeyedImageStyleMappings()) as $breakpoint_id => $multipliers) {
if (isset($breakpoints[$breakpoint_id])) {
$multipliers = array_reverse($multipliers);
$query = $breakpoints[$breakpoint_id]->getMediaQuery();
if ($query != "") {
$css .= ' @media ' . $query . ' {';
}
foreach ($multipliers as $multiplier => $mapping) {
$multiplier = rtrim($multiplier, "x");
if($mapping['image_mapping_type'] != 'image_style') {
continue;
}
if ($mapping['image_mapping'] == "_original image_") {
$url = file_create_url($image->getFileUri());
}
else {
$url = ImageStyle::load($mapping['image_mapping'])->buildUrl($image->getFileUri());
}
if ($multiplier != 1) {
$css .= ' @media (-webkit-min-device-pixel-ratio: ' . $multiplier . '), (min-resolution: ' . $multiplier * 96 . 'dpi), (min-resolution: ' . $multiplier . 'dppx) {';
}
$css .= $selector . ' {background-image: url(' . $url . ');}';
if ($multiplier != 1) {
$css .= '}';
}
}
if ($query != "") {
$css .= '}';
}
}
}
return $css;
}
}
<?php
namespace Drupal\bg_img_field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\image\Plugin\Field\FieldType\ImageItem;
/**
* Plugin implementation of the 'bg_img_field' field type.
*
* @FieldType(
* id = "bg_img_field",
* label = @Translation("Background Image Field"),
* description = @Translation("Field for creating responsive background
* images."), default_widget = "bg_img_field_widget", default_formatter =
* "bg_img_field_formatter"
* )
*/
class BgImgItem extends ImageItem {
public static function defaultStorageSettings() {
$settings = parent::defaultStorageSettings();
$settings['css_settings']['css_selector'] = '';
$settings['css_settings']['css_repeat'] = 'inherit';
$settings['css_settings']['css_background_size'] = 'inherit';
$settings['css_settings']['css_background_position'] = 'inherit';
return $settings;
}
/**
* {@inheritdoc}
*/
public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
$elements = parent::storageSettingsForm($form, $form_state, $has_data);
// remove title and alt from the setting form, they are not need
// in background images.
unset($elements['default_image']['alt']);
unset($elements['default_image']['title']);
return $elements;
}
/**
* {@inheritdoc}
*/
public static function defaultFieldSettings() {
$settings = parent::defaultFieldSettings();
// change value of setting set in image field
$settings['file_extensions'] = "png jpg jpeg svg";
$settings['alt_field'] = 0;
$settings['alt_field_required'] = 0;
// add the specific css settings.
$settings['css_settings']['css_selector'] = '';
$settings['css_settings']['css_repeat'] = 'inherit';
$settings['css_settings']['css_background_size'] = 'inherit';
$settings['css_settings']['css_background_position'] = 'inherit';
return $settings;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
$schema = parent::schema($field_definition);
$schema['columns']['css_selector'] = [
'description' => "CSS selector to target the background image placement.",
'type' => 'text',
];
$schema['columns']['css_repeat'] = [
'description' => "CSS property that determines the repeat attribute.",
'type' => 'varchar',
'length' => 255,
];
$schema['columns']['css_background_size'] = [
'description' => "CSS property that determines the background size attribute.",
'type' => 'varchar',
'length' => 255,
];
$schema['columns']['css_background_position'] = [
'description' => "CSS property that determines the background position attribute.",
'type' => 'varchar',
'length' => 255,
];
return $schema;
}
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties = parent::propertyDefinitions($field_definition);
$properties['css_selector'] = DataDefinition::create('string')
->setLabel(t('CSS Selector'))
->setDescription(t("CSS selector that will be used to place the background image. attribute."));
$properties['css_repeat'] = DataDefinition::create('string')
->setLabel(t('CSS Repeat Property'))
->setDescription(t("CSS attribute that set the repeating value of the background image."));
$properties['css_background_size'] = DataDefinition::create('string')
->setLabel(t('CSS Background Size Property'))
->setDescription(t("CSS attribute that set the background size value of the background image."));
$properties['css_background_position'] = DataDefinition::create('string')
->setLabel(t('CSS Background Position Property'))
->setDescription(t("CSS attribute that set the background position value of the background image."));
return $properties;
}
/**
* {@inheritdoc}
*/
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
$parentElements = parent::fieldSettingsForm($form, $form_state);
// unset fields from image field that will not be used.
unset($parentElements['alt_field']);
unset($parentElements['alt_field_required']);
unset($parentElements['title_field']);
unset($parentElements['title_field_required']);
// unset to clean up the UI.
unset($parentElements['default_image']['alt']);
unset($parentElements['default_image']['title']);
$elements['css_settings'] = [
'#type' => 'details',
'#title' => t('CSS Settings'),
'#description' => 'Set default CSS properties for the background image.',
'#open' => FALSE
];
// load tokens based on the entity type it is on.
$token_types = [$this->getFieldDefinition()->getTargetEntityTypeId()];
// Get defined settings
$css_option_settings = $this->getSetting('css_settings');
// The css selector input field needed to
$elements['css_settings']['css_selector'] = array(
'#type' => 'textfield',
'#title' => t('Selector'),
'#description' => t('CSS Selector for background image.'),
'#default_value' => $css_option_settings['css_selector'],
'#token_types' => $token_types,
'#element_validate' => 'token_element_validate',
);
// The tokens that are scoped for the selector input.
$elements['css_settings']['tokens'] = [
'#theme' => 'token_tree_link',
'#token_types' => $token_types,
'#global_types' => TRUE,
'#show_nested' => FALSE,
];
// User ability to select a background repeat option.
$elements['css_settings']['css_repeat'] = [
'#type' => 'radios',
'#title' => t('Repeat Options'),
'#description' => t('Add the css no repeat value to the background image.'),
'#default_value' => $css_option_settings['css_repeat'],
'#options' => [
"inherit" => t("inherit"),
"no-repeat" => t("no-repeat"),
"repeat" => t('repeat'),
]
];
// User the ability to choose background size.
$elements['css_settings']['css_background_size'] = [
'#type' => 'radios',
'#title' => t('Background Size'),
'#description' => t("Add the background size setting you would like for the image, use inherit for default."),
'#default_value' => $css_option_settings['css_background_size'],
'#options' => [
'inherit' => t('inherit'),
'auto' => t('auto'),
'cover' => t('cover'),
'contain' => t('contain'),
'initial' => t('initial'),
]
];
// User the ability to set the background position.
$elements['css_settings']['css_background_position'] = [
'#type' => 'radios',
'#title' => t('Background Position'),
'#description' => t('Set a background position, leave unchecked to have your own in your theme css.'),
'#default_value' => $css_option_settings['css_background_position'],
'#options' => [
"inherit" => t("inherit"),
"left top" => t("left top"),
"left center" => t("left center"),
"left bottom" => t("left bottom"),
"right top" => t("right bottom"),
"right center" => t("right center"),
"right bottom" => t("right bottom"),
"center top" => t("center top"),
"center center" => t("center center"),
"center bottom" => t("center bottom")
],
'#tree' => TRUE,
];
$elements['file_settings'] = [
'#type' => 'details',
'#title' => t("File Settings"),
'#open' => FALSE,
] + $parentElements;
return $elements;
}
}
\ No newline at end of file
<?php
namespace Drupal\bg_img_field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\image\Plugin\Field\FieldWidget\ImageWidget;
/**
* Plugin implementation of the 'image_image' widget.
*
* @FieldWidget(
* id = "bg_img_field_widget",
* label = @Translation("Background Image Field Widget"),
* field_types = {
* "bg_img_field"
* }
* )
*/
class BgImageFieldWidget extends ImageWidget {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
$settings = parent::defaultSettings();
// add the specific css settings.
$settings['css_settings']['css_selector'] = '';
$settings['css_settings']['css_repeat'] = 'inherit';
$settings['css_settings']['css_background_size'] = 'inherit';
$settings['css_settings']['css_background_position'] = 'inherit';
return $settings;
}
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$elements = parent::formElement($items, $delta, $element, $form, $form_state);
$elements['#upload_validators']['file_validate_extensions'][0] =
$this->getFieldSetting('file_extensions');
return $elements;
}
/**
* {@inheritdoc}
*/
public static function processMultiple($element, FormStateInterface $form_state, $form) {
return parent::processMultiple($element, $form_state, $form); // TODO: Change the autogenerated stub
}
/**
* {@inheritdoc}
*/
public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
$new_values = [];
foreach ($values as &$value) {
$new_value = $value;
$new_value['css_selector'] = $value['css_settings']['css_selector'];
$new_value['css_repeat'] = $value['css_settings']['css_repeat'];
$new_value['css_background_size'] = $value['css_settings']['css_background_size'];
$new_value['css_background_position'] = $value['css_settings']['css_background_position'];
foreach ($value['fids'] as $fid) {
$new_value['target_id'] = $fid;
unset($new_value['fids']);
}
unset($new_value['css_settings']);
$new_values[] = $new_value;
}
return $new_values;
}
/**
* {@inheritdoc}
*/
public static function process($element, FormStateInterface $form_state, $form) {
$elements = parent::process($element, $form_state, $form);
$entity = $form_state->getformObject()->getEntity();
$field_settings = $entity->getFieldDefinition($element['#field_name'])
->getSettings();
$css_option_settings = $field_settings['css_settings'];
if ($element['#files']) {
$elements['css_settings'] = [
'#type' => 'details',
'#title' => t('CSS Settings'),
'#description' => 'Set default CSS properties for the background image.',
'#open' => FALSE
];
$token_types = [$entity->getEntityType()->id()];
// Background selector value.
$selector = !empty($element['#value']['css_selector']) ?
$element['#value']['css_selector'] : $css_option_settings['css_selector'];
// The css selector input field needed to
$elements['css_settings']['css_selector'] = [
'#type' => 'textfield',
'#title' => t('Selector'),
'#description' => t('CSS Selector for background image.'),
'#default_value' => $selector,
'#token_types' => $token_types,
'#element_validate' => 'token_element_validate',
'#required' => TRUE,
];
// The tokens that are scoped for the selector input.
$elements['css_settings']['tokens'] = [
'#theme' => 'token_tree_link',
'#token_types' => $token_types,
'#global_types' => TRUE,
'#show_nested' => FALSE,
];
// Background repeat value
$repeat = !empty($element['#value']['css_selector']) ?
$element['#value']['css_repeat']
: $css_option_settings['css_repeat'];
// User ability to select a background repeat option.
$elements['css_settings']['css_repeat'] = [
'#type' => 'radios',
'#title' => t('Repeat Options'),
'#description' => t('Add the css no repeat value to the background image.'),
'#default_value' => $repeat,
'#options' => [
"inherit" => t("inherit"),
"no-repeat" => t("no-repeat"),
"repeat" => t('repeat'),
]
];
// Background size value
$background_size = !empty($element['#value']['css_selector']) ?
$element['#value']['css_background_size']
: $css_option_settings['css_background_size'];
// User the ability to choose background size.
$elements['css_settings']['css_background_size'] = [
'#type' => 'radios',
'#title' => t('Background Size'),
'#description' => t("Add the background size setting you would like for the image, use inherit for default."),
'#default_value' => $background_size,
'#options' => [
'inherit' => t('inherit'),
'auto' => t('auto'),
'cover' => t('cover'),
'contain' => t('contain'),
'initial' => t('initial'),
]
];
// Background position values.
$background_pos = !empty($element['#value']['css_selector']) ?
$element['#value']['css_background_position']
: $css_option_settings['css_background_position'];
// User the ability to set the background position.
$elements['css_settings']['css_background_position'] = [
'#type' => 'radios',
'#title' => t('Background Position'),
'#description' => t('Set a background position, leave unchecked to have your own in your theme css.'),
'#default_value' => $background_pos,
'#options' => [
"inherit" => t("inherit"),
"left top" => t("left top"),
"left center" => t("left center"),
"left bottom" => t("left bottom"),
"right top" => t("right bottom"),
"right center" => t("right center"),
"right bottom" => t("right bottom"),
"center top" => t("center top"),
"center center" => t("center center"),
"center bottom" => t("center bottom")
],
'#tree' => TRUE,
];
}
return $elements;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment