3 namespace Drupal\image\Plugin\Field\FieldFormatter;
5 use Drupal\Core\Entity\EntityStorageInterface;
6 use Drupal\Core\Field\FieldItemListInterface;
7 use Drupal\Core\Field\FieldDefinitionInterface;
9 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
10 use Drupal\Core\Session\AccountInterface;
12 use Drupal\image\Entity\ImageStyle;
13 use Symfony\Component\DependencyInjection\ContainerInterface;
14 use Drupal\Core\Form\FormStateInterface;
15 use Drupal\Core\Cache\Cache;
18 * Plugin implementation of the 'image' formatter.
22 * label = @Translation("Image"),
31 class ImageFormatter extends ImageFormatterBase implements ContainerFactoryPluginInterface {
36 * @var \Drupal\Core\Session\AccountInterface
38 protected $currentUser;
41 * The image style entity storage.
43 * @var \Drupal\image\ImageStyleStorageInterface
45 protected $imageStyleStorage;
48 * Constructs an ImageFormatter object.
50 * @param string $plugin_id
51 * The plugin_id for the formatter.
52 * @param mixed $plugin_definition
53 * The plugin implementation definition.
54 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
55 * The definition of the field to which the formatter is associated.
56 * @param array $settings
57 * The formatter settings.
58 * @param string $label
59 * The formatter label display setting.
60 * @param string $view_mode
62 * @param array $third_party_settings
63 * Any third party settings settings.
64 * @param \Drupal\Core\Session\AccountInterface $current_user
66 * @param \Drupal\Core\Entity\EntityStorageInterface $image_style_storage
67 * The image style storage.
69 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) {
70 parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
71 $this->currentUser = $current_user;
72 $this->imageStyleStorage = $image_style_storage;
78 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
82 $configuration['field_definition'],
83 $configuration['settings'],
84 $configuration['label'],
85 $configuration['view_mode'],
86 $configuration['third_party_settings'],
87 $container->get('current_user'),
88 $container->get('entity.manager')->getStorage('image_style')
95 public static function defaultSettings() {
99 ] + parent::defaultSettings();
105 public function settingsForm(array $form, FormStateInterface $form_state) {
106 $image_styles = image_style_options(FALSE);
107 $description_link = Link::fromTextAndUrl(
108 $this->t('Configure Image Styles'),
109 Url::fromRoute('entity.image_style.collection')
111 $element['image_style'] = [
112 '#title' => t('Image style'),
114 '#default_value' => $this->getSetting('image_style'),
115 '#empty_option' => t('None (original image)'),
116 '#options' => $image_styles,
117 '#description' => $description_link->toRenderable() + [
118 '#access' => $this->currentUser->hasPermission('administer image styles')
122 'content' => t('Content'),
125 $element['image_link'] = [
126 '#title' => t('Link image to'),
128 '#default_value' => $this->getSetting('image_link'),
129 '#empty_option' => t('Nothing'),
130 '#options' => $link_types,
139 public function settingsSummary() {
142 $image_styles = image_style_options(FALSE);
143 // Unset possible 'No defined styles' option.
144 unset($image_styles['']);
145 // Styles could be lost because of enabled/disabled modules that defines
146 // their styles in code.
147 $image_style_setting = $this->getSetting('image_style');
148 if (isset($image_styles[$image_style_setting])) {
149 $summary[] = t('Image style: @style', ['@style' => $image_styles[$image_style_setting]]);
152 $summary[] = t('Original image');
156 'content' => t('Linked to content'),
157 'file' => t('Linked to file'),
159 // Display this setting only if image is linked.
160 $image_link_setting = $this->getSetting('image_link');
161 if (isset($link_types[$image_link_setting])) {
162 $summary[] = $link_types[$image_link_setting];
171 public function viewElements(FieldItemListInterface $items, $langcode) {
173 $files = $this->getEntitiesToView($items, $langcode);
175 // Early opt-out if the field is empty.
181 $image_link_setting = $this->getSetting('image_link');
182 // Check if the formatter involves a link.
183 if ($image_link_setting == 'content') {
184 $entity = $items->getEntity();
185 if (!$entity->isNew()) {
186 $url = $entity->urlInfo();
189 elseif ($image_link_setting == 'file') {
193 $image_style_setting = $this->getSetting('image_style');
195 // Collect cache tags to be added for each item in the field.
196 $base_cache_tags = [];
197 if (!empty($image_style_setting)) {
198 $image_style = $this->imageStyleStorage->load($image_style_setting);
199 $base_cache_tags = $image_style->getCacheTags();
202 foreach ($files as $delta => $file) {
203 $cache_contexts = [];
204 if (isset($link_file)) {
205 $image_uri = $file->getFileUri();
206 // @todo Wrap in file_url_transform_relative(). This is currently
207 // impossible. As a work-around, we currently add the 'url.site' cache
208 // context to ensure different file URLs are generated for different
209 // sites in a multisite setup, including HTTP and HTTPS versions of the
210 // same site. Fix in https://www.drupal.org/node/2646744.
211 $url = Url::fromUri(file_create_url($image_uri));
212 $cache_contexts[] = 'url.site';
214 $cache_tags = Cache::mergeTags($base_cache_tags, $file->getCacheTags());
216 // Extract field item attributes for the theme function, and unset them
217 // from the $item so that the field template does not re-render them.
218 $item = $file->_referringItem;
219 $item_attributes = $item->_attributes;
220 unset($item->_attributes);
222 $elements[$delta] = [
223 '#theme' => 'image_formatter',
225 '#item_attributes' => $item_attributes,
226 '#image_style' => $image_style_setting,
229 'tags' => $cache_tags,
230 'contexts' => $cache_contexts,
241 public function calculateDependencies() {
242 $dependencies = parent::calculateDependencies();
243 $style_id = $this->getSetting('image_style');
244 /** @var \Drupal\image\ImageStyleInterface $style */
245 if ($style_id && $style = ImageStyle::load($style_id)) {
246 // If this formatter uses a valid image style to display the image, add
247 // the image style configuration entity as dependency of this formatter.
248 $dependencies[$style->getConfigDependencyKey()][] = $style->getConfigDependencyName();
250 return $dependencies;
256 public function onDependencyRemoval(array $dependencies) {
257 $changed = parent::onDependencyRemoval($dependencies);
258 $style_id = $this->getSetting('image_style');
259 /** @var \Drupal\image\ImageStyleInterface $style */
260 if ($style_id && $style = ImageStyle::load($style_id)) {
261 if (!empty($dependencies[$style->getConfigDependencyKey()][$style->getConfigDependencyName()])) {
262 $replacement_id = $this->imageStyleStorage->getReplacementId($style_id);
263 // If a valid replacement has been provided in the storage, replace the
264 // image style with the replacement and signal that the formatter plugin
265 // settings were updated.
266 if ($replacement_id && ImageStyle::load($replacement_id)) {
267 $this->setSetting('image_style', $replacement_id);