Issue #2405469 by yched, googletorp, amateescu, larowlan, Berdir: FileFormatterBase should extend EntityReferenceFormatterBase

8.0.x
Alex Pott 2015-02-27 11:47:51 +00:00
parent 9c0bfb2d3b
commit ef0cb6e091
14 changed files with 282 additions and 143 deletions

View File

@ -7,7 +7,8 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter; namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\Core\Field\FormatterBase; use Drupal\Core\Field\FormatterBase;
use Drupal\Core\TypedData\TranslatableInterface; use Drupal\Core\TypedData\TranslatableInterface;
@ -17,15 +18,26 @@ use Drupal\Core\TypedData\TranslatableInterface;
abstract class EntityReferenceFormatterBase extends FormatterBase { abstract class EntityReferenceFormatterBase extends FormatterBase {
/** /**
* Returns the accessible and translated entities for view. * Returns the referenced entities for display.
* *
* @param \Drupal\Core\Field\FieldItemListInterface $items * The method takes care of:
* - checking entity access,
* - placing the entities in the language expected for display.
* It is thus strongly recommended that formatters use it in their
* implementation of viewElements($items) rather than dealing with $items
* directly.
*
* For each entity, the EntityReferenceItem by which the entity is referenced
* is available in $entity->_referringItem. This is useful for field types
* that store additional values next to the reference itself.
*
* @param \Drupal\Core\Field\EntityReferenceFieldItemListInterface $items
* The item list. * The item list.
* *
* @return \Drupal\Core\Entity\EntityInterface[] * @return \Drupal\Core\Entity\EntityInterface[]
* The entities to view. * The array of referenced entities to display, keyed by delta.
*/ */
protected function getEntitiesToView(FieldItemListInterface $items) { protected function getEntitiesToView(EntityReferenceFieldItemListInterface $items) {
$entities = array(); $entities = array();
$parent_entity_langcode = $items->getEntity()->language()->getId(); $parent_entity_langcode = $items->getEntity()->language()->getId();
@ -39,8 +51,10 @@ abstract class EntityReferenceFormatterBase extends FormatterBase {
$entity = $entity->getTranslation($parent_entity_langcode); $entity = $entity->getTranslation($parent_entity_langcode);
} }
// Check entity access. // Check entity access if needed.
if ($entity->access('view')) { if (!$this->needsAccessCheck($item) || $entity->access('view')) {
// Add the referring item, in case the formatter needs it.
$entity->_referringItem = $items[$delta];
$entities[$delta] = $entity; $entities[$delta] = $entity;
} }
} }
@ -56,10 +70,9 @@ abstract class EntityReferenceFormatterBase extends FormatterBase {
* viewed. * viewed.
*/ */
public function prepareView(array $entities_items) { public function prepareView(array $entities_items) {
// Load the existing (non-autocreate) entities. For performance, we want to // Collect entity IDs to load. For performance, we want to use a single
// use a single "multiple entity load" to load all the entities for the // "multiple entity load" to load all the entities for the multiple
// multiple "entity reference item lists" that are being displayed. We thus // "entity reference item lists" being displayed. We thus cannot use
// cannot use
// \Drupal\Core\Field\EntityReferenceFieldItemList::referencedEntities(). // \Drupal\Core\Field\EntityReferenceFieldItemList::referencedEntities().
$ids = array(); $ids = array();
foreach ($entities_items as $items) { foreach ($entities_items as $items) {
@ -69,7 +82,7 @@ abstract class EntityReferenceFormatterBase extends FormatterBase {
// contains a valid entity ready for display. All items are initialized // contains a valid entity ready for display. All items are initialized
// at FALSE. // at FALSE.
$item->_loaded = FALSE; $item->_loaded = FALSE;
if ($item->target_id !== NULL) { if ($this->needsEntityLoad($item)) {
$ids[] = $item->target_id; $ids[] = $item->target_id;
} }
} }
@ -94,4 +107,30 @@ abstract class EntityReferenceFormatterBase extends FormatterBase {
} }
} }
/**
* Returns whether the entity referenced by an item needs to be loaded.
*
* @param \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $item
* The item to check.
*
* @return bool
* TRUE if the entity needs to be loaded.
*/
protected function needsEntityLoad(EntityReferenceItem $item) {
return !$item->hasNewEntity();
}
/**
* Returns whether entity access should be checked.
*
* @param \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $item
* The item to check.
*
* @return bool
* TRUE if entity access should be checked.
*/
protected function needsAccessCheck(EntityReferenceItem $item) {
return TRUE;
}
} }

