Commit 9008a595 authored by alexpott's avatar alexpott

Issue #1443606 by claudiu.cristea: Alt, title, width and height for default images.

parent a464d065
......@@ -58,6 +58,17 @@ function image_requirements($phase) {
return $requirements;
}
/**
* Implements hook_update_dependencies().
*/
function image_update_dependencies() {
// Convert image field and instance setting 'default_image' from integer to
// array only after fields and instances were converted to config.
$dependencies['image'][8003] = array('field' => 8006);
return $dependencies;
}
/**
* Loads all effects for an image style from backend.
*
......@@ -237,3 +248,42 @@ function image_update_8002() {
'image_style_preview_image' => 'preview_image',
));
}
/**
* Convert image field and instance setting 'default_image' from integer to
* array by adding alt, title, width, and height options.
*/
function image_update_8003() {
$image_factory = \Drupal::service('image.factory');
foreach (array('field', 'instance') as $type) {
$prefix = "field.$type";
foreach (config_get_storage_names_with_prefix($prefix) as $config_id) {
$config = \Drupal::config($config_id);
$is_image = ($type == 'field' && $config->get('type') == 'image') || ($type == 'instance' && $config->get('field_type') == 'image');
// Not dealing with an image field or image field instance?
if (!$is_image) {
continue;
}
$width = 0;
$height = 0;
if ($fid = (int) $config->get('settings.default_image')) {
$uri = db_query('SELECT fid FROM {file_managed} WHERE fid = :fid', array(':fid' => $fid))->fetchField();
if ($uri) {
$image = $image_factory->get($uri);
$width = $image->getWidth();
$height = $image->getHeight();
}
}
$default_image = array(
'fid' => $fid ?: NULL,
'alt' => '',
'title' => '',
'width' => $width ?: NULL,
'height' => $height ?: NULL,
);
$config
->set('settings.default_image', $default_image)
->save();
}
}
}
......@@ -424,14 +424,31 @@ function image_entity_presave(EntityInterface $entity, $type) {
elseif ($entity instanceof Field) {
$field = $entity;
}
if ($field && $field->type == 'image' && is_array($entity->settings['default_image'])) {
if (!empty($entity->settings['default_image'][0])) {
$entity->settings['default_image'] = $entity->settings['default_image'][0];
}
else {
$entity->settings['default_image'] = 0;
// Exit, if not saving an image field or image field instance entity.
if (!$field || $field->type != 'image') {
return;
}
if (!empty($entity->settings['default_image']['fid'][0])) {
$entity->settings['default_image']['fid'] = $entity->settings['default_image']['fid'][0];
}
if ($fid = $entity->settings['default_image']['fid']) {
$original_fid = isset($entity->original) ? $entity->original->settings['default_image']['fid'] : NULL;
if ($fid != $original_fid) {
$image = \Drupal::service('image.factory')->get(file_load($fid)->getFileUri());
$entity->settings['default_image']['width'] = $image->getWidth();
$entity->settings['default_image']['height'] = $image->getHeight();
}
}
else {
$entity->settings['default_image'] = array(
'fid' => NULL,
'alt' => '',
'title' => '',
'width' => NULL,
'height' => NULL,
);
}
}
/**
......@@ -446,10 +463,11 @@ function image_field_entity_update(FieldInterface $field) {
$prior_field = $field->original;
// The value of a managed_file element can be an array if #extended == TRUE.
$fid_new = (isset($field->settings['default_image']['fids']) ? $field->settings['default_image']['fids'] : $field->settings['default_image']);
$fid_old = (isset($prior_field->settings['default_image']['fids']) ? $prior_field->settings['default_image']['fids'] : $prior_field->settings['default_image']);
// Ensure sure that fid_new and old are arrays, because default_image might
// be the fallback value 0, see image_field_info().
$fid_new = isset($field->settings['default_image']['fid']['fids']) ? $field->settings['default_image']['fid']['fids'] : $field->settings['default_image']['fid'];
$fid_old = isset($prior_field->settings['default_image']['fid']['fids']) ? $prior_field->settings['default_image']['fid']['fids'] : $prior_field->settings['default_image']['fid'];
// Ensure that $fid_new and $fid_old are arrays, because the field setting
// 'default_image' key 'fid' might be the fallback value 0, see the annotation
// block of \Drupal\image\Plugin\Field\FieldType\ImageItem.
$fid_old = (array) $fid_old;
$fid_new = (array) $fid_new;
......@@ -492,11 +510,11 @@ function image_field_instance_update(FieldInstanceInterface $field_instance) {
// The value of a managed_file element can be an array if the #extended
// property is set to TRUE.
$fid_new = $field_instance->settings['default_image'];
$fid_new = $field_instance->settings['default_image']['fid'];
if (isset($fid_new['fids'])) {
$fid_new = $fid_new['fids'];
}
$fid_old = $prior_instance->settings['default_image'];
$fid_old = $prior_instance->settings['default_image']['fid'];
if (isset($fid_old['fids'])) {
$fid_old = $fid_old['fids'];
}
......@@ -534,7 +552,7 @@ function image_field_entity_delete(FieldInterface $field) {
}
// The value of a managed_file element can be an array if #extended == TRUE.
$fid = (isset($field->settings['default_image']['fids']) ? $field->settings['default_image']['fids'] : $field->settings['default_image']);
$fid = (isset($field->settings['default_image']['fid']['fids']) ? $field->settings['default_image']['fid']['fids'] : $field->settings['default_image']['fid']);
if ($fid && ($file = file_load($fid[0]))) {
file_usage()->delete($file, 'image', 'default_image', $field->uuid);
}
......@@ -552,7 +570,7 @@ function image_field_instance_delete(FieldInstanceInterface $field_instance) {
// The value of a managed_file element can be an array if the #extended
// property is set to TRUE.
$fid = $field_instance->settings['default_image'];
$fid = $field_instance->settings['default_image']['fid'];
if (is_array($fid)) {
$fid = $fid['fid'];
}
......
......@@ -25,18 +25,20 @@ public function prepareView(array $entities_items) {
foreach ($entities_items as $items) {
if ($items->isEmpty()) {
// Add the default image if one is found.
$fid = $this->getFieldSetting('default_image');
$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($fid) && $this->fieldDefinition instanceof FieldInstanceInterface) {
$fid = $this->fieldDefinition->getField()->getFieldSetting('default_image');
if (empty($default_image['fid']) && $this->fieldDefinition instanceof FieldInstanceInterface) {
$default_image = $this->fieldDefinition->getField()->getFieldSetting('default_image');
}
if ($fid && ($file = file_load($fid))) {
if (!empty($default_image['fid']) && ($file = file_load($default_image['fid']))) {
$items->setValue(array(array(
'is_default' => TRUE,
'alt' => '',
'title' => '',
'alt' => $default_image['alt'],
'title' => $default_image['title'],
'width' => $default_image['width'],
'height' => $default_image['height'],
'entity' => $file,
'target_id' => $file->id(),
)));
......
......@@ -19,7 +19,13 @@
* description = @Translation("This field stores the ID of an image file as an integer value."),
* settings = {
* "uri_scheme" = "",
* "default_image" = "0",
* "default_image" = {
* "fid" = NULL,
* "alt" = "",
* "title" = "",
* "width" = NULL,
* "height" = NULL
* },
* "column_groups" = {
* "file" = {
* "label" = @Translation("File"),
......@@ -45,7 +51,13 @@
* "title_field_required" = "0",
* "max_resolution" = "",
* "min_resolution" = "",
* "default_image" = "0"
* "default_image" = {
* "fid" = NULL,
* "alt" = "",
* "title" = "",
* "width" = NULL,
* "height" = NULL
* }
* },
* default_widget = "image_image",
* default_formatter = "image",
......@@ -153,13 +165,9 @@ public function settingsForm(array $form, array &$form_state, $has_data) {
'#description' => t('Select where the final files should be stored. Private file storage has significantly more overhead than public files, but allows restricted access to files within this field.'),
);
$element['default_image'] = array(
'#title' => t('Default image'),
'#type' => 'managed_file',
'#description' => t('If no image is uploaded, this image will be shown on display.'),
'#default_value' => empty($settings['default_image']) ? array() : array($settings['default_image']),
'#upload_location' => $settings['uri_scheme'] . '://default_images/',
);
// Add default_image element.
static::defaultImageForm($element, $settings);
$element['default_image']['#description'] = t('If no image is uploaded, this image will be shown on display.');
return $element;
}
......@@ -269,14 +277,9 @@ public function instanceSettingsForm(array $form, array &$form_state) {
),
);
// Add the default image to the instance.
$element['default_image'] = array(
'#title' => t('Default image'),
'#type' => 'managed_file',
'#description' => t("If no image is uploaded, this image will be shown on display and will override the field's default image."),
'#default_value' => empty($settings['default_image']) ? array() : array($settings['default_image']),
'#upload_location' => $settings['uri_scheme'] . '://default_images/',
);
// Add default_image element.
static::defaultImageForm($element, $settings);
$element['default_image']['#description'] = t("If no image is uploaded, this image will be shown on display and will override the field's default image.");
return $element;
}
......@@ -316,6 +319,51 @@ public static function validateResolution($element, &$form_state) {
}
}
/**
* Builds the default_image details element.
*
* @param array $element
* The form associative array passed by reference.
* @param array $settings
* The field settings array.
*/
protected function defaultImageForm(array &$element, array $settings) {
$element['default_image'] = array(
'#type' => 'details',
'#title' => t('Default image'),
'#open' => TRUE,
);
$element['default_image']['fid'] = array(
'#type' => 'managed_file',
'#title' => t('Image'),
'#description' => t('Image to be shown if no image is uploaded.'),
'#default_value' => empty($settings['default_image']['fid']) ? array() : array($settings['default_image']['fid']),
'#upload_location' => $settings['uri_scheme'] . '://default_images/',
);
$element['default_image']['alt'] = array(
'#type' => 'textfield',
'#title' => t('Alternate text'),
'#description' => t('This text will be used by screen readers, search engines, and when the image cannot be loaded.'),
'#default_value' => $settings['default_image']['alt'],
'#maxlength' => 512,
);
$element['default_image']['title'] = array(
'#type' => 'textfield',
'#title' => t('Title'),
'#description' => t('The title attribute is used as a tooltip when the mouse hovers over the image.'),
'#default_value' => $settings['default_image']['title'],
'#maxlength' => 1024,
);
$element['default_image']['width'] = array(
'#type' => 'value',
'#value' => $settings['default_image']['width'],
);
$element['default_image']['height'] = array(
'#type' => 'value',
'#value' => $settings['default_image']['height'],
);
}
/**
* {@inheritdoc}
*/
......
......@@ -42,11 +42,19 @@ public function testDefaultImages() {
// Create an image field and add an instance to the article content type.
$field_name = strtolower($this->randomName());
$field_settings = array(
'default_image' => $default_images['field']->id(),
$field_settings['default_image'] = array(
'fid' => $default_images['field']->id(),
'alt' => '',
'title' => '',
'width' => 0,
'height' => 0,
);
$instance_settings = array(
'default_image' => $default_images['instance']->id(),
$instance_settings['default_image'] = array(
'fid' => $default_images['instance']->id(),
'alt' => '',
'title' => '',
'width' => 0,
'height' => 0,
);
$widget_settings = array(
'preview_image_style' => 'medium',
......@@ -54,20 +62,22 @@ public function testDefaultImages() {
$instance = $this->createImageField($field_name, 'article', $field_settings, $instance_settings, $widget_settings);
// The instance default image id should be 2.
$this->assertEqual($instance->getFieldSetting('default_image'), $default_images['instance']->id());
$default_image = $instance->getFieldSetting('default_image');
$this->assertEqual($default_image['fid'], $default_images['instance']->id());
// Also test \Drupal\field\Entity\FieldInstance::getFieldSetting().
$instance_field_settings = $instance->getFieldSettings();
$this->assertEqual($instance_field_settings['default_image'], $default_images['instance']->id());
$this->assertEqual($instance_field_settings['default_image']['fid'], $default_images['instance']->id());
$field = $instance->getField();
// The field default image id should be 1.
$this->assertEqual($field->getFieldSetting('default_image'), $default_images['field']->id());
$default_image = $field->getFieldSetting('default_image');
$this->assertEqual($default_image['fid'], $default_images['field']->id());
// Also test \Drupal\field\Entity\Field::getFieldSettings().
$field_field_settings = $field->getFieldSettings();
$this->assertEqual($field_field_settings['default_image'], $default_images['field']->id());
$this->assertEqual($field_field_settings['default_image']['fid'], $default_images['field']->id());
// Add another instance with another default image to the page content type.
$instance2 = entity_create('field_instance', array(
......@@ -77,7 +87,13 @@ public function testDefaultImages() {
'label' => $instance->label(),
'required' => $instance->required,
'settings' => array(
'default_image' => $default_images['instance2']->id(),
'default_image' => array(
'fid' => $default_images['instance2']->id(),
'alt' => '',
'title' => '',
'width' => 0,
'height' => 0,
),
),
));
$instance2->save();
......@@ -93,7 +109,7 @@ public function testDefaultImages() {
// Confirm the defaults are present on the article field settings form.
$this->drupalGet("admin/structure/types/manage/article/fields/$instance->id/field");
$this->assertFieldByXpath(
'//input[@name="field[settings][default_image][fids]"]',
'//input[@name="field[settings][default_image][fid][fids]"]',
$default_images['field']->id(),
format_string(
'Article image field default equals expected file ID of @fid.',
......@@ -103,7 +119,7 @@ public function testDefaultImages() {
// Confirm the defaults are present on the article field edit form.
$this->drupalGet("admin/structure/types/manage/article/fields/$instance->id");
$this->assertFieldByXpath(
'//input[@name="instance[settings][default_image][fids]"]',
'//input[@name="instance[settings][default_image][fid][fids]"]',
$default_images['instance']->id(),
format_string(
'Article image field instance default equals expected file ID of @fid.',
......@@ -114,7 +130,7 @@ public function testDefaultImages() {
// Confirm the defaults are present on the page field settings form.
$this->drupalGet("admin/structure/types/manage/page/fields/$instance->id/field");
$this->assertFieldByXpath(
'//input[@name="field[settings][default_image][fids]"]',
'//input[@name="field[settings][default_image][fid][fids]"]',
$default_images['field']->id(),
format_string(
'Page image field default equals expected file ID of @fid.',
......@@ -124,7 +140,7 @@ public function testDefaultImages() {
// Confirm the defaults are present on the page field edit form.
$this->drupalGet("admin/structure/types/manage/page/fields/$instance2->id");
$this->assertFieldByXpath(
'//input[@name="instance[settings][default_image][fids]"]',
'//input[@name="instance[settings][default_image][fid][fids]"]',
$default_images['instance2']->id(),
format_string(
'Page image field instance default equals expected file ID of @fid.',
......@@ -157,13 +173,13 @@ public function testDefaultImages() {
);
// Upload a new default for the field.
$field->settings['default_image'] = array($default_images['field_new']->id());
$field->settings['default_image']['fid'] = array($default_images['field_new']->id());
$field->save();
// Confirm that the new default is used on the article field settings form.
$this->drupalGet("admin/structure/types/manage/article/fields/$instance->id/field");
$this->assertFieldByXpath(
'//input[@name="field[settings][default_image][fids]"]',
'//input[@name="field[settings][default_image][fid][fids]"]',
$default_images['field_new']->id(),
format_string(
'Updated image field default equals expected file ID of @fid.',
......@@ -192,14 +208,14 @@ public function testDefaultImages() {
);
// Upload a new default for the article's field instance.
$instance->settings['default_image'] = $default_images['instance_new']->id();
$instance->settings['default_image']['fid'] = $default_images['instance_new']->id();
$instance->save();
// Confirm the new field instance default is used on the article field
// admin form.
$this->drupalGet("admin/structure/types/manage/article/fields/$instance->id");
$this->assertFieldByXpath(
'//input[@name="instance[settings][default_image][fids]"]',
'//input[@name="instance[settings][default_image][fid][fids]"]',
$default_images['instance_new']->id(),
format_string(
'Updated article image field instance default equals expected file ID of @fid.',
......@@ -231,13 +247,13 @@ public function testDefaultImages() {
);
// Remove the instance default from articles.
$instance->settings['default_image'] = 0;
$instance->settings['default_image']['fid'] = 0;
$instance->save();
// Confirm the article field instance default has been removed.
$this->drupalGet("admin/structure/types/manage/article/fields/$instance->id");
$this->assertFieldByXpath(
'//input[@name="instance[settings][default_image][fids]"]',
'//input[@name="instance[settings][default_image][fid][fids]"]',
'',
'Updated article image field instance default has been successfully removed.'
);
......
......@@ -241,18 +241,27 @@ function testImageFieldDefaultImage() {
// Add a default image to the public imagefield instance.
$images = $this->drupalGetTestFiles('image');
$alt = $this->randomString(512);
$title = $this->randomString(1024);
$edit = array(
'files[field_settings_default_image]' => drupal_realpath($images[0]->uri),
'files[field_settings_default_image_fid]' => drupal_realpath($images[0]->uri),
'field[settings][default_image][alt]' => $alt,
'field[settings][default_image][title]' => $title,
);
$this->drupalPostForm("admin/structure/types/manage/article/fields/node.article.$field_name/field", $edit, t('Save field settings'));
// Clear field info cache so the new default image is detected.
field_info_cache_clear();
$field = field_info_field('node', $field_name);
$file = file_load($field->getFieldSetting('default_image'));
$default_image = $field->getFieldSetting('default_image');
$file = file_load($default_image['fid']);
$this->assertTrue($file->isPermanent(), 'The default image status is permanent.');
$image = array(
'#theme' => 'image',
'#uri' => $file->getFileUri(),
'#alt' => $alt,
'#title' => $title,
'#width' => 40,
'#height' => 20,
);
$default_output = drupal_render($image);
$this->drupalGet('node/' . $node->id());
......@@ -275,27 +284,31 @@ function testImageFieldDefaultImage() {
// Remove default image from the field and make sure it is no longer used.
$edit = array(
'field[settings][default_image][fids]' => 0,
'field[settings][default_image][fid][fids]' => 0,
);
$this->drupalPostForm("admin/structure/types/manage/article/fields/node.article.$field_name/field", $edit, t('Save field settings'));
// Clear field info cache so the new default image is detected.
field_info_cache_clear();
$field = field_info_field('node', $field_name);
$this->assertFalse($field->getFieldSetting('default_image'), 'Default image removed from field.');
$default_image = $field->getFieldSetting('default_image');
$this->assertFalse($default_image['fid'], 'Default image removed from field.');
// Create an image field that uses the private:// scheme and test that the
// default image works as expected.
$private_field_name = strtolower($this->randomName());
$this->createImageField($private_field_name, 'article', array('uri_scheme' => 'private'));
// Add a default image to the new field.
$edit = array(
'files[field_settings_default_image]' => drupal_realpath($images[1]->uri),
'files[field_settings_default_image_fid]' => drupal_realpath($images[1]->uri),
'field[settings][default_image][alt]' => $alt,
'field[settings][default_image][title]' => $title,
);
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.' . $private_field_name . '/field', $edit, t('Save field settings'));
// Clear field info cache so the new default image is detected.
field_info_cache_clear();
$private_field = field_info_field('node', $private_field_name);
$file = file_load($private_field->getFieldSetting('default_image'));
$default_image = $private_field->getFieldSetting('default_image');
$file = file_load($default_image['fid']);
$this->assertEqual('private', file_uri_scheme($file->getFileUri()), 'Default image uses private:// scheme.');
$this->assertTrue($file->isPermanent(), 'The default image status is permanent.');
// Create a new node with no image attached and ensure that default private
......@@ -304,6 +317,10 @@ function testImageFieldDefaultImage() {
$image = array(
'#theme' => 'image',
'#uri' => $file->getFileUri(),
'#alt' => $alt,
'#title' => $title,
'#width' => 40,
'#height' => 20,
);
$default_output = drupal_render($image);
$this->drupalGet('node/' . $node->id());
......
......@@ -145,4 +145,41 @@ public function sortByKey(array $data) {
}
return $data;
}
/**
* Tests if the field and instance setting 'default_image' has been
* successfully converted from an integer to an associative array.
*/
public function testImageFieldDefaultImageUpgrade() {
// Perform upgrade.
$this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.');
// While this test is testing the upgrade path against a Drupal 7 'standard'
// profile installation we assume that node bundle 'article', having the
// 'field_image' image field, is installed too.
$cases = array('field.field.node.field_image', 'field.instance.node.article.field_image');
foreach ($cases as $case) {
$default_image = \Drupal::config($case)->get('settings.default_image');
// Check is was converted to an array.
$this->assertTrue(is_array($default_image));
// Check if 'default_image' contains a key named 'fid'. It might be an
// integer or NULL.
$this->assertTrue(array_key_exists('fid', $default_image));
// Check if 'alt' key exists and is a string.
$this->assertTrue(isset($default_image['alt']) && is_string($default_image['alt']));
// Check if 'title' key exists and is a string.
$this->assertTrue(isset($default_image['title']) && is_string($default_image['title']));
// Check if 'width' key exists. It might be an integer or NULL.
$this->assertTrue(array_key_exists('width', $default_image));
// Check if 'height' key exists. It might be an integer or NULL.
$this->assertTrue(array_key_exists('height', $default_image));
}
}
}
......@@ -44,7 +44,7 @@ public function testUserPictureUpgrade() {
$instance = field_info_instance('user', 'user_picture', 'user');
// We explicitly avoid using the getFieldSetting() method here, since it
// merges field and instance settings.
$file = entity_load('file', $instance->settings['default_image'][0]);
$file = entity_load('file', $instance->settings['default_image']['fid']);
$this->assertTrue($file, 'Default user picture has been migrated.');
$this->assertEqual($file->getFileUri(), 'public://user_pictures_dir/druplicon.png', 'File id matches the uri expected.');
$this->assertEqual($file->getFilename(), 'druplicon.png');
......
......@@ -272,7 +272,13 @@ function user_install_picture_field() {
'indexes' => array('target_id' => array('target_id')),
'settings' => array(
'uri_scheme' => 'public',
'default_image' => FALSE,
'default_image' => array(
'fid' => NULL,
'alt' => '',
'title' => '',
'width' => NULL,
'height' => NULL,
),
),
);
entity_create('field_entity', $field)->save();
......@@ -292,7 +298,13 @@ function user_install_picture_field() {
'title_field' => 0,
'max_resolution' => '85x85',
'min_resolution' => '',
'default_image' => 0,
'default_image' => array(
'fid' => NULL,
'alt' => '',
'title' => '',
'width' => NULL,
'height' => NULL,
),
),
);
entity_create('field_instance', $instance)->save();
......@@ -611,9 +623,9 @@ function user_update_8011() {
if (!\Drupal::moduleHandler()->moduleExists('image')) {
$old_schema = \Drupal::moduleHandler()->install(array('image'));
if ($old_schema['image'] == SCHEMA_UNINSTALLED) {
// Install image.module with schema version 8002 as a previous version
// Install image.module with schema version 8003 as a previous version
// would have to create tables that would be removed again.
update_set_schema('image', 8002);
update_set_schema('image', 8003);
// Inlined version of image_install(), make sure that the styles
// directory exists.
$directory = update_variable_get('file_default_scheme', 'public') . '://styles';
......@@ -720,7 +732,7 @@ function user_update_8011() {
'title_field' => 0,
'max_resolution' => update_variable_get('user_picture_dimensions', '85x85'),
'min_resolution' => '',
'default_image' => !empty($default_image_fid) ? array($default_image_fid) : array(),
'default_image' => !empty($default_image_fid) ? $default_image_fid : 0,
),
);
_update_8003_field_create_instance($field, $instance);
......
......@@ -7,7 +7,12 @@ module: image
active: '1'
settings:
uri_scheme: public
default_image: false
default_image:
fid: null
alt: ''
title: ''
width: null
height: null
column_groups:
file:
label: File
......
......@@ -19,6 +19,11 @@ settings:
title_field: ''
alt_field_required: 0
title_field_required: 0
default_image: 0
default_image:
fid: null
alt: ''
title: ''
width: null
height: null
status: 1
langcode: und
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment