diff --git a/core/modules/image/config/schema/image.schema.yml b/core/modules/image/config/schema/image.schema.yml
index 323220b..cb1a4a8 100644
--- a/core/modules/image/config/schema/image.schema.yml
+++ b/core/modules/image/config/schema/image.schema.yml
@@ -139,6 +139,20 @@ field.formatter.settings.image:
type: string
label: 'Image style'
+field.formatter.settings.image_url:
+ type: mapping
+ label: 'Image URL formatter settings'
+ mapping:
+ image_style:
+ type: string
+ label: 'Image style'
+ url_link:
+ type: boolean
+ label: 'Link to the image URL'
+ trim_length:
+ type: integer
+ label: 'Trim link text length'
+
field.widget.settings.image_image:
type: mapping
label: 'Image field display format settings'
diff --git a/core/modules/image/src/Plugin/Field/FieldFormatter/ImageUrlFormatter.php b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageUrlFormatter.php
new file mode 100644
index 0000000..0a861de
--- /dev/null
+++ b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageUrlFormatter.php
@@ -0,0 +1,145 @@
+ '',
+ 'url_link' => '',
+ 'trim_length' => '80',
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsForm(array $form, FormStateInterface $form_state) {
+ $element = parent::settingsForm($form, $form_state);
+
+ $element['url_link'] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Provide link'),
+ '#description' => $this->t('If checked, the URL to image will get a click-able link otherwise, the plain URL will be displayed.'),
+ '#default_value' => $this->getSetting('url_link'),
+ ];
+ $element['trim_length'] = [
+ '#type' => 'number',
+ '#title' => $this->t('Trim link text length'),
+ '#field_suffix' => $this->t('characters'),
+ '#default_value' => $this->getSetting('trim_length'),
+ '#min' => 1,
+ '#description' => $this->t('Leave blank to allow unlimited link text lengths.'),
+ '#states' => [
+ 'invisible' => [
+ ":input[name=\"fields[{$this->fieldDefinition->getName()}][settings_edit_form][settings][url_link]\"]" => ['checked' => FALSE],
+ ],
+ ],
+ ];
+
+ unset($element['image_link']);;
+
+ return $element;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsSummary() {
+ $summary = [];
+
+ $image_styles = image_style_options(FALSE);
+ // Unset possible 'No defined styles' option.
+ unset($image_styles['']);
+ // Styles could be lost because of enabled/disabled modules that defines
+ // their styles in code.
+ $image_style_setting = $this->getSetting('image_style');
+ if (isset($image_styles[$image_style_setting])) {
+ $summary[] = $this->t('Image style: @style', ['@style' => $image_styles[$image_style_setting]]);
+ }
+ else {
+ $summary[] = $this->t('Original image');
+ }
+
+ return $summary;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function viewElements(FieldItemListInterface $items, $langcode) {
+ $elements = [];
+ $settings = $this->getSettings();
+
+ /**
+ * @var \Drupal\file\Entity\File[] $images
+ * @var \Drupal\Core\Field\EntityReferenceFieldItemListInterface $items
+ */
+ if (empty($images = $this->getEntitiesToView($items, $langcode))) {
+ // Early opt-out if the field is empty.
+ return $elements;
+ }
+
+ /** @var \Drupal\image\ImageStyleInterface $image_style */
+ $image_style = $this->imageStyleStorage->load($settings['image_style']);
+
+ foreach ($images as $delta => $image) {
+ $image_uri = $image->getFileUri();
+ $url = $image_style ? $image_style->buildUrl($image_uri) : file_create_url($image_uri);
+
+ // Add cacheable metadata from the image and image style.
+ $cacheable_metadata = CacheableMetadata::createFromObject($image);
+ if ($image_style) {
+ $cacheable_metadata->addCacheableDependency(CacheableMetadata::createFromObject($image_style));
+ }
+
+ if ($settings['url_link']) {
+ // Linked url.
+ $link_title = $url;
+ if (!empty($settings['trim_length'])) {
+ $link_title = Unicode::truncate($link_title, $settings['trim_length'], FALSE, TRUE);
+ }
+ $elements[$delta] = [
+ '#type' => 'link',
+ '#url' => Url::fromUri(file_create_url($url)),
+ '#title' => $link_title,
+ ];
+ }
+ else {
+ // Plain text URL.
+ $elements[$delta] = ['#markup' => $url];
+ }
+ $cacheable_metadata->applyTo($elements[$delta]);
+ }
+
+ return $elements;
+ }
+
+}
diff --git a/core/modules/image/src/Tests/ImageFieldDisplayTest.php b/core/modules/image/src/Tests/ImageFieldDisplayTest.php
index 666a791..f30d273 100644
--- a/core/modules/image/src/Tests/ImageFieldDisplayTest.php
+++ b/core/modules/image/src/Tests/ImageFieldDisplayTest.php
@@ -2,6 +2,7 @@
namespace Drupal\image\Tests;
+use Drupal\Component\Utility\Unicode;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\user\RoleInterface;
@@ -88,6 +89,7 @@ function _testImageFieldFormatters($scheme) {
// Save node.
$nid = $this->uploadNodeImage($test_image, $field_name, 'article', $alt);
$node_storage->resetCache(array($nid));
+ /** @var \Drupal\node\Entity\Node $node */
$node = $node_storage->load($nid);
// Test that the default formatter is being used.
@@ -199,6 +201,38 @@ function _testImageFieldFormatters($scheme) {
$this->drupalGet(ImageStyle::load('thumbnail')->buildUrl($image_uri));
$this->assertResponse('403', 'Access denied to image style thumbnail as anonymous user.');
}
+
+ // Test the image URL formatter without an image style.
+ $display_options = [
+ 'type' => 'image_url',
+ 'settings' => [
+ 'image_style' => '',
+ 'url_link' => FALSE,
+ 'trim_length' => 80,
+ ],
+ ];
+ $expected_url = file_create_url($image_uri);
+ $this->assertEqual($expected_url, $node->{$field_name}->view($display_options)[0]['#markup']);
+
+ // Test the image URL formatter without an image style.
+ $display_options['settings']['image_style'] = 'thumbnail';
+ $expected_url = ImageStyle::load('thumbnail')->buildUrl($image_uri);
+ $this->assertEqual($expected_url, $node->{$field_name}->view($display_options)[0]['#markup']);
+
+ // Test the image URL formatter with an image style with link.
+ $display_options['settings']['url_link'] = TRUE;
+ $display_options['settings']['trim_length'] = 0;
+
+ $expected_output = '' . $expected_url . '';
+ $this->assertEqual($expected_output, (string) $renderer->renderRoot($node->{$field_name}->view($display_options)[0]));
+
+ // Test the image URL formatter with an image style with link and trimmed
+ // link text.
+ $display_options['settings']['trim_length'] = 10;
+
+ $expected_link_text = Unicode::truncate($expected_url, 10, FALSE, TRUE);
+ $expected_output = '' . $expected_link_text . '';
+ $this->assertEqual($expected_output, (string) $renderer->renderRoot($node->{$field_name}->view($display_options)[0]));
}
/**