View File

@ -0,0 +1,30 @@
<?php
/**
* @file
* Contains \Drupal\file\FileAccessFormatterControlHandlerInterface.
*/
namespace Drupal\file;
use Drupal\Core\Entity\EntityAccessControlHandlerInterface;
/**
* Defines an interface for file access handlers that need to run on file formatters.
*
* \Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceFormatterBase,
* which file and image formatters extend, checks 'view' access on the
* referenced files before displaying them. That check would be useless and
* costly with Core's default access control implementation for files
* (\Drupal\file\FileAccessControlHandler grants access based on whether
* there are existing entities with granted access that reference the file). But
* it might be needed if a different access control handler with different logic
* is swapped in.
*
* \Drupal\file\Plugin\Field\FieldFormatter\FileFormatterBase thus adjusts that
* behavior, and only checks access if the access control handler in use for
* files opts in by implementing this interface.
*
* @see \Drupal\file\Plugin\Field\FieldFormatter\FileFormatterBase::needsAccessCheck()
*/
interface FileAccessFormatterControlHandlerInterface extends EntityAccessControlHandlerInterface { }

View File

@ -7,40 +7,29 @@
namespace Drupal\file\Plugin\Field\FieldFormatter; namespace Drupal\file\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FormatterBase; use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceFormatterBase;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
/** /**
* Base class for file formatters. * Base class for file formatters.
*/ */
abstract class FileFormatterBase extends FormatterBase { abstract class FileFormatterBase extends EntityReferenceFormatterBase {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function prepareView(array $entities_items) { protected function needsEntityLoad(EntityReferenceItem $item) {
// Remove files specified to not be displayed. return parent::needsEntityLoad($item) && $item->isDisplayed();
$fids = array();
foreach ($entities_items as $items) {
foreach ($items as $item) {
if ($item->isDisplayed() && !empty($item->target_id)) {
// Load the files from the files table.
$fids[] = $item->target_id;
}
}
}
if ($fids) {
$files = file_load_multiple($fids);
foreach ($entities_items as $items) {
/** @var \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $item */
foreach ($items as $item) {
// If the file does not exist, mark the entire item as empty.
if (!empty($item->target_id) && !$item->hasNewEntity()) {
$item->entity = isset($files[$item->target_id]) ? $files[$item->target_id] : NULL;
}
}
}
}
} }
/**
* {@inheritdoc}
*/
protected function needsAccessCheck(EntityReferenceItem $item) {
// Only check access if the current file access control handler explicitly
// opts in by implementing FileAccessFormatterControlHandlerInterface.
$access_handler_class = $item->entity->getEntityType()->getHandlerClass('access');
return is_subclass_of($access_handler_class, '\Drupal\file\FileAccessFormatterControlHandlerInterface');
}
} }

View File

