diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 36bd18a9c30..a52d917bff9 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -996,6 +996,7 @@ function field_view_field(EntityInterface $entity, $field_name, $display_options * An array of field items keyed by delta if available, FALSE otherwise. */ function field_get_items(EntityInterface $entity, $field_name, $langcode = NULL) { + $entity = $entity->getBCEntity(); $langcode = field_language($entity, $field_name, $langcode); return isset($entity->{$field_name}[$langcode]) ? $entity->{$field_name}[$langcode] : FALSE; } diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc index 9d58d1d4e21..e3a7d7ce384 100644 --- a/core/modules/file/file.field.inc +++ b/core/modules/file/file.field.inc @@ -29,6 +29,7 @@ function file_field_info() { ), 'default_widget' => 'file_generic', 'default_formatter' => 'file_default', + 'field item class' => '\Drupal\file\Type\FileItem', ), ); } diff --git a/core/modules/file/file.module b/core/modules/file/file.module index 75c4f84df78..69fcbf37570 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -590,7 +590,7 @@ function file_theme() { return array( // file.module. 'file_link' => array( - 'variables' => array('file' => NULL, 'icon_directory' => NULL), + 'variables' => array('file' => NULL, 'icon_directory' => NULL, 'description' => NULL), ), 'file_icon' => array( 'variables' => array('file' => NULL, 'icon_directory' => NULL), @@ -1238,6 +1238,7 @@ function file_managed_file_pre_render($element) { * - icon_directory: (optional) A path to a directory of icons to be used for * files. Defaults to the value of the "icon.directory" * variable. + * - description: A description to be displayed instead of the filename. * * @ingroup themeable */ @@ -1247,7 +1248,7 @@ function theme_file_link($variables) { $url = file_create_url($file->uri); // theme_file_icon() requires a file entity, make sure it gets one. - $icon = theme('file_icon', array('file' => ($file instanceof File) ? $file : file_load($file->fid), 'icon_directory' => $icon_directory)); + $icon = theme('file_icon', array('file' => $file, 'icon_directory' => $icon_directory)); // Set options as per anchor format described at // http://microformats.org/wiki/file-format-examples @@ -1258,11 +1259,11 @@ function theme_file_link($variables) { ); // Use the description as the link text if available. - if (empty($file->description)) { + if (empty($variables['description'])) { $link_text = $file->filename; } else { - $link_text = $file->description; + $link_text = $variables['description']; $options['attributes']['title'] = check_plain($file->filename); } diff --git a/core/modules/file/lib/Drupal/file/Plugin/field/formatter/GenericFileFormatter.php b/core/modules/file/lib/Drupal/file/Plugin/field/formatter/GenericFileFormatter.php index f3f89a9c5d0..8cd6d8968e8 100644 --- a/core/modules/file/lib/Drupal/file/Plugin/field/formatter/GenericFileFormatter.php +++ b/core/modules/file/lib/Drupal/file/Plugin/field/formatter/GenericFileFormatter.php @@ -35,7 +35,8 @@ class GenericFileFormatter extends FormatterBase { foreach ($items as $delta => $item) { $elements[$delta] = array( '#theme' => 'file_link', - '#file' => (object) $item, + '#file' => file_load($item['fid']), + '#description' => $item['description'], ); } diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php index 7e9094f81f6..81e87d0a004 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php @@ -41,7 +41,9 @@ class FileFieldDisplayTest extends FileFieldTestBase { // Create a new node *without* the file field set, and check that the field // is not shown for each node display. $node = $this->drupalCreateNode(array('type' => $type_name)); - $file_formatters = array('file_default', 'file_table', 'file_url_plain', 'hidden'); + // Check file_default last as the assertions below assume that this is the + // case. + $file_formatters = array('file_table', 'file_url_plain', 'hidden', 'file_default'); foreach ($file_formatters as $formatter) { $edit = array( "fields[$field_name][type]" => $formatter, @@ -55,7 +57,6 @@ class FileFieldDisplayTest extends FileFieldTestBase { // Create a new node with the uploaded file. $nid = $this->uploadNodeFile($test_file, $field_name, $type_name); - $this->drupalGet('node/' . $nid . '/edit'); // Check that the default formatter is displaying with the file name. $node = node_load($nid, TRUE); @@ -69,5 +70,13 @@ class FileFieldDisplayTest extends FileFieldTestBase { $this->assertNoRaw($default_output, t('Field is hidden when "display" option is unchecked.')); + // Add a description and make sure that it is displayed. + $description = $this->randomName(); + $edit = array( + $field_name . '[' . LANGUAGE_NOT_SPECIFIED . '][0][description]' => $description, + $field_name . '[' . LANGUAGE_NOT_SPECIFIED . '][0][display]' => TRUE, + ); + $this->drupalPost('node/' . $nid . '/edit', $edit, t('Save and keep published')); + $this->assertText($description); } } diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php index 28a5d5d268c..05b0cadd95d 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php @@ -285,7 +285,7 @@ class FileFieldWidgetTest extends FileFieldTestBase { $this->drupalLogin($user); $comment = comment_load($cid); - $comment_file = file_load($comment->{'field_' . $name}[LANGUAGE_NOT_SPECIFIED][0]['fid']); + $comment_file = $comment->{'field_' . $name}->entity; $this->assertFileExists($comment_file, t('New file saved to disk on node creation.')); // Test authenticated file download. $url = file_create_url($comment_file->uri); diff --git a/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php b/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php new file mode 100644 index 00000000000..be275f1df15 --- /dev/null +++ b/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php @@ -0,0 +1,103 @@ + 'File field item API', + 'description' => 'Tests using entity fields of the file field type.', + 'group' => 'File', + ); + } + + public function setUp() { + parent::setUp(); + + $this->installSchema('file', 'file_managed'); + $this->installSchema('file', 'file_usage'); + + $field = array( + 'field_name' => 'file_test', + 'type' => 'file', + 'cardinality' => FIELD_CARDINALITY_UNLIMITED, + ); + field_create_field($field); + $instance = array( + 'entity_type' => 'entity_test', + 'field_name' => 'file_test', + 'bundle' => 'entity_test', + 'widget' => array( + 'type' => 'options_select', + ), + ); + field_create_instance($instance); + file_put_contents('public://example.txt', $this->randomName()); + $this->file = entity_create('file', array( + 'uri' => 'public://example.txt', + )); + $this->file->save(); + } + + /** + * Tests using entity fields of the file field type. + */ + public function testFileItem() { + // Create a test entity with the + $entity = entity_create('entity_test', array()); + $entity->file_test->fid = $this->file->id(); + $entity->file_test->display = 1; + $entity->file_test->description = $description = $this->randomName(); + $entity->name->value = $this->randomName(); + $entity->save(); + + $entity = entity_load('entity_test', $entity->id()); + $this->assertTrue($entity->file_test instanceof FieldInterface, 'Field implements interface.'); + $this->assertTrue($entity->file_test[0] instanceof FieldItemInterface, 'Field item implements interface.'); + $this->assertEqual($entity->file_test->fid, $this->file->id()); + $this->assertEqual($entity->file_test->display, 1); + $this->assertEqual($entity->file_test->description, $description); + $this->assertEqual($entity->file_test->entity->uri, $this->file->uri); + $this->assertEqual($entity->file_test->entity->id(), $this->file->id()); + $this->assertEqual($entity->file_test->entity->uuid(), $this->file->uuid()); + + // Make sure the computed files reflects updates to the file. + file_put_contents('public://example-2.txt', $this->randomName()); + $file2 = entity_create('file', array( + 'uri' => 'public://example-2.txt', + )); + $file2->save(); + + $entity->file_test->fid = $file2->id(); + $this->assertEqual($entity->file_test->entity->id(), $file2->id()); + $this->assertEqual($entity->file_test->entity->uri, $file2->uri); + } + +} diff --git a/core/modules/file/lib/Drupal/file/Type/FileItem.php b/core/modules/file/lib/Drupal/file/Type/FileItem.php new file mode 100644 index 00000000000..e1c7d24e33e --- /dev/null +++ b/core/modules/file/lib/Drupal/file/Type/FileItem.php @@ -0,0 +1,93 @@ + 'integer', + 'label' => t('Referenced file id.'), + ); + static::$propertyDefinitions['display'] = array( + 'type' => 'boolean', + 'label' => t('Flag to control whether this file should be displayed when viewing content.'), + ); + static::$propertyDefinitions['description'] = array( + 'type' => 'string', + 'label' => t('A description of the file.'), + ); + static::$propertyDefinitions['entity'] = array( + 'type' => 'entity', + 'constraints' => array( + 'EntityType' => 'file', + ), + 'label' => t('File'), + 'description' => t('The referenced file'), + // The entity object is computed out of the fid. + 'computed' => TRUE, + 'read-only' => FALSE, + 'settings' => array('id source' => 'fid'), + ); + } + return static::$propertyDefinitions; + } + + /** + * Overrides \Drupal\Core\Entity\Field\FieldItemBase::setValue(). + */ + public function setValue($values) { + // Treat the values as property value of the entity field, if no array + // is given. + if (!is_array($values)) { + $values = array('entity' => $values); + } + + if (isset($values['display'])) { + $this->properties['display']->setValue($values['display']); + } + if (isset($values['description'])) { + $this->properties['description']->setValue($values['description']); + } + + // Entity is computed out of the ID, so we only need to update the ID. Only + // set the entity field if no ID is given. + if (isset($values['fid'])) { + $this->properties['fid']->setValue($values['fid']); + } + elseif (isset($values['entity'])) { + $this->properties['entity']->setValue($values['entity']); + } + else { + $this->properties['entity']->setValue(NULL); + } + unset($values['entity'], $values['fid'], $values['display'], $values['description']); + if ($values) { + throw new \InvalidArgumentException('Property ' . key($values) . ' is unknown.'); + } + } + +}