diff --git a/core/modules/image/config/schema/image.schema.yml b/core/modules/image/config/schema/image.schema.yml index 0006ad7..cb1a4a8 100644 --- a/core/modules/image/config/schema/image.schema.yml +++ b/core/modules/image/config/schema/image.schema.yml @@ -146,6 +146,12 @@ field.formatter.settings.image_url: 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 diff --git a/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php index 29dec18..b833446 100644 --- a/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php +++ b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php @@ -8,6 +8,7 @@ namespace Drupal\image\Plugin\Field\FieldFormatter; use Drupal\Core\Field\FieldItemListInterface; +use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; use Drupal\Core\Cache\Cache; @@ -27,6 +28,54 @@ class ImageFormatter extends ImageFormatterBase { /** * {@inheritdoc} */ + public static function defaultSettings() { + return ['image_link' => ''] + parent::defaultSettings(); + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + $element = parent::settingsForm($form, $form_state); + + $link_types = [ + 'content' => $this->t('Content'), + 'file' => $this->t('File'), + ]; + $element['image_link'] = [ + '#title' => $this->t('Link image to'), + '#type' => 'select', + '#default_value' => $this->getSetting('image_link'), + '#empty_option' => $this->t('Nothing'), + '#options' => $link_types, + ]; + + return $element; + } + + + /** + * {@inheritdoc} + */ + public function settingsSummary() { + $summary = parent::settingsSummary(); + + $link_types = [ + 'content' => $this->t('Linked to content'), + 'file' => $this->t('Linked to file'), + ]; + // Display this setting only if image is linked. + $image_link_setting = $this->getSetting('image_link'); + if ($image_link_setting && isset($link_types[$image_link_setting])) { + $summary[] = $link_types[$image_link_setting]; + } + + return $summary; + } + + /** + * {@inheritdoc} + */ public function viewElements(FieldItemListInterface $items, $langcode) { $elements = array(); /** @var \Drupal\file\Entity\File[] $files */ diff --git a/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatterBase.php b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatterBase.php index cfebfa8..d714d56 100644 --- a/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatterBase.php +++ b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatterBase.php @@ -26,7 +26,7 @@ abstract class ImageFormatterBase extends FileFormatterBase implements ContainerFactoryPluginInterface { /** - * The current user. + * The current user service. * * @var \Drupal\Core\Session\AccountInterface */ @@ -40,6 +40,13 @@ protected $imageStyleStorage; /** + * The entity repository service. + * + * @var \Drupal\Core\Entity\EntityRepositoryInterface + */ + protected $entityRepository; + + /** * Constructs an ImageFormatterBase object. * * @param string $plugin_id @@ -58,6 +65,8 @@ * Any third party settings. * @param \Drupal\Core\Session\AccountInterface $current_user * The current user. + * @param \Drupal\Core\Entity\EntityStorageInterface $image_style_storage + * The image style entity storage. */ 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) { parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings); @@ -78,19 +87,15 @@ public static function create(ContainerInterface $container, array $configuratio $configuration['view_mode'], $configuration['third_party_settings'], $container->get('current_user'), - $container->get('entity.manager')->getStorage('image_style') + $container->get('entity_type.manager')->getStorage('image_style') ); } - /** * {@inheritdoc} */ public static function defaultSettings() { - return [ - 'image_style' => '', - 'image_link' => '', - ] + parent::defaultSettings(); + return ['image_style' => ''] + parent::defaultSettings(); } /** @@ -99,7 +104,7 @@ public static function defaultSettings() { public function settingsForm(array $form, FormStateInterface $form_state) { $image_styles = image_style_options(FALSE); $description_link = Link::fromTextAndUrl( - $this->$this->t('Configure image styles'), + $this->t('Configure image styles'), Url::fromRoute('entity.image_style.collection') ); $element['image_style'] = [ @@ -112,17 +117,6 @@ public function settingsForm(array $form, FormStateInterface $form_state) { '#access' => AccessResult::allowedIfHasPermission($this->currentUser, 'administer image styles'), ], ]; - $link_types = [ - 'content' => $this->t('Content'), - 'file' => $this->t('File'), - ]; - $element['image_link'] = [ - '#title' => $this->t('Link image to'), - '#type' => 'select', - '#default_value' => $this->getSetting('image_link'), - '#empty_option' => $this->t('Nothing'), - '#options' => $link_types, - ]; return $element; } @@ -145,16 +139,6 @@ public function settingsSummary() { $summary[] = $this->t('Original image'); } - $link_types = [ - 'content' => $this->t('Linked to content'), - 'file' => $this->t('Linked to file'), - ]; - // Display this setting only if image is linked. - $image_link_setting = $this->getSetting('image_link'); - if (isset($link_types[$image_link_setting])) { - $summary[] = $link_types[$image_link_setting]; - } - return $summary; } @@ -170,7 +154,7 @@ protected function getEntitiesToView(EntityReferenceFieldItemListInterface $item if (empty($default_image['uuid']) && $this->fieldDefinition instanceof FieldConfigInterface) { $default_image = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('default_image'); } - if (!empty($default_image['uuid']) && $file = \Drupal::entityManager()->loadEntityByUuid('file', $default_image['uuid'])) { + if (!empty($default_image['uuid']) && $file = $this->getEntityRepository()->loadEntityByUuid('file', $default_image['uuid'])) { // Clone the FieldItemList into a runtime-only object for the formatter, // so that the fallback image can be rendered without affecting the // field values in the entity being rendered. @@ -199,13 +183,29 @@ public function calculateDependencies() { // Make sure to include third party dependencies. $dependencies = parent::calculateDependencies(); - // Check for image style. - if (!empty($image_style_setting) && $image_style = $this->imageStyleStorage->load($image_style_setting)) { - // Add the image style dependencies as well. - $dependencies['config'][] = $image_style->getConfigDependencyName(); + // Check for a valid image style. + if (!empty($image_style_name = $this->getSetting('image_style'))) { + if ($image_style = $this->imageStyleStorage->load($image_style_name)) { + // Add a dependency to image style. + $dependencies[$image_style->getConfigDependencyKey()][] = $image_style->getConfigDependencyName(); + } } return $dependencies; } + /** + * Returns the entity repository service. + * + * @return \Drupal\Core\Entity\EntityRepositoryInterface + * + * @todo Inject this service in constructor, in Drupal 9.0.x. + */ + protected function getEntityRepository() { + if (!isset($this->entityRepository)) { + $this->entityRepository = \Drupal::service('entity.repository'); + } + return $this->entityRepository; + } + } diff --git a/core/modules/image/src/Plugin/Field/FieldFormatter/ImageUrlFormatter.php b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageUrlFormatter.php index 72c3f23..4014fd5 100644 --- a/core/modules/image/src/Plugin/Field/FieldFormatter/ImageUrlFormatter.php +++ b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageUrlFormatter.php @@ -1,4 +1,5 @@ FALSE, + 'trim_length' => 80, + ] + parent::defaultSettings(); + } + + /** + * {@inheritdoc} + */ public function settingsForm(array $form, FormStateInterface $form_state) { $element = parent::settingsForm($form, $form_state); - // The URL formatter does not support image_link. - unset($element['image_link']); + $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'), + ]; + $field_name = $this->fieldDefinition->getName(); + $element['trim_length'] = [ + '#type' => 'number', + '#title' => t('Trim link text length'), + '#field_suffix' => t('characters'), + '#default_value' => $this->getSetting('trim_length'), + '#min' => 1, + '#description' => t('Leave blank to allow unlimited link text lengths.'), + '#states' => [ + 'visible' => [ + ':input[name="fields[' . $field_name . '][settings_edit_form][settings][url_link]"]' => ['checked' => TRUE], + ], + ], + ]; return $element; } @@ -40,39 +70,46 @@ public function settingsForm(array $form, FormStateInterface $form_state) { /** * {@inheritdoc} */ + public function settingsSummary() { + $summary = parent::settingsSummary(); + + if (!$this->getSetting('url_link')) { + $summary[] = $this->t('Show URL as plain-text'); + } + else { + $trim_length = $this->getSetting('trim_length'); + if (empty($trim_length)) { + $summary[] = $this->t('Show URL as link'); + } + else { + $summary[] = t('Show URL as link trimmed to @limit characters', ['@limit' => $trim_length]); + } + } + + return $summary; + } + + /** + * {@inheritdoc} + */ public function viewElements(FieldItemListInterface $items, $langcode) { $elements = []; - /** @var \Drupal\file\Entity\File[] $images */ - $images = $this->getEntitiesToView($items, $langcode); - // Early opt-out if the field is empty. - if (empty($images)) { + /** + * @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; } - $image_link_setting = $this->getSetting('image_link'); - // URL to be linked to. - $link_url = FALSE; - // Check if the formatter involves a link. - if ($image_link_setting == 'content') { - $entity = $items->getEntity(); - if (!$entity->isNew()) { - $link_url = $entity->urlInfo(); - } - } - - /** @var \Drupal\image\Entity\ImageStyle|false $image_style */ - $image_style = ($image_style_setting = $this->getSetting('image_style')) && !empty($image_style_setting) ? $this->imageStyleStorage->load($image_style_setting) : NULL; + /** @var \Drupal\image\ImageStyleInterface $image_style */ + $image_style = $this->imageStyleStorage->load($this->getSetting('image_style')); foreach ($images as $delta => $image) { - /** @var \Drupal\file\Entity\File $image */ $image_uri = $image->getFileUri(); - $url = $image_style - ? $image_style->buildUrl($image_uri) - : file_create_url($image_uri); - - // Set the link URL if settings require such. - $link_url = ($image_link_setting == 'file') ? Url::fromUri($url) : $link_url; + $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); @@ -80,16 +117,23 @@ public function viewElements(FieldItemListInterface $items, $langcode) { $cacheable_metadata->addCacheableDependency(CacheableMetadata::createFromObject($image_style)); } - // Add a link if we have a valid link URL. - if ($link_url instanceof Url) { - $elements[$delta] = Link::fromTextAndUrl($url, $link_url)->toRenderable(); + if (!$this->getSetting('url_link')) { + // Plain text URL. + $elements[$delta] = ['#markup' => $url]; } else { - $elements[$delta] = ['#markup' => $url]; + // Link URL. + $text = $url; + // Trim the link text to the desired length. + if (!empty($trim_length = $this->getSetting('trim_length'))) { + $text = Unicode::truncate($text, $trim_length, FALSE, TRUE); + } + $elements[$delta] = Link::fromTextAndUrl($text, Url::fromUri($url))->toRenderable(); } $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 d7c56be..a83952f 100644 --- a/core/modules/image/src/Tests/ImageFieldDisplayTest.php +++ b/core/modules/image/src/Tests/ImageFieldDisplayTest.php @@ -7,6 +7,7 @@ namespace Drupal\image\Tests; +use Drupal\Component\Utility\Unicode; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\field\Entity\FieldStorageConfig; use Drupal\user\RoleInterface; @@ -207,46 +208,33 @@ function _testImageFieldFormatters($scheme) { // Test the image URL formatter without an image style. $display_options = [ 'type' => 'image_url', - 'settings' => [], - ]; - - $expected_url = file_create_url($image_uri); - $this->assertEqual($expected_url, $node->{$field_name}->view($display_options)[0]['#markup']); - - // Test the image URL formatter with an image style. - $display_options = [ - 'type' => 'image_url', 'settings' => [ - 'image_style' => 'thumbnail', + '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 that links to content. - $display_options = [ - 'type' => 'image_url', - 'settings' => [ - 'image_style' => 'thumbnail', - 'image_link' => 'content', - ], - ]; + // 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 = '' . ImageStyle::load('thumbnail')->buildUrl($image_uri) . ''; + $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 that links to file. - $display_options = [ - 'type' => 'image_url', - 'settings' => [ - 'image_style' => 'thumbnail', - 'image_link' => 'file', - ], - ]; + // Test the image URL formatter with an image style with link and trimmed + // link text. + $display_options['settings']['trim_length'] = 10; - $expected_url = ImageStyle::load('thumbnail')->buildUrl($image_uri); - $expected_output = '' . $expected_url . ''; + $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])); } diff --git a/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php b/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php index 08648e7..da9c965 100644 --- a/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php +++ b/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php @@ -12,7 +12,6 @@ use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Url; use Drupal\image\Plugin\Field\FieldFormatter\ImageFormatterBase; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -30,27 +29,13 @@ * } * ) */ -class ResponsiveImageFormatter extends ImageFormatterBase implements ContainerFactoryPluginInterface { +class ResponsiveImageFormatter extends ImageFormatterBase { /** * @var EntityStorageInterface */ protected $responsiveImageStyleStorage; - /* - * The image style entity storage. - * - * @var \Drupal\Core\Entity\EntityStorageInterface - */ - protected $imageStyleStorage; - - /** - * The current user. - * - * @var \Drupal\Core\Session\AccountInterface - */ - protected $currentUser; - /** * The link generator. * @@ -86,11 +71,8 @@ class ResponsiveImageFormatter extends ImageFormatterBase implements ContainerFa */ public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, EntityStorageInterface $responsive_image_style_storage, EntityStorageInterface $image_style_storage, LinkGeneratorInterface $link_generator, AccountInterface $current_user) { parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings, $current_user, $image_style_storage); - $this->responsiveImageStyleStorage = $responsive_image_style_storage; - $this->imageStyleStorage = $image_style_storage; $this->linkGenerator = $link_generator; - $this->currentUser = $current_user; } /** @@ -116,14 +98,10 @@ public static function create(ContainerInterface $container, array $configuratio * {@inheritdoc} */ public static function defaultSettings() { - $default_settings = parent::defaultSettings(); - - // Use responsive_image_style instead of image_style; - unset($default_settings['image_style']); - - return [ + return array( 'responsive_image_style' => '', - ] + $default_settings ; + 'image_link' => '', + ); } /**