@ -28,21 +28,20 @@ class GenericFileFormatter extends FileFormatterBase {
public function viewElements(FieldItemListInterface $items) { public function viewElements(FieldItemListInterface $items) {
$elements = array(); $elements = array();
foreach ($items as $delta => $item) { foreach ($this->getEntitiesToView($items) as $delta => $file) {
if ($item->isDisplayed() && $item->entity) { $item = $file->_referringItem;
$elements[$delta] = array( $elements[$delta] = array(
'#theme' => 'file_link', '#theme' => 'file_link',
'#file' => $item->entity, '#file' => $file,
'#description' => $item->description, '#description' => $item->description,
); );
// Pass field item attributes to the theme function. // Pass field item attributes to the theme function.
if (isset($item->_attributes)) { if (isset($item->_attributes)) {
$elements[$delta] += array('#attributes' => array()); $elements[$delta] += array('#attributes' => array());
$elements[$delta]['#attributes'] += $item->_attributes; $elements[$delta]['#attributes'] += $item->_attributes;
// Unset field item attributes since they have been included in the // Unset field item attributes since they have been included in the
// formatter output and should not be rendered in the field template. // formatter output and should not be rendered in the field template.
unset($item->_attributes); unset($item->_attributes);
}
} }
} }
if (!empty($elements)) { if (!empty($elements)) {

View File

@ -29,21 +29,16 @@ class RSSEnclosureFormatter extends FileFormatterBase {
$entity = $items->getEntity(); $entity = $items->getEntity();
// Add the first file as an enclosure to the RSS item. RSS allows only one // Add the first file as an enclosure to the RSS item. RSS allows only one
// enclosure per item. See: http://en.wikipedia.org/wiki/RSS_enclosure // enclosure per item. See: http://en.wikipedia.org/wiki/RSS_enclosure
foreach ($items as $item) { foreach ($this->getEntitiesToView($items) as $delta => $file) {
if ($item->isDisplayed() && $item->entity) { $entity->rss_elements[] = array(
$file = $item->entity; 'key' => 'enclosure',
$entity->rss_elements[] = array( 'attributes' => array(
'key' => 'enclosure', 'url' => file_create_url($file->getFileUri()),
'attributes' => array( 'length' => $file->getSize(),
'url' => file_create_url($file->getFileUri()), 'type' => $file->getMimeType(),
'length' => $file->getSize(), ),
'type' => $file->getMimeType(), );
),
);
break;
}
} }
} }
} }

View File

@ -28,22 +28,19 @@ class TableFormatter extends FileFormatterBase {
public function viewElements(FieldItemListInterface $items) { public function viewElements(FieldItemListInterface $items) {
$elements = array(); $elements = array();
if (!$items->isEmpty()) { if ($files = $this->getEntitiesToView($items)) {
$header = array(t('Attachment'), t('Size')); $header = array(t('Attachment'), t('Size'));
$rows = array(); $rows = array();
foreach ($items as $delta => $item) { foreach ($files as $delta => $file) {
if ($item->isDisplayed() && $item->entity) { $rows[] = array(
$rows[] = array( array(
array( 'data' => array(
'data' => array( '#theme' => 'file_link',
'#theme' => 'file_link', '#file' => $file,
'#file' => $item->entity,
),
), ),
array('data' => format_size($item->entity->getSize())), ),
); array('data' => format_size($file->getSize())),
} );
} }
$elements[0] = array(); $elements[0] = array();

View File

@ -28,10 +28,8 @@ class UrlPlainFormatter extends FileFormatterBase {
public function viewElements(FieldItemListInterface $items) { public function viewElements(FieldItemListInterface $items) {
$elements = array(); $elements = array();
foreach ($items as $delta => $item) { foreach ($this->getEntitiesToView($items) as $delta => $file) {
if ($item->isDisplayed() && $item->entity) { $elements[$delta] = array('#markup' => file_create_url($file->getFileUri()));
$elements[$delta] = array('#markup' => empty($item->entity) ? '' : file_create_url($item->entity->getFileUri()));
}
} }
return $elements; return $elements;

View File

@ -0,0 +1,38 @@
<?php
/**
* @file
* Contains \Drupal\file\Tests\FileFieldFormatterAccessTest.
*/
namespace Drupal\file\Tests;
/**
* Tests file formatter access.
* @group file
*/
class FileFieldFormatterAccessTest extends FileFieldTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'file', 'field_ui', 'file_test'];
/**
* Tests the custom access handler is invoked.
*/
public function testFileAccessHandler() {
$type_name = 'article';
$field_name = strtolower($this->randomMachineName());
$this->createFileField($field_name, 'node', $type_name);
\Drupal::state()->set('file_test_alternate_access_handler', TRUE);
\Drupal::entityManager()->clearCachedDefinitions();
$test_file = $this->getTestFile('text');
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
$this->drupalGet('node/' . $nid);
$this->assertTrue(\Drupal::state()->get('file_access_formatter_check', FALSE));
}
}

View File

@ -333,3 +333,14 @@ function file_test_file_scan_callback($filepath = NULL) {
function file_test_file_scan_callback_reset() { function file_test_file_scan_callback_reset() {
drupal_static_reset('file_test_file_scan_callback'); drupal_static_reset('file_test_file_scan_callback');
} }
/**
* Implements hook_entity_info_alter().
*/
function file_test_entity_type_alter(&$entity_types) {
if (\Drupal::state()->get('file_test_alternate_access_handler', FALSE)) {
/** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
$entity_types['file']
->setAccessClass('Drupal\file_test\FileTestAccessControlHandler');
}
}

View File

@ -0,0 +1,27 @@
<?php
/**
* @file
* Contains \Drupal\file_test\FileTestAccessControlHandler.
*/
namespace Drupal\file_test;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\file\FileAccessFormatterControlHandlerInterface;
use Drupal\file\FileAccessControlHandler;
/**
* Defines a class for an alternate file access control handler.
*/
class FileTestAccessControlHandler extends FileAccessControlHandler implements FileAccessFormatterControlHandlerInterface {
/**
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
\Drupal::state()->set('file_access_formatter_check', TRUE);
return parent::checkAccess($entity, $operation, $langcode, $account);
}
}

View File

@ -167,8 +167,14 @@ class ImageFormatter extends ImageFormatterBase implements ContainerFactoryPlugi
*/ */
public function viewElements(FieldItemListInterface $items) { public function viewElements(FieldItemListInterface $items) {
$elements = array(); $elements = array();
$url = NULL; $files = $this->getEntitiesToView($items);
// Early opt-out if the field is empty.
if (empty($files)) {
return $elements;
}
$url = NULL;
$image_link_setting = $this->getSetting('image_link'); $image_link_setting = $this->getSetting('image_link');
// Check if the formatter involves a link. // Check if the formatter involves a link.
if ($image_link_setting == 'content') { if ($image_link_setting == 'content') {
@ -190,29 +196,28 @@ class ImageFormatter extends ImageFormatterBase implements ContainerFactoryPlugi
$cache_tags = $image_style->getCacheTags(); $cache_tags = $image_style->getCacheTags();
} }
foreach ($items as $delta => $item) { foreach ($files as $delta => $file) {
if ($item->entity) { if (isset($link_file)) {
if (isset($link_file)) { $image_uri = $file->getFileUri();
$image_uri = $item->entity->getFileUri(); $url = Url::fromUri(file_create_url($image_uri));
$url = Url::fromUri(file_create_url($image_uri));
}
// Extract field item attributes for the theme function, and unset them
// from the $item so that the field template does not re-render them.
$item_attributes = $item->_attributes;
unset($item->_attributes);
$elements[$delta] = array(
'#theme' => 'image_formatter',
'#item' => $item,
'#item_attributes' => $item_attributes,
'#image_style' => $image_style_setting,
'#url' => $url,
'#cache' => array(
'tags' => $cache_tags,
),
);
} }
// Extract field item attributes for the theme function, and unset them
// from the $item so that the field template does not re-render them.
$item = $file->_referringItem;
$item_attributes = $item->_attributes;
unset($item->_attributes);
$elements[$delta] = array(
'#theme' => 'image_formatter',
'#item' => $item,
'#item_attributes' => $item_attributes,
'#image_style' => $image_style_setting,
'#url' => $url,
'#cache' => array(
'tags' => $cache_tags,
),
);
} }
return $elements; return $elements;

View File

@ -7,6 +7,7 @@
namespace Drupal\image\Plugin\Field\FieldFormatter; namespace Drupal\image\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
use Drupal\field\FieldConfigInterface; use Drupal\field\FieldConfigInterface;
use Drupal\file\Plugin\Field\FieldFormatter\FileFormatterBase; use Drupal\file\Plugin\Field\FieldFormatter\FileFormatterBase;
@ -18,33 +19,36 @@ abstract class ImageFormatterBase extends FileFormatterBase {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function prepareView(array $entities_items) { protected function getEntitiesToView(EntityReferenceFieldItemListInterface $items) {
parent::prepareView($entities_items); // Add the default image if needed.
if ($items->isEmpty()) {
$default_image = $this->getFieldSetting('default_image');
// If we are dealing with a configurable field, look in both
// instance-level and field-level settings.
if (empty($default_image['uuid']) && $this->fieldDefinition instanceof FieldConfigInterface) {
$default_image = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('default_image');
}
// If there are no files specified at all, use the default. if (!empty($default_image['uuid']) && $file = \Drupal::entityManager()->loadEntityByUuid('file', $default_image['uuid'])) {
foreach ($entities_items as $items) { // Clone the FieldItemList into a runtime-only object for the formatter,
if ($items->isEmpty()) { // so that the fallback image can be rendered without affecting the
// Add the default image if one is found. // field values in the entity being rendered.
$default_image = $this->getFieldSetting('default_image'); $items = clone $items;
// If we are dealing with a configurable field, look in both $items->setValue(array(
// instance-level and field-level settings. 'target_id' => $file->id(),
if (empty($default_image['uuid']) && $this->fieldDefinition instanceof FieldConfigInterface) { 'alt' => $default_image['alt'],
$default_image = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('default_image'); 'title' => $default_image['title'],
} 'width' => $default_image['width'],
'height' => $default_image['height'],
if (!empty($default_image['uuid']) && ($file = \Drupal::entityManager()->loadEntityByUuid('file', $default_image['uuid']))) { 'entity' => $file,
$items->setValue(array(array( '_loaded' => TRUE,
'is_default' => TRUE, '_is_default' => TRUE,
'alt' => $default_image['alt'], ));
'title' => $default_image['title'], $file->_referringItem = $items[0];
'width' => $default_image['width'],
'height' => $default_image['height'],
'entity' => $file,
'target_id' => $file->id(),
)));
}
} }
} }
return parent::getEntitiesToView($items);
} }
} }

View File

@ -154,7 +154,7 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
$article = $this->drupalCreateNode(array('type' => 'article')); $article = $this->drupalCreateNode(array('type' => 'article'));
$article_built = $this->drupalBuildEntityView($article); $article_built = $this->drupalBuildEntityView($article);
$this->assertEqual( $this->assertEqual(
$article_built[$field_name]['#items'][0]->target_id, $article_built[$field_name][0]['#item']->target_id,
$default_images['field']->id(), $default_images['field']->id(),
format_string( format_string(
'A new article node without an image has the expected default image file ID of @fid.', 'A new article node without an image has the expected default image file ID of @fid.',
@ -166,7 +166,7 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
$page = $this->drupalCreateNode(array('type' => 'page')); $page = $this->drupalCreateNode(array('type' => 'page'));
$page_built = $this->drupalBuildEntityView($page); $page_built = $this->drupalBuildEntityView($page);
$this->assertEqual( $this->assertEqual(
$page_built[$field_name]['#items'][0]->target_id, $page_built[$field_name][0]['#item']->target_id,
$default_images['field2']->id(), $default_images['field2']->id(),
format_string( format_string(
'A new page node without an image has the expected default image file ID of @fid.', 'A new page node without an image has the expected default image file ID of @fid.',
@ -196,7 +196,7 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
$article_built = $this->drupalBuildEntityView($article = $node_storage->load($article->id())); $article_built = $this->drupalBuildEntityView($article = $node_storage->load($article->id()));
$page_built = $this->drupalBuildEntityView($page = $node_storage->load($page->id())); $page_built = $this->drupalBuildEntityView($page = $node_storage->load($page->id()));
$this->assertEqual( $this->assertEqual(
$article_built[$field_name]['#items'][0]->target_id, $article_built[$field_name][0]['#item']->target_id,
$default_images['field']->id(), $default_images['field']->id(),
format_string( format_string(
'An existing article node without an image has the expected default image file ID of @fid.', 'An existing article node without an image has the expected default image file ID of @fid.',
@ -204,7 +204,7 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
) )
); );
$this->assertEqual( $this->assertEqual(
$page_built[$field_name]['#items'][0]->target_id, $page_built[$field_name][0]['#item']->target_id,
$default_images['field2']->id(), $default_images['field2']->id(),
format_string( format_string(
'An existing page node without an image has the expected default image file ID of @fid.', 'An existing page node without an image has the expected default image file ID of @fid.',
@ -235,7 +235,7 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
// Confirm the article uses the new default. // Confirm the article uses the new default.
$this->assertEqual( $this->assertEqual(
$article_built[$field_name]['#items'][0]->target_id, $article_built[$field_name][0]['#item']->target_id,
$default_images['field_new']->id(), $default_images['field_new']->id(),
format_string( format_string(
'An existing article node without an image has the expected default image file ID of @fid.', 'An existing article node without an image has the expected default image file ID of @fid.',
@ -244,7 +244,7 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
); );
// Confirm the page remains unchanged. // Confirm the page remains unchanged.
$this->assertEqual( $this->assertEqual(
$page_built[$field_name]['#items'][0]->target_id, $page_built[$field_name][0]['#item']->target_id,
$default_images['field2']->id(), $default_images['field2']->id(),
format_string( format_string(
'An existing page node without an image has the expected default image file ID of @fid.', 'An existing page node without an image has the expected default image file ID of @fid.',
@ -275,7 +275,7 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
$page_built = $this->drupalBuildEntityView($page = $node_storage->load($page->id())); $page_built = $this->drupalBuildEntityView($page = $node_storage->load($page->id()));
// Confirm the article uses the new field (not field) default. // Confirm the article uses the new field (not field) default.
$this->assertEqual( $this->assertEqual(
$article_built[$field_name]['#items'][0]->target_id, $article_built[$field_name][0]['#item']->target_id,
$default_images['field_new']->id(), $default_images['field_new']->id(),
format_string( format_string(
'An existing article node without an image has the expected default image file ID of @fid.', 'An existing article node without an image has the expected default image file ID of @fid.',
@ -284,7 +284,7 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
); );
// Confirm the page remains unchanged. // Confirm the page remains unchanged.
$this->assertEqual( $this->assertEqual(
$page_built[$field_name]['#items'][0]->target_id, $page_built[$field_name][0]['#item']->target_id,
$default_images['field2']->id(), $default_images['field2']->id(),
format_string( format_string(
'An existing page node without an image has the expected default image file ID of @fid.', 'An existing page node without an image has the expected default image file ID of @fid.',

View File

@ -175,6 +175,13 @@ class ResponsiveImageFormatter extends ImageFormatterBase implements ContainerFa
*/ */
public function viewElements(FieldItemListInterface $items) { public function viewElements(FieldItemListInterface $items) {
$elements = array(); $elements = array();
$files = $this->getEntitiesToView($items);
// Early opt-out if the field is empty.
if (empty($files)) {
return $elements;
}
$url = NULL; $url = NULL;
// Check if the formatter involves a link. // Check if the formatter involves a link.
if ($this->getSetting('image_link') == 'content') { if ($this->getSetting('image_link') == 'content') {
@ -220,10 +227,10 @@ class ResponsiveImageFormatter extends ImageFormatterBase implements ContainerFa
$cache_tags = Cache::mergeTags($cache_tags, $image_style->getCacheTags()); $cache_tags = Cache::mergeTags($cache_tags, $image_style->getCacheTags());
} }
foreach ($items as $delta => $item) { foreach ($files as $delta => $file) {
// Link the <picture> element to the original file. // Link the <picture> element to the original file.
if (isset($link_file)) { if (isset($link_file)) {
$url = Url::fromUri(file_create_url($item->entity->getFileUri())); $url = Url::fromUri(file_create_url($file->getFileUri()));
} }
$elements[$delta] = array( $elements[$delta] = array(
'#theme' => 'responsive_image_formatter', '#theme' => 'responsive_image_formatter',
@ -232,7 +239,7 @@ class ResponsiveImageFormatter extends ImageFormatterBase implements ContainerFa
'core/picturefill', 'core/picturefill',
), ),
), ),
'#item' => $item, '#item' => $file->_referringItem,
'#image_style' => $fallback_image_style, '#image_style' => $fallback_image_style,
'#responsive_image_style_id' => $responsive_image_style ? $responsive_image_style->id() : '', '#responsive_image_style_id' => $responsive_image_style ? $responsive_image_style->id() : '',
'#url' => $url, '#url' => $url,