a00d9e27f0c11af46aa9493a051c6584e413082e
[yaffs-website] / web / core / modules / image / src / Plugin / Field / FieldFormatter / ImageFormatter.php
1 <?php
2
3 namespace Drupal\image\Plugin\Field\FieldFormatter;
4
5 use Drupal\Core\Entity\EntityStorageInterface;
6 use Drupal\Core\Field\FieldItemListInterface;
7 use Drupal\Core\Field\FieldDefinitionInterface;
8 use Drupal\Core\Link;
9 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
10 use Drupal\Core\Session\AccountInterface;
11 use Drupal\Core\Url;
12 use Drupal\image\Entity\ImageStyle;
13 use Symfony\Component\DependencyInjection\ContainerInterface;
14 use Drupal\Core\Form\FormStateInterface;
15 use Drupal\Core\Cache\Cache;
16
17 /**
18  * Plugin implementation of the 'image' formatter.
19  *
20  * @FieldFormatter(
21  *   id = "image",
22  *   label = @Translation("Image"),
23  *   field_types = {
24  *     "image"
25  *   },
26  *   quickedit = {
27  *     "editor" = "image"
28  *   }
29  * )
30  */
31 class ImageFormatter extends ImageFormatterBase implements ContainerFactoryPluginInterface {
32
33   /**
34    * The current user.
35    *
36    * @var \Drupal\Core\Session\AccountInterface
37    */
38   protected $currentUser;
39
40   /**
41    * The image style entity storage.
42    *
43    * @var \Drupal\image\ImageStyleStorageInterface
44    */
45   protected $imageStyleStorage;
46
47   /**
48    * Constructs an ImageFormatter object.
49    *
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
61    *   The view mode.
62    * @param array $third_party_settings
63    *   Any third party settings settings.
64    * @param \Drupal\Core\Session\AccountInterface $current_user
65    *   The current user.
66    * @param \Drupal\Core\Entity\EntityStorageInterface $image_style_storage
67    *   The image style storage.
68    */
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;
73   }
74
75   /**
76    * {@inheritdoc}
77    */
78   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
79     return new static(
80       $plugin_id,
81       $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')
89     );
90   }
91
92   /**
93    * {@inheritdoc}
94    */
95   public static function defaultSettings() {
96     return [
97       'image_style' => '',
98       'image_link' => '',
99     ] + parent::defaultSettings();
100   }
101
102   /**
103    * {@inheritdoc}
104    */
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')
110     );
111     $element['image_style'] = [
112       '#title' => t('Image style'),
113       '#type' => 'select',
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')
119       ],
120     ];
121     $link_types = [
122       'content' => t('Content'),
123       'file' => t('File'),
124     ];
125     $element['image_link'] = [
126       '#title' => t('Link image to'),
127       '#type' => 'select',
128       '#default_value' => $this->getSetting('image_link'),
129       '#empty_option' => t('Nothing'),
130       '#options' => $link_types,
131     ];
132
133     return $element;
134   }
135
136   /**
137    * {@inheritdoc}
138    */
139   public function settingsSummary() {
140     $summary = [];
141
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]]);
150     }
151     else {
152       $summary[] = t('Original image');
153     }
154
155     $link_types = [
156       'content' => t('Linked to content'),
157       'file' => t('Linked to file'),
158     ];
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];
163     }
164
165     return $summary;
166   }
167
168   /**
169    * {@inheritdoc}
170    */
171   public function viewElements(FieldItemListInterface $items, $langcode) {
172     $elements = [];
173     $files = $this->getEntitiesToView($items, $langcode);
174
175     // Early opt-out if the field is empty.
176     if (empty($files)) {
177       return $elements;
178     }
179
180     $url = NULL;
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();
187       }
188     }
189     elseif ($image_link_setting == 'file') {
190       $link_file = TRUE;
191     }
192
193     $image_style_setting = $this->getSetting('image_style');
194
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();
200     }
201
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';
213       }
214       $cache_tags = Cache::mergeTags($base_cache_tags, $file->getCacheTags());
215
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);
221
222       $elements[$delta] = [
223         '#theme' => 'image_formatter',
224         '#item' => $item,
225         '#item_attributes' => $item_attributes,
226         '#image_style' => $image_style_setting,
227         '#url' => $url,
228         '#cache' => [
229           'tags' => $cache_tags,
230           'contexts' => $cache_contexts,
231         ],
232       ];
233     }
234
235     return $elements;
236   }
237
238   /**
239    * {@inheritdoc}
240    */
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();
249     }
250     return $dependencies;
251   }
252
253   /**
254    * {@inheritdoc}
255    */
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);
268           $changed = TRUE;
269         }
270       }
271     }
272     return $changed;
273   }
274
275 }