upgrades core to 8.4.2
This commit is contained in:
@@ -14,8 +14,7 @@ field_default_image:
|
||||
type: mapping
|
||||
mapping:
|
||||
uuid:
|
||||
type: string
|
||||
label: 'Image'
|
||||
type: uuid
|
||||
alt:
|
||||
type: label
|
||||
label: 'Alternative text'
|
||||
|
||||
@@ -21,7 +21,7 @@ image.style.*:
|
||||
weight:
|
||||
type: integer
|
||||
uuid:
|
||||
type: string
|
||||
type: uuid
|
||||
|
||||
image.effect.*:
|
||||
type: mapping
|
||||
|
||||
@@ -51,9 +51,9 @@
|
||||
}
|
||||
|
||||
@keyframes quickedit-image-spin {
|
||||
0% {transform: rotate(0deg);}
|
||||
50% {transform: rotate(180deg);}
|
||||
100% {transform: rotate(360deg);}
|
||||
0% { transform: rotate(0deg); }
|
||||
50% { transform: rotate(180deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.quickedit-image-text {
|
||||
|
||||
@@ -8,8 +8,8 @@ dependencies:
|
||||
- file
|
||||
configure: entity.image_style.collection
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1502903957
|
||||
datestamp: 1509719929
|
||||
|
||||
@@ -16,6 +16,8 @@ use Drupal\image\Entity\ImageStyle;
|
||||
* Image style constant for user presets in the database.
|
||||
*
|
||||
* @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.0.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1820974
|
||||
*/
|
||||
const IMAGE_STORAGE_NORMAL = 1;
|
||||
|
||||
@@ -23,6 +25,8 @@ const IMAGE_STORAGE_NORMAL = 1;
|
||||
* Image style constant for user presets that override module-defined presets.
|
||||
*
|
||||
* @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.0.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1820974
|
||||
*/
|
||||
const IMAGE_STORAGE_OVERRIDE = 2;
|
||||
|
||||
@@ -30,6 +34,8 @@ const IMAGE_STORAGE_OVERRIDE = 2;
|
||||
* Image style constant for module-defined presets in code.
|
||||
*
|
||||
* @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.0.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1820974
|
||||
*/
|
||||
const IMAGE_STORAGE_DEFAULT = 4;
|
||||
|
||||
@@ -37,6 +43,8 @@ const IMAGE_STORAGE_DEFAULT = 4;
|
||||
* Image style constant to represent an editable preset.
|
||||
*
|
||||
* @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.0.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1820974
|
||||
*/
|
||||
define('IMAGE_STORAGE_EDITABLE', IMAGE_STORAGE_NORMAL | IMAGE_STORAGE_OVERRIDE);
|
||||
|
||||
@@ -44,6 +52,8 @@ define('IMAGE_STORAGE_EDITABLE', IMAGE_STORAGE_NORMAL | IMAGE_STORAGE_OVERRIDE);
|
||||
* Image style constant to represent any module-based preset.
|
||||
*
|
||||
* @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.0.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1820974
|
||||
*/
|
||||
define('IMAGE_STORAGE_MODULE', IMAGE_STORAGE_OVERRIDE | IMAGE_STORAGE_DEFAULT);
|
||||
|
||||
@@ -292,10 +302,28 @@ function template_preprocess_image_style(&$variables) {
|
||||
'#width' => $dimensions['width'],
|
||||
'#height' => $dimensions['height'],
|
||||
'#attributes' => $variables['attributes'],
|
||||
'#uri' => $style->buildUrl($variables['uri']),
|
||||
'#style_name' => $variables['style_name'],
|
||||
];
|
||||
|
||||
// If the current image toolkit supports this file type, prepare the URI for
|
||||
// the derivative image. If not, just use the original image resized to the
|
||||
// dimensions specified by the style.
|
||||
if ($style->supportsUri($variables['uri'])) {
|
||||
$variables['image']['#uri'] = $style->buildUrl($variables['uri']);
|
||||
}
|
||||
else {
|
||||
$variables['image']['#uri'] = $variables['uri'];
|
||||
// Don't render the image by default, but allow other preprocess functions
|
||||
// to override that if they need to.
|
||||
$variables['image']['#access'] = FALSE;
|
||||
|
||||
// Inform the site builders why their image didn't work.
|
||||
\Drupal::logger('image')->warning('Could not apply @style image style to @uri because the style does not support it.', [
|
||||
'@style' => $style->label(),
|
||||
'@uri' => $variables['uri'],
|
||||
]);
|
||||
}
|
||||
|
||||
if (isset($variables['alt']) || array_key_exists('alt', $variables)) {
|
||||
$variables['image']['#alt'] = $variables['alt'];
|
||||
}
|
||||
@@ -404,7 +432,7 @@ function image_field_storage_config_update(FieldStorageConfigInterface $field_st
|
||||
if ($file_new && (file_uri_scheme($file_new->getFileUri()) != $field_storage->getSetting('uri_scheme'))) {
|
||||
$directory = $field_storage->getSetting('uri_scheme') . '://default_images/';
|
||||
file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
|
||||
file_move($file_new, $directory . $file_new->filename);
|
||||
file_move($file_new, $directory . $file_new->getFilename());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,7 +470,7 @@ function image_field_config_update(FieldConfigInterface $field) {
|
||||
if ($file_new && (file_uri_scheme($file_new->getFileUri()) != $field_storage->getSetting('uri_scheme'))) {
|
||||
$directory = $field_storage->getSetting('uri_scheme') . '://default_images/';
|
||||
file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
|
||||
file_move($file_new, $directory . $file_new->filename);
|
||||
file_move($file_new, $directory . $file_new->getFilename());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
/**
|
||||
* @file
|
||||
* Drag+drop based in-place editor for images.
|
||||
*/
|
||||
|
||||
(function ($, _, Drupal) {
|
||||
Drupal.quickedit.editors.image = Drupal.quickedit.EditorView.extend(/** @lends Drupal.quickedit.editors.image# */{
|
||||
|
||||
/**
|
||||
* @constructs
|
||||
*
|
||||
* @augments Drupal.quickedit.EditorView
|
||||
*
|
||||
* @param {object} options
|
||||
* Options for the image editor.
|
||||
*/
|
||||
initialize(options) {
|
||||
Drupal.quickedit.EditorView.prototype.initialize.call(this, options);
|
||||
// Set our original value to our current HTML (for reverting).
|
||||
this.model.set('originalValue', this.$el.html().trim());
|
||||
// $.val() callback function for copying input from our custom form to
|
||||
// the Quick Edit Field Form.
|
||||
this.model.set('currentValue', function (index, value) {
|
||||
const matches = $(this).attr('name').match(/(alt|title)]$/);
|
||||
if (matches) {
|
||||
const name = matches[1];
|
||||
const $toolgroup = $(`#${options.fieldModel.toolbarView.getMainWysiwygToolgroupId()}`);
|
||||
const $input = $toolgroup.find(`.quickedit-image-field-info input[name="${name}"]`);
|
||||
if ($input.length) {
|
||||
return $input.val();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {Drupal.quickedit.FieldModel} fieldModel
|
||||
* The field model that holds the state.
|
||||
* @param {string} state
|
||||
* The state to change to.
|
||||
* @param {object} options
|
||||
* State options, if needed by the state change.
|
||||
*/
|
||||
stateChange(fieldModel, state, options) {
|
||||
const from = fieldModel.previous('state');
|
||||
switch (state) {
|
||||
case 'inactive':
|
||||
break;
|
||||
|
||||
case 'candidate':
|
||||
if (from !== 'inactive') {
|
||||
this.$el.find('.quickedit-image-dropzone').remove();
|
||||
this.$el.removeClass('quickedit-image-element');
|
||||
}
|
||||
if (from === 'invalid') {
|
||||
this.removeValidationErrors();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'highlighted':
|
||||
break;
|
||||
|
||||
case 'activating':
|
||||
// Defer updating the field model until the current state change has
|
||||
// propagated, to not trigger a nested state change event.
|
||||
_.defer(() => {
|
||||
fieldModel.set('state', 'active');
|
||||
});
|
||||
break;
|
||||
|
||||
case 'active':
|
||||
var self = this;
|
||||
|
||||
// Indicate that this element is being edited by Quick Edit Image.
|
||||
this.$el.addClass('quickedit-image-element');
|
||||
|
||||
// Render our initial dropzone element. Once the user reverts changes
|
||||
// or saves a new image, this element is removed.
|
||||
var $dropzone = this.renderDropzone('upload', Drupal.t('Drop file here or click to upload'));
|
||||
|
||||
$dropzone.on('dragenter', function (e) {
|
||||
$(this).addClass('hover');
|
||||
});
|
||||
$dropzone.on('dragleave', function (e) {
|
||||
$(this).removeClass('hover');
|
||||
});
|
||||
|
||||
$dropzone.on('drop', function (e) {
|
||||
// Only respond when a file is dropped (could be another element).
|
||||
if (e.originalEvent.dataTransfer && e.originalEvent.dataTransfer.files.length) {
|
||||
$(this).removeClass('hover');
|
||||
self.uploadImage(e.originalEvent.dataTransfer.files[0]);
|
||||
}
|
||||
});
|
||||
|
||||
$dropzone.on('click', (e) => {
|
||||
// Create an <input> element without appending it to the DOM, and
|
||||
// trigger a click event. This is the easiest way to arbitrarily
|
||||
// open the browser's upload dialog.
|
||||
$('<input type="file">')
|
||||
.trigger('click')
|
||||
.on('change', function () {
|
||||
if (this.files.length) {
|
||||
self.uploadImage(this.files[0]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Prevent the browser's default behavior when dragging files onto
|
||||
// the document (usually opens them in the same tab).
|
||||
$dropzone.on('dragover dragenter dragleave drop click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
this.renderToolbar(fieldModel);
|
||||
break;
|
||||
|
||||
case 'changed':
|
||||
break;
|
||||
|
||||
case 'saving':
|
||||
if (from === 'invalid') {
|
||||
this.removeValidationErrors();
|
||||
}
|
||||
|
||||
this.save(options);
|
||||
break;
|
||||
|
||||
case 'saved':
|
||||
break;
|
||||
|
||||
case 'invalid':
|
||||
this.showValidationErrors();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Validates/uploads a given file.
|
||||
*
|
||||
* @param {File} file
|
||||
* The file to upload.
|
||||
*/
|
||||
uploadImage(file) {
|
||||
// Indicate loading by adding a special class to our icon.
|
||||
this.renderDropzone('upload loading', Drupal.t('Uploading <i>@file</i>…', { '@file': file.name }));
|
||||
|
||||
// Build a valid URL for our endpoint.
|
||||
const fieldID = this.fieldModel.get('fieldID');
|
||||
const url = Drupal.quickedit.util.buildUrl(fieldID, Drupal.url('quickedit/image/upload/!entity_type/!id/!field_name/!langcode/!view_mode'));
|
||||
|
||||
// Construct form data that our endpoint can consume.
|
||||
const data = new FormData();
|
||||
data.append('files[image]', file);
|
||||
|
||||
// Construct a POST request to our endpoint.
|
||||
const self = this;
|
||||
this.ajax({
|
||||
type: 'POST',
|
||||
url,
|
||||
data,
|
||||
success(response) {
|
||||
const $el = $(self.fieldModel.get('el'));
|
||||
// Indicate that the field has changed - this enables the
|
||||
// "Save" button.
|
||||
self.fieldModel.set('state', 'changed');
|
||||
self.fieldModel.get('entity').set('inTempStore', true);
|
||||
self.removeValidationErrors();
|
||||
|
||||
// Replace our html with the new image. If we replaced our entire
|
||||
// element with data.html, we would have to implement complicated logic
|
||||
// like what's in Drupal.quickedit.AppView.renderUpdatedField.
|
||||
const $content = $(response.html).closest('[data-quickedit-field-id]').children();
|
||||
$el.empty().append($content);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Utility function to make an AJAX request to the server.
|
||||
*
|
||||
* In addition to formatting the correct request, this also handles error
|
||||
* codes and messages by displaying them visually inline with the image.
|
||||
*
|
||||
* Drupal.ajax is not called here as the Form API is unused by this
|
||||
* in-place editor, and our JSON requests/responses try to be
|
||||
* editor-agnostic. Ideally similar logic and routes could be used by
|
||||
* modules like CKEditor for drag+drop file uploads as well.
|
||||
*
|
||||
* @param {object} options
|
||||
* Ajax options.
|
||||
* @param {string} options.type
|
||||
* The type of request (i.e. GET, POST, PUT, DELETE, etc.)
|
||||
* @param {string} options.url
|
||||
* The URL for the request.
|
||||
* @param {*} options.data
|
||||
* The data to send to the server.
|
||||
* @param {function} options.success
|
||||
* A callback function used when a request is successful, without errors.
|
||||
*/
|
||||
ajax(options) {
|
||||
const defaultOptions = {
|
||||
context: this,
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
error() {
|
||||
this.renderDropzone('error', Drupal.t('A server error has occurred.'));
|
||||
},
|
||||
};
|
||||
|
||||
const ajaxOptions = $.extend(defaultOptions, options);
|
||||
const successCallback = ajaxOptions.success;
|
||||
|
||||
// Handle the success callback.
|
||||
ajaxOptions.success = function (response) {
|
||||
if (response.main_error) {
|
||||
this.renderDropzone('error', response.main_error);
|
||||
if (response.errors.length) {
|
||||
this.model.set('validationErrors', response.errors);
|
||||
}
|
||||
this.showValidationErrors();
|
||||
}
|
||||
else {
|
||||
successCallback(response);
|
||||
}
|
||||
};
|
||||
|
||||
$.ajax(ajaxOptions);
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders our toolbar form for editing metadata.
|
||||
*
|
||||
* @param {Drupal.quickedit.FieldModel} fieldModel
|
||||
* The current Field Model.
|
||||
*/
|
||||
renderToolbar(fieldModel) {
|
||||
const $toolgroup = $(`#${fieldModel.toolbarView.getMainWysiwygToolgroupId()}`);
|
||||
let $toolbar = $toolgroup.find('.quickedit-image-field-info');
|
||||
if ($toolbar.length === 0) {
|
||||
// Perform an AJAX request for extra image info (alt/title).
|
||||
const fieldID = fieldModel.get('fieldID');
|
||||
const url = Drupal.quickedit.util.buildUrl(fieldID, Drupal.url('quickedit/image/info/!entity_type/!id/!field_name/!langcode/!view_mode'));
|
||||
const self = this;
|
||||
self.ajax({
|
||||
type: 'GET',
|
||||
url,
|
||||
success(response) {
|
||||
$toolbar = $(Drupal.theme.quickeditImageToolbar(response));
|
||||
$toolgroup.append($toolbar);
|
||||
$toolbar.on('keyup paste', () => {
|
||||
fieldModel.set('state', 'changed');
|
||||
});
|
||||
// Re-position the toolbar, which could have changed size.
|
||||
fieldModel.get('entity').toolbarView.position();
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders our dropzone element.
|
||||
*
|
||||
* @param {string} state
|
||||
* The current state of our editor. Only used for visual styling.
|
||||
* @param {string} text
|
||||
* The text to display in the dropzone area.
|
||||
*
|
||||
* @return {jQuery}
|
||||
* The rendered dropzone.
|
||||
*/
|
||||
renderDropzone(state, text) {
|
||||
let $dropzone = this.$el.find('.quickedit-image-dropzone');
|
||||
// If the element already exists, modify its contents.
|
||||
if ($dropzone.length) {
|
||||
$dropzone
|
||||
.removeClass('upload error hover loading')
|
||||
.addClass(`.quickedit-image-dropzone ${state}`)
|
||||
.children('.quickedit-image-text')
|
||||
.html(text);
|
||||
}
|
||||
else {
|
||||
$dropzone = $(Drupal.theme('quickeditImageDropzone', {
|
||||
state,
|
||||
text,
|
||||
}));
|
||||
this.$el.append($dropzone);
|
||||
}
|
||||
|
||||
return $dropzone;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
revert() {
|
||||
this.$el.html(this.model.get('originalValue'));
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getQuickEditUISettings() {
|
||||
return { padding: false, unifiedToolbar: true, fullWidthToolbar: true, popup: false };
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
showValidationErrors() {
|
||||
const errors = Drupal.theme('quickeditImageErrors', {
|
||||
errors: this.model.get('validationErrors'),
|
||||
});
|
||||
$(`#${this.fieldModel.toolbarView.getMainWysiwygToolgroupId()}`)
|
||||
.append(errors);
|
||||
this.getEditedElement()
|
||||
.addClass('quickedit-validation-error');
|
||||
// Re-position the toolbar, which could have changed size.
|
||||
this.fieldModel.get('entity').toolbarView.position();
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
removeValidationErrors() {
|
||||
$(`#${this.fieldModel.toolbarView.getMainWysiwygToolgroupId()}`)
|
||||
.find('.quickedit-image-errors').remove();
|
||||
this.getEditedElement()
|
||||
.removeClass('quickedit-validation-error');
|
||||
},
|
||||
|
||||
});
|
||||
}(jQuery, _, Drupal));
|
||||
@@ -1,28 +1,17 @@
|
||||
/**
|
||||
* @file
|
||||
* Drag+drop based in-place editor for images.
|
||||
*/
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function ($, _, Drupal) {
|
||||
|
||||
'use strict';
|
||||
|
||||
Drupal.quickedit.editors.image = Drupal.quickedit.EditorView.extend(/** @lends Drupal.quickedit.editors.image# */{
|
||||
|
||||
/**
|
||||
* @constructs
|
||||
*
|
||||
* @augments Drupal.quickedit.EditorView
|
||||
*
|
||||
* @param {object} options
|
||||
* Options for the image editor.
|
||||
*/
|
||||
initialize: function (options) {
|
||||
Drupal.quickedit.editors.image = Drupal.quickedit.EditorView.extend({
|
||||
initialize: function initialize(options) {
|
||||
Drupal.quickedit.EditorView.prototype.initialize.call(this, options);
|
||||
// Set our original value to our current HTML (for reverting).
|
||||
|
||||
this.model.set('originalValue', this.$el.html().trim());
|
||||
// $.val() callback function for copying input from our custom form to
|
||||
// the Quick Edit Field Form.
|
||||
|
||||
this.model.set('currentValue', function (index, value) {
|
||||
var matches = $(this).attr('name').match(/(alt|title)]$/);
|
||||
if (matches) {
|
||||
@@ -35,18 +24,7 @@
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {Drupal.quickedit.FieldModel} fieldModel
|
||||
* The field model that holds the state.
|
||||
* @param {string} state
|
||||
* The state to change to.
|
||||
* @param {object} options
|
||||
* State options, if needed by the state change.
|
||||
*/
|
||||
stateChange: function (fieldModel, state, options) {
|
||||
stateChange: function stateChange(fieldModel, state, options) {
|
||||
var from = fieldModel.previous('state');
|
||||
switch (state) {
|
||||
case 'inactive':
|
||||
@@ -66,8 +44,6 @@
|
||||
break;
|
||||
|
||||
case 'activating':
|
||||
// Defer updating the field model until the current state change has
|
||||
// propagated, to not trigger a nested state change event.
|
||||
_.defer(function () {
|
||||
fieldModel.set('state', 'active');
|
||||
});
|
||||
@@ -76,11 +52,8 @@
|
||||
case 'active':
|
||||
var self = this;
|
||||
|
||||
// Indicate that this element is being edited by Quick Edit Image.
|
||||
this.$el.addClass('quickedit-image-element');
|
||||
|
||||
// Render our initial dropzone element. Once the user reverts changes
|
||||
// or saves a new image, this element is removed.
|
||||
var $dropzone = this.renderDropzone('upload', Drupal.t('Drop file here or click to upload'));
|
||||
|
||||
$dropzone.on('dragenter', function (e) {
|
||||
@@ -91,7 +64,6 @@
|
||||
});
|
||||
|
||||
$dropzone.on('drop', function (e) {
|
||||
// Only respond when a file is dropped (could be another element).
|
||||
if (e.originalEvent.dataTransfer && e.originalEvent.dataTransfer.files.length) {
|
||||
$(this).removeClass('hover');
|
||||
self.uploadImage(e.originalEvent.dataTransfer.files[0]);
|
||||
@@ -99,20 +71,13 @@
|
||||
});
|
||||
|
||||
$dropzone.on('click', function (e) {
|
||||
// Create an <input> element without appending it to the DOM, and
|
||||
// trigger a click event. This is the easiest way to arbitrarily
|
||||
// open the browser's upload dialog.
|
||||
$('<input type="file">')
|
||||
.trigger('click')
|
||||
.on('change', function () {
|
||||
if (this.files.length) {
|
||||
self.uploadImage(this.files[0]);
|
||||
}
|
||||
});
|
||||
$('<input type="file">').trigger('click').on('change', function () {
|
||||
if (this.files.length) {
|
||||
self.uploadImage(this.files[0]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Prevent the browser's default behavior when dragging files onto
|
||||
// the document (usually opens them in the same tab).
|
||||
$dropzone.on('dragover dragenter dragleave drop click', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
@@ -140,78 +105,40 @@
|
||||
break;
|
||||
}
|
||||
},
|
||||
uploadImage: function uploadImage(file) {
|
||||
this.renderDropzone('upload loading', Drupal.t('Uploading <i>@file</i>…', { '@file': file.name }));
|
||||
|
||||
/**
|
||||
* Validates/uploads a given file.
|
||||
*
|
||||
* @param {File} file
|
||||
* The file to upload.
|
||||
*/
|
||||
uploadImage: function (file) {
|
||||
// Indicate loading by adding a special class to our icon.
|
||||
this.renderDropzone('upload loading', Drupal.t('Uploading <i>@file</i>…', {'@file': file.name}));
|
||||
|
||||
// Build a valid URL for our endpoint.
|
||||
var fieldID = this.fieldModel.get('fieldID');
|
||||
var url = Drupal.quickedit.util.buildUrl(fieldID, Drupal.url('quickedit/image/upload/!entity_type/!id/!field_name/!langcode/!view_mode'));
|
||||
|
||||
// Construct form data that our endpoint can consume.
|
||||
var data = new FormData();
|
||||
data.append('files[image]', file);
|
||||
|
||||
// Construct a POST request to our endpoint.
|
||||
var self = this;
|
||||
this.ajax({
|
||||
type: 'POST',
|
||||
url: url,
|
||||
data: data,
|
||||
success: function (response) {
|
||||
success: function success(response) {
|
||||
var $el = $(self.fieldModel.get('el'));
|
||||
// Indicate that the field has changed - this enables the
|
||||
// "Save" button.
|
||||
|
||||
self.fieldModel.set('state', 'changed');
|
||||
self.fieldModel.get('entity').set('inTempStore', true);
|
||||
self.removeValidationErrors();
|
||||
|
||||
// Replace our html with the new image. If we replaced our entire
|
||||
// element with data.html, we would have to implement complicated logic
|
||||
// like what's in Drupal.quickedit.AppView.renderUpdatedField.
|
||||
var $content = $(response.html).closest('[data-quickedit-field-id]').children();
|
||||
$el.empty().append($content);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Utility function to make an AJAX request to the server.
|
||||
*
|
||||
* In addition to formatting the correct request, this also handles error
|
||||
* codes and messages by displaying them visually inline with the image.
|
||||
*
|
||||
* Drupal.ajax is not called here as the Form API is unused by this
|
||||
* in-place editor, and our JSON requests/responses try to be
|
||||
* editor-agnostic. Ideally similar logic and routes could be used by
|
||||
* modules like CKEditor for drag+drop file uploads as well.
|
||||
*
|
||||
* @param {object} options
|
||||
* Ajax options.
|
||||
* @param {string} options.type
|
||||
* The type of request (i.e. GET, POST, PUT, DELETE, etc.)
|
||||
* @param {string} options.url
|
||||
* The URL for the request.
|
||||
* @param {*} options.data
|
||||
* The data to send to the server.
|
||||
* @param {function} options.success
|
||||
* A callback function used when a request is successful, without errors.
|
||||
*/
|
||||
ajax: function (options) {
|
||||
ajax: function ajax(options) {
|
||||
var defaultOptions = {
|
||||
context: this,
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
error: function () {
|
||||
error: function error() {
|
||||
this.renderDropzone('error', Drupal.t('A server error has occurred.'));
|
||||
}
|
||||
};
|
||||
@@ -219,7 +146,6 @@
|
||||
var ajaxOptions = $.extend(defaultOptions, options);
|
||||
var successCallback = ajaxOptions.success;
|
||||
|
||||
// Handle the success callback.
|
||||
ajaxOptions.success = function (response) {
|
||||
if (response.main_error) {
|
||||
this.renderDropzone('error', response.main_error);
|
||||
@@ -227,67 +153,41 @@
|
||||
this.model.set('validationErrors', response.errors);
|
||||
}
|
||||
this.showValidationErrors();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
successCallback(response);
|
||||
}
|
||||
};
|
||||
|
||||
$.ajax(ajaxOptions);
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders our toolbar form for editing metadata.
|
||||
*
|
||||
* @param {Drupal.quickedit.FieldModel} fieldModel
|
||||
* The current Field Model.
|
||||
*/
|
||||
renderToolbar: function (fieldModel) {
|
||||
renderToolbar: function renderToolbar(fieldModel) {
|
||||
var $toolgroup = $('#' + fieldModel.toolbarView.getMainWysiwygToolgroupId());
|
||||
var $toolbar = $toolgroup.find('.quickedit-image-field-info');
|
||||
if ($toolbar.length === 0) {
|
||||
// Perform an AJAX request for extra image info (alt/title).
|
||||
var fieldID = fieldModel.get('fieldID');
|
||||
var url = Drupal.quickedit.util.buildUrl(fieldID, Drupal.url('quickedit/image/info/!entity_type/!id/!field_name/!langcode/!view_mode'));
|
||||
var self = this;
|
||||
self.ajax({
|
||||
type: 'GET',
|
||||
url: url,
|
||||
success: function (response) {
|
||||
success: function success(response) {
|
||||
$toolbar = $(Drupal.theme.quickeditImageToolbar(response));
|
||||
$toolgroup.append($toolbar);
|
||||
$toolbar.on('keyup paste', function () {
|
||||
fieldModel.set('state', 'changed');
|
||||
});
|
||||
// Re-position the toolbar, which could have changed size.
|
||||
|
||||
fieldModel.get('entity').toolbarView.position();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders our dropzone element.
|
||||
*
|
||||
* @param {string} state
|
||||
* The current state of our editor. Only used for visual styling.
|
||||
* @param {string} text
|
||||
* The text to display in the dropzone area.
|
||||
*
|
||||
* @return {jQuery}
|
||||
* The rendered dropzone.
|
||||
*/
|
||||
renderDropzone: function (state, text) {
|
||||
renderDropzone: function renderDropzone(state, text) {
|
||||
var $dropzone = this.$el.find('.quickedit-image-dropzone');
|
||||
// If the element already exists, modify its contents.
|
||||
|
||||
if ($dropzone.length) {
|
||||
$dropzone
|
||||
.removeClass('upload error hover loading')
|
||||
.addClass('.quickedit-image-dropzone ' + state)
|
||||
.children('.quickedit-image-text')
|
||||
.html(text);
|
||||
}
|
||||
else {
|
||||
$dropzone.removeClass('upload error hover loading').addClass('.quickedit-image-dropzone ' + state).children('.quickedit-image-text').html(text);
|
||||
} else {
|
||||
$dropzone = $(Drupal.theme('quickeditImageDropzone', {
|
||||
state: state,
|
||||
text: text
|
||||
@@ -297,46 +197,24 @@
|
||||
|
||||
return $dropzone;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
revert: function () {
|
||||
revert: function revert() {
|
||||
this.$el.html(this.model.get('originalValue'));
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getQuickEditUISettings: function () {
|
||||
return {padding: false, unifiedToolbar: true, fullWidthToolbar: true, popup: false};
|
||||
getQuickEditUISettings: function getQuickEditUISettings() {
|
||||
return { padding: false, unifiedToolbar: true, fullWidthToolbar: true, popup: false };
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
showValidationErrors: function () {
|
||||
showValidationErrors: function showValidationErrors() {
|
||||
var errors = Drupal.theme('quickeditImageErrors', {
|
||||
errors: this.model.get('validationErrors')
|
||||
});
|
||||
$('#' + this.fieldModel.toolbarView.getMainWysiwygToolgroupId())
|
||||
.append(errors);
|
||||
this.getEditedElement()
|
||||
.addClass('quickedit-validation-error');
|
||||
// Re-position the toolbar, which could have changed size.
|
||||
$('#' + this.fieldModel.toolbarView.getMainWysiwygToolgroupId()).append(errors);
|
||||
this.getEditedElement().addClass('quickedit-validation-error');
|
||||
|
||||
this.fieldModel.get('entity').toolbarView.position();
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
removeValidationErrors: function () {
|
||||
$('#' + this.fieldModel.toolbarView.getMainWysiwygToolgroupId())
|
||||
.find('.quickedit-image-errors').remove();
|
||||
this.getEditedElement()
|
||||
.removeClass('quickedit-validation-error');
|
||||
removeValidationErrors: function removeValidationErrors() {
|
||||
$('#' + this.fieldModel.toolbarView.getMainWysiwygToolgroupId()).find('.quickedit-image-errors').remove();
|
||||
this.getEditedElement().removeClass('quickedit-validation-error');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})(jQuery, _, Drupal);
|
||||
})(jQuery, _, Drupal);
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @file
|
||||
* Provides theme functions for image Quick Edit's client-side HTML.
|
||||
*/
|
||||
|
||||
(function (Drupal) {
|
||||
/**
|
||||
* Theme function for validation errors of the Image in-place editor.
|
||||
*
|
||||
* @param {object} settings
|
||||
* Settings object used to construct the markup.
|
||||
* @param {string} settings.errors
|
||||
* Already escaped HTML representing error messages.
|
||||
*
|
||||
* @return {string}
|
||||
* The corresponding HTML.
|
||||
*/
|
||||
Drupal.theme.quickeditImageErrors = function (settings) {
|
||||
return `<div class="quickedit-image-errors">${settings.errors}</div>`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme function for the dropzone element of the Image module's in-place
|
||||
* editor.
|
||||
*
|
||||
* @param {object} settings
|
||||
* Settings object used to construct the markup.
|
||||
* @param {string} settings.state
|
||||
* State of the upload.
|
||||
* @param {string} settings.text
|
||||
* Text to display inline with the dropzone element.
|
||||
*
|
||||
* @return {string}
|
||||
* The corresponding HTML.
|
||||
*/
|
||||
Drupal.theme.quickeditImageDropzone = function (settings) {
|
||||
return `<div class="quickedit-image-dropzone ${settings.state}">` +
|
||||
' <i class="quickedit-image-icon"></i>' +
|
||||
` <span class="quickedit-image-text">${settings.text}</span>` +
|
||||
'</div>';
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme function for the toolbar of the Image module's in-place editor.
|
||||
*
|
||||
* @param {object} settings
|
||||
* Settings object used to construct the markup.
|
||||
* @param {bool} settings.alt_field
|
||||
* Whether or not the "Alt" field is enabled for this field.
|
||||
* @param {bool} settings.alt_field_required
|
||||
* Whether or not the "Alt" field is required for this field.
|
||||
* @param {string} settings.alt
|
||||
* The current value for the "Alt" field.
|
||||
* @param {bool} settings.title_field
|
||||
* Whether or not the "Title" field is enabled for this field.
|
||||
* @param {bool} settings.title_field_required
|
||||
* Whether or not the "Title" field is required for this field.
|
||||
* @param {string} settings.title
|
||||
* The current value for the "Title" field.
|
||||
*
|
||||
* @return {string}
|
||||
* The corresponding HTML.
|
||||
*/
|
||||
Drupal.theme.quickeditImageToolbar = function (settings) {
|
||||
let html = '<form class="quickedit-image-field-info">';
|
||||
if (settings.alt_field) {
|
||||
html += `${' <div>' +
|
||||
' <label for="alt" class="'}${settings.alt_field_required ? 'required' : ''}">${Drupal.t('Alternative text')}</label>` +
|
||||
` <input type="text" placeholder="${settings.alt}" value="${settings.alt}" name="alt" ${settings.alt_field_required ? 'required' : ''}/>` +
|
||||
' </div>';
|
||||
}
|
||||
if (settings.title_field) {
|
||||
html += `${' <div>' +
|
||||
' <label for="title" class="'}${settings.title_field_required ? 'form-required' : ''}">${Drupal.t('Title')}</label>` +
|
||||
` <input type="text" placeholder="${settings.title}" value="${settings.title}" name="title" ${settings.title_field_required ? 'required' : ''}/>` +
|
||||
' </div>';
|
||||
}
|
||||
html += '</form>';
|
||||
|
||||
return html;
|
||||
};
|
||||
}(Drupal));
|
||||
@@ -1,86 +1,29 @@
|
||||
/**
|
||||
* @file
|
||||
* Provides theme functions for image Quick Edit's client-side HTML.
|
||||
*/
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function (Drupal) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Theme function for validation errors of the Image in-place editor.
|
||||
*
|
||||
* @param {object} settings
|
||||
* Settings object used to construct the markup.
|
||||
* @param {string} settings.errors
|
||||
* Already escaped HTML representing error messages.
|
||||
*
|
||||
* @return {string}
|
||||
* The corresponding HTML.
|
||||
*/
|
||||
Drupal.theme.quickeditImageErrors = function (settings) {
|
||||
return '<div class="quickedit-image-errors">' + settings.errors + '</div>';
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme function for the dropzone element of the Image module's in-place
|
||||
* editor.
|
||||
*
|
||||
* @param {object} settings
|
||||
* Settings object used to construct the markup.
|
||||
* @param {string} settings.state
|
||||
* State of the upload.
|
||||
* @param {string} settings.text
|
||||
* Text to display inline with the dropzone element.
|
||||
*
|
||||
* @return {string}
|
||||
* The corresponding HTML.
|
||||
*/
|
||||
Drupal.theme.quickeditImageDropzone = function (settings) {
|
||||
return '<div class="quickedit-image-dropzone ' + settings.state + '">' +
|
||||
' <i class="quickedit-image-icon"></i>' +
|
||||
' <span class="quickedit-image-text">' + settings.text + '</span>' +
|
||||
'</div>';
|
||||
return '<div class="quickedit-image-dropzone ' + settings.state + '">' + ' <i class="quickedit-image-icon"></i>' + (' <span class="quickedit-image-text">' + settings.text + '</span>') + '</div>';
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme function for the toolbar of the Image module's in-place editor.
|
||||
*
|
||||
* @param {object} settings
|
||||
* Settings object used to construct the markup.
|
||||
* @param {bool} settings.alt_field
|
||||
* Whether or not the "Alt" field is enabled for this field.
|
||||
* @param {bool} settings.alt_field_required
|
||||
* Whether or not the "Alt" field is required for this field.
|
||||
* @param {string} settings.alt
|
||||
* The current value for the "Alt" field.
|
||||
* @param {bool} settings.title_field
|
||||
* Whether or not the "Title" field is enabled for this field.
|
||||
* @param {bool} settings.title_field_required
|
||||
* Whether or not the "Title" field is required for this field.
|
||||
* @param {string} settings.title
|
||||
* The current value for the "Title" field.
|
||||
*
|
||||
* @return {string}
|
||||
* The corresponding HTML.
|
||||
*/
|
||||
Drupal.theme.quickeditImageToolbar = function (settings) {
|
||||
var html = '<form class="quickedit-image-field-info">';
|
||||
if (settings.alt_field) {
|
||||
html += ' <div>' +
|
||||
' <label for="alt" class="' + (settings.alt_field_required ? 'required' : '') + '">' + Drupal.t('Alternative text') + '</label>' +
|
||||
' <input type="text" placeholder="' + settings.alt + '" value="' + settings.alt + '" name="alt" ' + (settings.alt_field_required ? 'required' : '') + '/>' +
|
||||
' </div>';
|
||||
html += '' + (' <div>' + ' <label for="alt" class="') + (settings.alt_field_required ? 'required' : '') + '">' + Drupal.t('Alternative text') + '</label>' + (' <input type="text" placeholder="' + settings.alt + '" value="' + settings.alt + '" name="alt" ' + (settings.alt_field_required ? 'required' : '') + '/>') + ' </div>';
|
||||
}
|
||||
if (settings.title_field) {
|
||||
html += ' <div>' +
|
||||
' <label for="title" class="' + (settings.title_field_required ? 'form-required' : '') + '">' + Drupal.t('Title') + '</label>' +
|
||||
' <input type="text" placeholder="' + settings.title + '" value="' + settings.title + '" name="title" ' + (settings.title_field_required ? 'required' : '') + '/>' +
|
||||
' </div>';
|
||||
html += '' + (' <div>' + ' <label for="title" class="') + (settings.title_field_required ? 'form-required' : '') + '">' + Drupal.t('Title') + '</label>' + (' <input type="text" placeholder="' + settings.title + '" value="' + settings.title + '" name="title" ' + (settings.title_field_required ? 'required' : '') + '/>') + ' </div>';
|
||||
}
|
||||
html += '</form>';
|
||||
|
||||
return html;
|
||||
};
|
||||
|
||||
})(Drupal);
|
||||
})(Drupal);
|
||||
@@ -8,6 +8,7 @@ source:
|
||||
- allow_insecure_derivatives
|
||||
- suppress_itok_output
|
||||
- image_style_preview_image
|
||||
source_module: image
|
||||
process:
|
||||
suppress_itok_output: suppress_itok_output
|
||||
allow_insecure_derivatives: allow_insecure_derivatives
|
||||
|
||||
@@ -8,7 +8,7 @@ process:
|
||||
name: name
|
||||
label: label
|
||||
effects:
|
||||
plugin: iterator
|
||||
plugin: sub_process
|
||||
source: effects
|
||||
process:
|
||||
id: name
|
||||
|
||||
@@ -14,9 +14,11 @@ use Drupal\image\ImageEffectInterface;
|
||||
use Drupal\image\ImageStyleInterface;
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
|
||||
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
|
||||
/**
|
||||
* Defines an image style configuration entity.
|
||||
*
|
||||
@@ -274,9 +276,8 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, Entity
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createDerivative($original_uri, $derivative_uri) {
|
||||
|
||||
// If the source file doesn't exist, return FALSE without creating folders.
|
||||
$image = \Drupal::service('image.factory')->get($original_uri);
|
||||
$image = $this->getImageFactory()->get($original_uri);
|
||||
if (!$image->isValid()) {
|
||||
return FALSE;
|
||||
}
|
||||
@@ -340,6 +341,18 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, Entity
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsUri($uri) {
|
||||
// Only support the URI if its extension is supported by the current image
|
||||
// toolkit.
|
||||
return in_array(
|
||||
Unicode::strtolower(pathinfo($uri, PATHINFO_EXTENSION)),
|
||||
$this->getImageFactory()->getSupportedExtensions()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -408,6 +421,16 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, Entity
|
||||
return \Drupal::service('plugin.manager.image.effect');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the image factory.
|
||||
*
|
||||
* @return \Drupal\Core\Image\ImageFactory
|
||||
* The image factory.
|
||||
*/
|
||||
protected function getImageFactory() {
|
||||
return \Drupal::service('image.factory');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Drupal private key.
|
||||
*
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\image\Form;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
@@ -58,7 +59,7 @@ class ImageStyleEditForm extends ImageStyleFormBase {
|
||||
$form['preview'] = [
|
||||
'#type' => 'item',
|
||||
'#title' => $this->t('Preview'),
|
||||
'#markup' => drupal_render($preview_arguments),
|
||||
'#markup' => \Drupal::service('renderer')->render($preview_arguments),
|
||||
// Render preview above parent elements.
|
||||
'#weight' => -5,
|
||||
];
|
||||
@@ -143,7 +144,7 @@ class ImageStyleEditForm extends ImageStyleFormBase {
|
||||
$new_effect_options = [];
|
||||
$effects = $this->imageEffectManager->getDefinitions();
|
||||
uasort($effects, function ($a, $b) {
|
||||
return strcasecmp($a['id'], $b['id']);
|
||||
return Unicode::strcasecmp($a['label'], $b['label']);
|
||||
});
|
||||
foreach ($effects as $effect => $definition) {
|
||||
$new_effect_options[$effect] = $definition['label'];
|
||||
|
||||
@@ -194,4 +194,15 @@ interface ImageStyleInterface extends ConfigEntityInterface {
|
||||
*/
|
||||
public function deleteImageEffect(ImageEffectInterface $effect);
|
||||
|
||||
/**
|
||||
* Determines if this style can be applied to a given image.
|
||||
*
|
||||
* @param string $uri
|
||||
* The URI of the image.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the image is supported, FALSE otherwise.
|
||||
*/
|
||||
public function supportsUri($uri);
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Drupal\image\Plugin\Field\FieldType;
|
||||
|
||||
use Drupal\Component\Utility\Random;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
@@ -313,13 +314,18 @@ class ImageItem extends FileItem {
|
||||
$height = $this->height;
|
||||
|
||||
// Determine the dimensions if necessary.
|
||||
if (empty($width) || empty($height)) {
|
||||
$image = \Drupal::service('image.factory')->get($this->entity->getFileUri());
|
||||
if ($image->isValid()) {
|
||||
$this->width = $image->getWidth();
|
||||
$this->height = $image->getHeight();
|
||||
if ($this->entity && $this->entity instanceof EntityInterface) {
|
||||
if (empty($width) || empty($height)) {
|
||||
$image = \Drupal::service('image.factory')->get($this->entity->getFileUri());
|
||||
if ($image->isValid()) {
|
||||
$this->width = $image->getWidth();
|
||||
$this->height = $image->getHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
trigger_error(sprintf("Missing file with ID %s.", $this->target_id), E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,7 @@ use Drupal\migrate\Row;
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d6_imagecache_presets",
|
||||
* source_provider = "imagecache"
|
||||
* source_module = "imagecache"
|
||||
* )
|
||||
*/
|
||||
class ImageCachePreset extends DrupalSqlBase {
|
||||
|
||||
@@ -10,7 +10,7 @@ use Drupal\migrate\Row;
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d7_image_styles",
|
||||
* source_provider = "image"
|
||||
* source_module = "image"
|
||||
* )
|
||||
*/
|
||||
class ImageStyles extends DrupalSqlBase {
|
||||
|
||||
@@ -203,6 +203,10 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
|
||||
$this->assertTitle(t('Edit style @name | Drupal', ['@name' => $style_label]));
|
||||
$this->assertResponse(200, format_string('Image style %original renamed to %new', ['%original' => $style->id(), '%new' => $style_name]));
|
||||
|
||||
// Check that the available image effects are properly sorted.
|
||||
$option = $this->xpath('//select[@id=:id]//option', [':id' => 'edit-new--2']);
|
||||
$this->assertTrue($option[1] == 'Ajax test', '"Ajax test" is the first selectable effect.');
|
||||
|
||||
// Check that the image was flushed after updating the style.
|
||||
// This is especially important when renaming the style. Make sure that
|
||||
// the old image directory has been deleted.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
@@ -8,7 +9,7 @@ use Drupal\file\Entity\File;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests setting up default images both to the field and field field.
|
||||
* Tests setting up default images both to the field and field storage.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
@@ -22,7 +23,7 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
||||
public static $modules = ['field_ui'];
|
||||
|
||||
/**
|
||||
* Tests CRUD for fields and fields fields with default images.
|
||||
* Tests CRUD for fields and field storages with default images.
|
||||
*/
|
||||
public function testDefaultImages() {
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
@@ -37,16 +38,17 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
||||
$file->save();
|
||||
}
|
||||
$default_images = [];
|
||||
foreach (['field', 'field', 'field2', 'field_new', 'field_new'] as $image_target) {
|
||||
foreach (['field_storage', 'field', 'field2', 'field_storage_new', 'field_new', 'field_storage_private', 'field_private'] as $image_target) {
|
||||
$file = File::create((array) array_pop($files));
|
||||
$file->save();
|
||||
$default_images[$image_target] = $file;
|
||||
}
|
||||
|
||||
// Create an image field and add an field to the article content type.
|
||||
// Create an image field storage and add a field to the article content
|
||||
// type.
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$storage_settings['default_image'] = [
|
||||
'uuid' => $default_images['field']->uuid(),
|
||||
'uuid' => $default_images['field_storage']->uuid(),
|
||||
'alt' => '',
|
||||
'title' => '',
|
||||
'width' => 0,
|
||||
@@ -72,11 +74,11 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
||||
|
||||
$field_storage = $field->getFieldStorageDefinition();
|
||||
|
||||
// The field default image id should be 1.
|
||||
$this->assertEqual($field_storage->getSetting('default_image')['uuid'], $default_images['field']->uuid());
|
||||
// The field storage default image id should be 1.
|
||||
$this->assertEqual($field_storage->getSetting('default_image')['uuid'], $default_images['field_storage']->uuid());
|
||||
|
||||
// Also test \Drupal\field\Entity\FieldStorageConfig::getSettings().
|
||||
$this->assertEqual($field_storage->getSettings()['default_image']['uuid'], $default_images['field']->uuid());
|
||||
$this->assertEqual($field_storage->getSettings()['default_image']['uuid'], $default_images['field_storage']->uuid());
|
||||
|
||||
// Add another field with another default image to the page content type.
|
||||
$field2 = FieldConfig::create([
|
||||
@@ -104,15 +106,16 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Confirm the defaults are present on the article field settings form.
|
||||
// Confirm the defaults are present on the article field storage settings
|
||||
// form.
|
||||
$field_id = $field->id();
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id/storage");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field']->id(),
|
||||
$default_images['field_storage']->id(),
|
||||
format_string(
|
||||
'Article image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field']->id()]
|
||||
'Article image field storage default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_storage']->id()]
|
||||
)
|
||||
);
|
||||
// Confirm the defaults are present on the article field edit form.
|
||||
@@ -121,19 +124,19 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field']->id(),
|
||||
format_string(
|
||||
'Article image field field default equals expected file ID of @fid.',
|
||||
'Article image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
// Confirm the defaults are present on the page field settings form.
|
||||
// Confirm the defaults are present on the page field storage settings form.
|
||||
$this->drupalGet("admin/structure/types/manage/page/fields/$field_id/storage");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field']->id(),
|
||||
$default_images['field_storage']->id(),
|
||||
format_string(
|
||||
'Page image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field']->id()]
|
||||
'Page image field storage default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_storage']->id()]
|
||||
)
|
||||
);
|
||||
// Confirm the defaults are present on the page field edit form.
|
||||
@@ -143,7 +146,7 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field2']->id(),
|
||||
format_string(
|
||||
'Page image field field default equals expected file ID of @fid.',
|
||||
'Page image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field2']->id()]
|
||||
)
|
||||
);
|
||||
@@ -181,22 +184,23 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
||||
|
||||
// Upload a new default for the field storage.
|
||||
$default_image_settings = $field_storage->getSetting('default_image');
|
||||
$default_image_settings['uuid'] = $default_images['field_new']->uuid();
|
||||
$default_image_settings['uuid'] = $default_images['field_storage_new']->uuid();
|
||||
$field_storage->setSetting('default_image', $default_image_settings);
|
||||
$field_storage->save();
|
||||
|
||||
// Confirm that the new default is used on the article field settings form.
|
||||
// Confirm that the new default is used on the article field storage
|
||||
// settings form.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id/storage");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field_new']->id(),
|
||||
$default_images['field_storage_new']->id(),
|
||||
format_string(
|
||||
'Updated image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_new']->id()]
|
||||
'Updated image field storage default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_storage_new']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
// Reload the nodes and confirm the field field defaults are used.
|
||||
// Reload the nodes and confirm the field defaults are used.
|
||||
$node_storage->resetCache([$article->id(), $page->id()]);
|
||||
$article_built = $this->drupalBuildEntityView($article = $node_storage->load($article->id()));
|
||||
$page_built = $this->drupalBuildEntityView($page = $node_storage->load($page->id()));
|
||||
@@ -217,20 +221,19 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
||||
)
|
||||
);
|
||||
|
||||
// Upload a new default for the article's field field.
|
||||
// Upload a new default for the article's field.
|
||||
$default_image_settings = $field->getSetting('default_image');
|
||||
$default_image_settings['uuid'] = $default_images['field_new']->uuid();
|
||||
$field->setSetting('default_image', $default_image_settings);
|
||||
$field->save();
|
||||
|
||||
// Confirm the new field field default is used on the article field
|
||||
// admin form.
|
||||
// Confirm the new field default is used on the article field admin form.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field_new']->id(),
|
||||
format_string(
|
||||
'Updated article image field field default equals expected file ID of @fid.',
|
||||
'Updated article image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_new']->id()]
|
||||
)
|
||||
);
|
||||
@@ -264,31 +267,31 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertRaw($file->getFilename());
|
||||
|
||||
// Remove the instance default from articles.
|
||||
// Remove the field default from articles.
|
||||
$default_image_settings = $field->getSetting('default_image');
|
||||
$default_image_settings['uuid'] = 0;
|
||||
$field->setSetting('default_image', $default_image_settings);
|
||||
$field->save();
|
||||
|
||||
// Confirm the article field field default has been removed.
|
||||
// Confirm the article field default has been removed.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
'',
|
||||
'Updated article image field field default has been successfully removed.'
|
||||
'Updated article image field default has been successfully removed.'
|
||||
);
|
||||
|
||||
// Reload the nodes.
|
||||
$node_storage->resetCache([$article->id(), $page->id()]);
|
||||
$article_built = $this->drupalBuildEntityView($article = $node_storage->load($article->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 storage (not field) default.
|
||||
$this->assertEqual(
|
||||
$article_built[$field_name][0]['#item']->target_id,
|
||||
$default_images['field_new']->id(),
|
||||
$default_images['field_storage_new']->id(),
|
||||
format_string(
|
||||
'An existing article node without an image has the expected default image file ID of @fid.',
|
||||
['@fid' => $default_images['field_new']->id()]
|
||||
['@fid' => $default_images['field_storage_new']->id()]
|
||||
)
|
||||
);
|
||||
// Confirm the page remains unchanged.
|
||||
@@ -307,13 +310,52 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
||||
$this->assertText('Only files with the following extensions are allowed: png gif jpg jpeg.');
|
||||
|
||||
// Confirm the default image is shown on the node form.
|
||||
$file = File::load($default_images['field_new']->id());
|
||||
$file = File::load($default_images['field_storage_new']->id());
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertRaw($file->getFilename());
|
||||
|
||||
// Change the default image for the field storage and also change the upload
|
||||
// destination to the private filesystem at the same time.
|
||||
$default_image_settings = $field_storage->getSetting('default_image');
|
||||
$default_image_settings['uuid'] = $default_images['field_storage_private']->uuid();
|
||||
$field_storage->setSetting('default_image', $default_image_settings);
|
||||
$field_storage->setSetting('uri_scheme', 'private');
|
||||
$field_storage->save();
|
||||
|
||||
// Confirm that the new default is used on the article field storage
|
||||
// settings form.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id/storage");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field_storage_private']->id(),
|
||||
format_string(
|
||||
'Updated image field storage default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_storage_private']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
// Upload a new default for the article's field after setting the field
|
||||
// storage upload destination to 'private'.
|
||||
$default_image_settings = $field->getSetting('default_image');
|
||||
$default_image_settings['uuid'] = $default_images['field_private']->uuid();
|
||||
$field->setSetting('default_image', $default_image_settings);
|
||||
$field->save();
|
||||
|
||||
// Confirm the new field field default is used on the article field
|
||||
// admin form.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field_private']->id(),
|
||||
format_string(
|
||||
'Updated article image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_private']->id()]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests image field and field having an invalid default image.
|
||||
* Tests image field and field storage having an invalid default image.
|
||||
*/
|
||||
public function testInvalidDefaultImage() {
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
|
||||
@@ -282,7 +282,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
||||
$field_name . '[0][alt]' => $image['#alt'],
|
||||
$field_name . '[0][title]' => $image['#title'],
|
||||
];
|
||||
$this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save and keep published'));
|
||||
$this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save'));
|
||||
$default_output = str_replace("\n", NULL, $renderer->renderRoot($image));
|
||||
$this->assertRaw($default_output, 'Image displayed using user supplied alt and title attributes.');
|
||||
|
||||
@@ -292,7 +292,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
||||
$field_name . '[0][alt]' => $this->randomMachineName($test_size),
|
||||
$field_name . '[0][title]' => $this->randomMachineName($test_size),
|
||||
];
|
||||
$this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save and keep published'));
|
||||
$this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save'));
|
||||
$schema = $field->getFieldStorageDefinition()->getSchema();
|
||||
$this->assertRaw(t('Alternative text cannot be longer than %max characters but is currently %length characters long.', [
|
||||
'%max' => $schema['columns']['alt']['length'],
|
||||
@@ -314,9 +314,9 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
||||
$edit = [
|
||||
'files[' . $field_name . '_1][]' => drupal_realpath($test_image->uri),
|
||||
];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
// Add the required alt text.
|
||||
$this->drupalPostForm(NULL, [$field_name . '[1][alt]' => $alt], t('Save and keep published'));
|
||||
$this->drupalPostForm(NULL, [$field_name . '[1][alt]' => $alt], t('Save'));
|
||||
$this->assertText(format_string('Article @title has been updated.', ['@title' => $node->getTitle()]));
|
||||
|
||||
// Assert ImageWidget::process() calls FieldWidget::process().
|
||||
|
||||
@@ -90,10 +90,10 @@ abstract class ImageFieldTestBase extends WebTestBase {
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$edit['files[' . $field_name . '_0]'] = drupal_realpath($image->uri);
|
||||
$this->drupalPostForm('node/add/' . $type, $edit, t('Save and publish'));
|
||||
$this->drupalPostForm('node/add/' . $type, $edit, t('Save'));
|
||||
if ($alt) {
|
||||
// Add alt text.
|
||||
$this->drupalPostForm(NULL, [$field_name . '[0][alt]' => $alt], t('Save and publish'));
|
||||
$this->drupalPostForm(NULL, [$field_name . '[0][alt]' => $alt], t('Save'));
|
||||
}
|
||||
|
||||
// Retrieve ID of the newly created node from the current URL.
|
||||
|
||||
@@ -119,7 +119,7 @@ class ImageFieldValidateTest extends ImageFieldTestBase {
|
||||
$edit = [
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save and publish'));
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
|
||||
$this->assertNoText(t('Alternative text field is required.'));
|
||||
$this->assertNoText(t('Title field is required.'));
|
||||
@@ -132,7 +132,7 @@ class ImageFieldValidateTest extends ImageFieldTestBase {
|
||||
$edit = [
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save and publish'));
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
|
||||
$this->assertNoText(t('Alternative text field is required.'));
|
||||
$this->assertNoText(t('Title field is required.'));
|
||||
|
||||
@@ -29,6 +29,9 @@ class ImageOnTranslatedEntityTest extends ImageFieldTestBase {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// This test expects unused managed files to be marked as a temporary file.
|
||||
$this->config('file.settings')->set('make_unused_managed_files_temporary', TRUE)->save();
|
||||
|
||||
// Create the "Basic page" node type.
|
||||
// @todo Remove the disabling of new revision creation in
|
||||
// https://www.drupal.org/node/1239558.
|
||||
|
||||
@@ -193,13 +193,13 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
||||
$this->drupalGet($generate_url_noaccess);
|
||||
$this->assertResponse(403, 'Confirmed that access is denied for the private image style.');
|
||||
// Verify that images are not appended to the response. Currently this test only uses PNG images.
|
||||
if (strpos($generate_url, '.png') === FALSE ) {
|
||||
if (strpos($generate_url, '.png') === FALSE) {
|
||||
$this->fail('Confirming that private image styles are not appended require PNG file.');
|
||||
}
|
||||
else {
|
||||
// Check for PNG-Signature (cf. http://www.libpng.org/pub/png/book/chapter08.html#png.ch08.div.2) in the
|
||||
// response body.
|
||||
$this->assertNoRaw( chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10), 'No PNG signature found in the response body.');
|
||||
$this->assertNoRaw(chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10), 'No PNG signature found in the response body.');
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -139,7 +139,7 @@ class QuickEditImageControllerTest extends WebTestBase {
|
||||
foreach ($this->drupalGetTestFiles('image') as $image) {
|
||||
/** @var \Drupal\Core\Image\ImageInterface $image_file */
|
||||
$image_file = $image_factory->get($image->uri);
|
||||
if ($image_file->getWidth() < 50 || $image_file->getWidth() > 100 ) {
|
||||
if ($image_file->getWidth() < 50 || $image_file->getWidth() > 100) {
|
||||
$invalid_image = $image;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ package: Testing
|
||||
# version: VERSION
|
||||
# core: 8.x
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1502903957
|
||||
datestamp: 1509719929
|
||||
|
||||
@@ -8,8 +8,8 @@ dependencies:
|
||||
- image
|
||||
- views
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1502903957
|
||||
datestamp: 1509719929
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Update;
|
||||
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests Image update path.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8-rc1.bare.standard.php.gz',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests image_post_update_image_style_dependencies().
|
||||
*
|
||||
* @see image_post_update_image_style_dependencies()
|
||||
*/
|
||||
public function testPostUpdateImageStylesDependencies() {
|
||||
$view = 'core.entity_view_display.node.article.default';
|
||||
$form = 'core.entity_form_display.node.article.default';
|
||||
|
||||
// Check that view display 'node.article.default' doesn't depend on image
|
||||
// style 'image.style.large'.
|
||||
$dependencies = $this->config($view)->get('dependencies.config');
|
||||
$this->assertFalse(in_array('image.style.large', $dependencies));
|
||||
// Check that form display 'node.article.default' doesn't depend on image
|
||||
// style 'image.style.thumbnail'.
|
||||
$dependencies = $this->config($form)->get('dependencies.config');
|
||||
$this->assertFalse(in_array('image.style.thumbnail', $dependencies));
|
||||
|
||||
// Run updates.
|
||||
$this->runUpdates();
|
||||
|
||||
// Check that view display 'node.article.default' depend on image style
|
||||
// 'image.style.large'.
|
||||
$dependencies = $this->config($view)->get('dependencies.config');
|
||||
$this->assertTrue(in_array('image.style.large', $dependencies));
|
||||
// Check that form display 'node.article.default' depend on image style
|
||||
// 'image.style.thumbnail'.
|
||||
$dependencies = $this->config($view)->get('dependencies.config');
|
||||
$this->assertTrue(in_array('image.style.large', $dependencies));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,8 @@ use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\Tests\field\Kernel\FieldKernelTestBase;
|
||||
|
||||
/**
|
||||
@@ -99,4 +101,89 @@ class ImageFormatterTest extends FieldKernelTestBase {
|
||||
$this->assertEquals($entity->{$this->fieldName}[1]->entity->getCacheTags(), $build[$this->fieldName][1]['#cache']['tags'], 'Second image cache tags is as expected');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests ImageFormatter's handling of SVG images.
|
||||
*
|
||||
* @requires extension gd
|
||||
*/
|
||||
public function testImageFormatterSvg() {
|
||||
// Install the default image styles.
|
||||
$this->installConfig(['image']);
|
||||
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
|
||||
$png = File::create([
|
||||
'uri' => 'public://test-image.png',
|
||||
]);
|
||||
$png->save();
|
||||
|
||||
// We need to create an actual empty PNG, or the GD toolkit will not
|
||||
// consider the image valid.
|
||||
$png_resource = imagecreate(300, 300);
|
||||
imagefill($png_resource, 0, 0, imagecolorallocate($png_resource, 0, 0, 0));
|
||||
imagepng($png_resource, $png->getFileUri());
|
||||
|
||||
$svg = File::create([
|
||||
'uri' => 'public://test-image.svg',
|
||||
]);
|
||||
$svg->save();
|
||||
// We don't have to put any real SVG data in here, because the GD toolkit
|
||||
// won't be able to load it anyway.
|
||||
touch($svg->getFileUri());
|
||||
|
||||
$entity = EntityTest::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
$this->fieldName => [$png, $svg],
|
||||
]);
|
||||
$entity->save();
|
||||
|
||||
// Ensure that the display is using the medium image style.
|
||||
$component = $this->display->getComponent($this->fieldName);
|
||||
$component['settings']['image_style'] = 'medium';
|
||||
$this->display->setComponent($this->fieldName, $component)->save();
|
||||
|
||||
$build = $this->display->build($entity);
|
||||
|
||||
// The first image is a PNG, so it is supported by the GD image toolkit.
|
||||
// The image style should be applied with its cache tags, image derivative
|
||||
// computed with its URI and dimensions.
|
||||
$this->assertCacheTags($build[$this->fieldName][0], ImageStyle::load('medium')->getCacheTags());
|
||||
$renderer->renderRoot($build[$this->fieldName][0]);
|
||||
$this->assertEquals('medium', $build[$this->fieldName][0]['#image_style']);
|
||||
// We check that the image URL contains the expected style directory
|
||||
// structure.
|
||||
$this->assertTrue(strpos($build[$this->fieldName][0]['#markup'], 'styles/medium/public/test-image.png') !== FALSE);
|
||||
$this->assertTrue(strpos($build[$this->fieldName][0]['#markup'], 'width="220"') !== FALSE);
|
||||
$this->assertTrue(strpos($build[$this->fieldName][0]['#markup'], 'height="220"') !== FALSE);
|
||||
|
||||
// The second image is an SVG, which is not supported by the GD toolkit.
|
||||
// The image style should still be applied with its cache tags, but image
|
||||
// derivative will not be available so <img> tag will point to the original
|
||||
// image.
|
||||
$this->assertCacheTags($build[$this->fieldName][1], ImageStyle::load('medium')->getCacheTags());
|
||||
$renderer->renderRoot($build[$this->fieldName][1]);
|
||||
$this->assertEquals('medium', $build[$this->fieldName][1]['#image_style']);
|
||||
// We check that the image URL does not contain the style directory
|
||||
// structure.
|
||||
$this->assertFalse(strpos($build[$this->fieldName][1]['#markup'], 'styles/medium/public/test-image.svg'));
|
||||
// Since we did not store original image dimensions, width and height
|
||||
// HTML attributes will not be present.
|
||||
$this->assertFalse(strpos($build[$this->fieldName][1]['#markup'], 'width'));
|
||||
$this->assertFalse(strpos($build[$this->fieldName][1]['#markup'], 'height'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a renderable array has a set of cache tags.
|
||||
*
|
||||
* @param array $renderable
|
||||
* The renderable array. Must have a #cache[tags] element.
|
||||
* @param array $cache_tags
|
||||
* The expected cache tags.
|
||||
*/
|
||||
protected function assertCacheTags(array $renderable, array $cache_tags) {
|
||||
$diff = array_diff($cache_tags, $renderable['#cache']['tags']);
|
||||
$this->assertEmpty($diff);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\Tests\image\Kernel;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
@@ -129,4 +130,26 @@ class ImageItemTest extends FieldKernelTestBase {
|
||||
$this->assertEqual($entity->image_test->entity->get('filemime')->value, 'image/jpeg');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a malformed image.
|
||||
*/
|
||||
public function testImageItemMalformed() {
|
||||
// Validate entity is an image and don't gather dimensions if it is not.
|
||||
$entity = EntityTest::create();
|
||||
$entity->image_test = NULL;
|
||||
$entity->image_test->target_id = 9999;
|
||||
// PHPUnit re-throws E_USER_WARNING as an exception.
|
||||
try {
|
||||
$entity->save();
|
||||
$this->fail('Exception did not fail');
|
||||
}
|
||||
catch (EntityStorageException $exception) {
|
||||
$this->assertInstanceOf(\PHPUnit_Framework_Error_Warning::class, $exception->getPrevious());
|
||||
$this->assertEquals($exception->getMessage(), 'Missing file with ID 9999.');
|
||||
$this->assertEmpty($entity->image_test->width);
|
||||
$this->assertEmpty($entity->image_test->height);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -141,9 +141,11 @@ class MigrateImageCacheTest extends MigrateDrupal6TestBase {
|
||||
|
||||
$this->startCollectingMessages();
|
||||
$this->executeMigration('d6_imagecache_presets');
|
||||
$this->assertEqual(['error' => [
|
||||
'The Drupal 8 image crop effect does not support numeric values for x and y offsets. Use keywords to set crop effect offsets instead.'
|
||||
]], $this->migrateMessages);
|
||||
$this->assertEqual([
|
||||
'error' => [
|
||||
'The Drupal 8 image crop effect does not support numeric values for x and y offsets. Use keywords to set crop effect offsets instead.',
|
||||
],
|
||||
], $this->migrateMessages);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user