updated core to 8.6.1 via composer
This commit is contained in:
@@ -76,6 +76,10 @@ image.effect.image_desaturate:
|
||||
image.effect.image_scale_and_crop:
|
||||
type: image_size
|
||||
label: 'Image scale and crop'
|
||||
mapping:
|
||||
anchor:
|
||||
label: 'Anchor'
|
||||
type: string
|
||||
|
||||
image.settings:
|
||||
type: config_object
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* Implement an image field, based on the file module's file field.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Render\Element;
|
||||
|
||||
/**
|
||||
@@ -64,7 +63,7 @@ function template_preprocess_image_formatter(&$variables) {
|
||||
$item = $variables['item'];
|
||||
|
||||
// Do not output an empty 'title' attribute.
|
||||
if (Unicode::strlen($item->title) != 0) {
|
||||
if (mb_strlen($item->title) != 0) {
|
||||
$variables['image']['#title'] = $item->title;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,5 +5,5 @@ package: Field types
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- file
|
||||
- drupal:file
|
||||
configure: entity.image_style.collection
|
||||
|
||||
@@ -67,4 +67,8 @@ function image_requirements($phase) {
|
||||
*/
|
||||
function image_update_8201() {
|
||||
// Empty update to trigger a cache flush.
|
||||
|
||||
// Use hook_post_update_NAME() instead to clear the cache. The use of
|
||||
// hook_update_N() to clear the cache has been deprecated see
|
||||
// https://www.drupal.org/node/2960601 for more details.
|
||||
}
|
||||
|
||||
@@ -152,6 +152,9 @@ function image_theme() {
|
||||
'image_crop_summary' => [
|
||||
'variables' => ['data' => NULL, 'effect' => []],
|
||||
],
|
||||
'image_scale_and_crop_summary' => [
|
||||
'variables' => ['data' => NULL, 'effect' => []],
|
||||
],
|
||||
'image_rotate_summary' => [
|
||||
'variables' => ['data' => NULL, 'effect' => []],
|
||||
],
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* Post-update functions for Image.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityUpdater;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
|
||||
@@ -20,3 +21,19 @@ function image_post_update_image_style_dependencies() {
|
||||
$display->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add 'anchor' setting to 'Scale and crop' effects.
|
||||
*/
|
||||
function image_post_update_scale_and_crop_effect_add_anchor(&$sandbox = NULL) {
|
||||
\Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'image_style', function ($image_style) {
|
||||
/** @var \Drupal\image\ImageStyleInterface $image_style */
|
||||
$effects = $image_style->getEffects();
|
||||
foreach ($effects as $effect) {
|
||||
if ($effect->getPluginId() === 'image_scale_and_crop') {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,337 +3,374 @@
|
||||
* 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': {
|
||||
const 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.
|
||||
const $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]);
|
||||
(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();
|
||||
}
|
||||
});
|
||||
|
||||
$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;
|
||||
/**
|
||||
* @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 'saved':
|
||||
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 'invalid':
|
||||
this.showValidationErrors();
|
||||
break;
|
||||
}
|
||||
},
|
||||
case 'highlighted':
|
||||
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');
|
||||
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');
|
||||
});
|
||||
// Re-position the toolbar, which could have changed size.
|
||||
fieldModel.get('entity').toolbarView.position();
|
||||
break;
|
||||
|
||||
case 'active': {
|
||||
const 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.
|
||||
const $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);
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 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')
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
} else {
|
||||
$dropzone = $(
|
||||
Drupal.theme('quickeditImageDropzone', {
|
||||
state,
|
||||
text,
|
||||
}),
|
||||
);
|
||||
this.$el.append($dropzone);
|
||||
}
|
||||
|
||||
return $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');
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* @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));
|
||||
);
|
||||
})(jQuery, _, Drupal);
|
||||
|
||||
@@ -203,7 +203,12 @@
|
||||
this.$el.html(this.model.get('originalValue'));
|
||||
},
|
||||
getQuickEditUISettings: function getQuickEditUISettings() {
|
||||
return { padding: false, unifiedToolbar: true, fullWidthToolbar: true, popup: false };
|
||||
return {
|
||||
padding: false,
|
||||
unifiedToolbar: true,
|
||||
fullWidthToolbar: true,
|
||||
popup: false
|
||||
};
|
||||
},
|
||||
showValidationErrors: function showValidationErrors() {
|
||||
var errors = Drupal.theme('quickeditImageErrors', {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Provides theme functions for image Quick Edit's client-side HTML.
|
||||
*/
|
||||
|
||||
(function (Drupal) {
|
||||
(function(Drupal) {
|
||||
/**
|
||||
* Theme function for validation errors of the Image in-place editor.
|
||||
*
|
||||
@@ -15,7 +15,7 @@
|
||||
* @return {string}
|
||||
* The corresponding HTML.
|
||||
*/
|
||||
Drupal.theme.quickeditImageErrors = function (settings) {
|
||||
Drupal.theme.quickeditImageErrors = function(settings) {
|
||||
return `<div class="quickedit-image-errors">${settings.errors}</div>`;
|
||||
};
|
||||
|
||||
@@ -33,11 +33,13 @@
|
||||
* @return {string}
|
||||
* The corresponding HTML.
|
||||
*/
|
||||
Drupal.theme.quickeditImageDropzone = function (settings) {
|
||||
return `<div class="quickedit-image-dropzone ${settings.state}">` +
|
||||
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>';
|
||||
'</div>'
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -61,22 +63,30 @@
|
||||
* @return {string}
|
||||
* The corresponding HTML.
|
||||
*/
|
||||
Drupal.theme.quickeditImageToolbar = function (settings) {
|
||||
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' : ''}/>` +
|
||||
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);
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
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>';
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
|
||||
|
||||
@@ -79,6 +80,8 @@ class ImageStyleDownloadController extends FileDownloadController {
|
||||
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse|\Symfony\Component\HttpFoundation\Response
|
||||
* The transferred file as response or some error response.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
* Thrown when the file request is invalid.
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
|
||||
* Thrown when the user does not have access to the file.
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException
|
||||
@@ -104,7 +107,11 @@ class ImageStyleDownloadController extends FileDownloadController {
|
||||
$valid &= $request->query->get(IMAGE_DERIVATIVE_TOKEN) === $image_style->getPathToken($image_uri);
|
||||
}
|
||||
if (!$valid) {
|
||||
throw new AccessDeniedHttpException();
|
||||
// Return a 404 (Page Not Found) rather than a 403 (Access Denied) as the
|
||||
// image token is for DDoS protection rather than access checking. 404s
|
||||
// are more likely to be cached (e.g. at a proxy) which enhances
|
||||
// protection from DDoS.
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$derivative_uri = $image_style->buildUri($image_uri);
|
||||
|
||||
@@ -14,7 +14,6 @@ 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;
|
||||
@@ -25,6 +24,13 @@ use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
* @ConfigEntityType(
|
||||
* id = "image_style",
|
||||
* label = @Translation("Image style"),
|
||||
* label_collection = @Translation("Image styles"),
|
||||
* label_singular = @Translation("image style"),
|
||||
* label_plural = @Translation("image styles"),
|
||||
* label_count = @PluralTranslation(
|
||||
* singular = "@count image style",
|
||||
* plural = "@count image styles",
|
||||
* ),
|
||||
* handlers = {
|
||||
* "form" = {
|
||||
* "add" = "Drupal\image\Form\ImageStyleAddForm",
|
||||
@@ -348,7 +354,7 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, Entity
|
||||
// Only support the URI if its extension is supported by the current image
|
||||
// toolkit.
|
||||
return in_array(
|
||||
Unicode::strtolower(pathinfo($uri, PATHINFO_EXTENSION)),
|
||||
mb_strtolower(pathinfo($uri, PATHINFO_EXTENSION)),
|
||||
$this->getImageFactory()->getSupportedExtensions()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ class ImageEffectDeleteForm extends ConfirmFormBase {
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->imageStyle->deleteImageEffect($this->imageEffect);
|
||||
drupal_set_message($this->t('The image effect %name has been deleted.', ['%name' => $this->imageEffect->label()]));
|
||||
$this->messenger()->addStatus($this->t('The image effect %name has been deleted.', ['%name' => $this->imageEffect->label()]));
|
||||
$form_state->setRedirectUrl($this->imageStyle->urlInfo('edit-form'));
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ abstract class ImageEffectFormBase extends FormBase {
|
||||
}
|
||||
$this->imageStyle->save();
|
||||
|
||||
drupal_set_message($this->t('The image effect was successfully applied.'));
|
||||
$this->messenger()->addStatus($this->t('The image effect was successfully applied.'));
|
||||
$form_state->setRedirectUrl($this->imageStyle->urlInfo('edit-form'));
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class ImageStyleAddForm extends ImageStyleFormBase {
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
drupal_set_message($this->t('Style %name was created.', ['%name' => $this->entity->label()]));
|
||||
$this->messenger()->addStatus($this->t('Style %name was created.', ['%name' => $this->entity->label()]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,7 @@ class ImageStyleDeleteForm extends EntityDeleteForm {
|
||||
public function getQuestion() {
|
||||
return $this->t('Optionally select a style before deleting %style', ['%style' => $this->entity->label()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
||||
@@ -231,7 +231,7 @@ class ImageStyleEditForm extends ImageStyleFormBase {
|
||||
$effect_id = $this->entity->addImageEffect($effect);
|
||||
$this->entity->save();
|
||||
if (!empty($effect_id)) {
|
||||
drupal_set_message($this->t('The image effect was successfully applied.'));
|
||||
$this->messenger()->addStatus($this->t('The image effect was successfully applied.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,17 +254,7 @@ class ImageStyleEditForm extends ImageStyleFormBase {
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
parent::save($form, $form_state);
|
||||
drupal_set_message($this->t('Changes to the style have been saved.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = $this->t('Update style');
|
||||
|
||||
return $actions;
|
||||
$this->messenger()->addStatus($this->t('Changes to the style have been saved.'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,7 +45,7 @@ class ImageStyleFlushForm extends EntityConfirmFormBase {
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->entity->flush();
|
||||
drupal_set_message($this->t('The image style %name has been flushed.', ['%name' => $this->entity->label()]));
|
||||
$this->messenger()->addStatus($this->t('The image style %name has been flushed.', ['%name' => $this->entity->label()]));
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ interface ImageStyleInterface extends ConfigEntityInterface {
|
||||
*/
|
||||
public function setName($name);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the URI of this image when using this style.
|
||||
*
|
||||
|
||||
@@ -115,7 +115,7 @@ class ImageFormatter extends ImageFormatterBase implements ContainerFactoryPlugi
|
||||
'#empty_option' => t('None (original image)'),
|
||||
'#options' => $image_styles,
|
||||
'#description' => $description_link->toRenderable() + [
|
||||
'#access' => $this->currentUser->hasPermission('administer image styles')
|
||||
'#access' => $this->currentUser->hasPermission('administer image styles'),
|
||||
],
|
||||
];
|
||||
$link_types = [
|
||||
|
||||
@@ -263,7 +263,7 @@ class ImageItem extends FileItem {
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable <em>Alt</em> field'),
|
||||
'#default_value' => $settings['alt_field'],
|
||||
'#description' => t('The alt attribute may be used by search engines, screen readers, and when the image cannot be loaded. Enabling this field is recommended.'),
|
||||
'#description' => t('Short description of the image used by screen readers and displayed when the image is not loaded. Enabling this field is recommended.'),
|
||||
'#weight' => 9,
|
||||
];
|
||||
$element['alt_field_required'] = [
|
||||
@@ -433,7 +433,7 @@ class ImageItem extends FileItem {
|
||||
$element['default_image']['alt'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Alternative text'),
|
||||
'#description' => t('This text will be used by screen readers, search engines, and when the image cannot be loaded.'),
|
||||
'#description' => t('Short description of the image used by screen readers and displayed when the image is not loaded. This is important for accessibility.'),
|
||||
'#default_value' => $settings['default_image']['alt'],
|
||||
'#maxlength' => 512,
|
||||
];
|
||||
|
||||
@@ -263,7 +263,7 @@ class ImageWidget extends FileWidget {
|
||||
'#title' => t('Alternative text'),
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => isset($item['alt']) ? $item['alt'] : '',
|
||||
'#description' => t('This text will be used by screen readers, search engines, or when the image cannot be loaded.'),
|
||||
'#description' => t('Short description of the image used by screen readers and displayed when the image is not loaded. This is important for accessibility.'),
|
||||
// @see https://www.drupal.org/node/465106#alt-text
|
||||
'#maxlength' => 512,
|
||||
'#weight' => -12,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Drupal\image\Plugin\ImageEffect;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Image\ImageInterface;
|
||||
use Drupal\image\ConfigurableImageEffectBase;
|
||||
@@ -41,7 +40,7 @@ class ConvertImageEffect extends ConfigurableImageEffectBase {
|
||||
*/
|
||||
public function getSummary() {
|
||||
$summary = [
|
||||
'#markup' => Unicode::strtoupper($this->configuration['extension']),
|
||||
'#markup' => mb_strtoupper($this->configuration['extension']),
|
||||
];
|
||||
$summary += parent::getSummary();
|
||||
|
||||
@@ -64,7 +63,7 @@ class ConvertImageEffect extends ConfigurableImageEffectBase {
|
||||
$extensions = \Drupal::service('image.toolkit.manager')->getDefaultToolkit()->getSupportedExtensions();
|
||||
$options = array_combine(
|
||||
$extensions,
|
||||
array_map(['\Drupal\Component\Utility\Unicode', 'strtoupper'], $extensions)
|
||||
array_map('mb_strtoupper', $extensions)
|
||||
);
|
||||
$form['extension'] = [
|
||||
'#type' => 'select',
|
||||
|
||||
@@ -13,17 +13,38 @@ use Drupal\Core\Image\ImageInterface;
|
||||
* description = @Translation("Scale and crop will maintain the aspect-ratio of the original image, then crop the larger dimension. This is most useful for creating perfectly square thumbnails without stretching the image.")
|
||||
* )
|
||||
*/
|
||||
class ScaleAndCropImageEffect extends ResizeImageEffect {
|
||||
class ScaleAndCropImageEffect extends CropImageEffect {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applyEffect(ImageInterface $image) {
|
||||
if (!$image->scaleAndCrop($this->configuration['width'], $this->configuration['height'])) {
|
||||
$width = $this->configuration['width'];
|
||||
$height = $this->configuration['height'];
|
||||
$scale = max($width / $image->getWidth(), $height / $image->getHeight());
|
||||
|
||||
list($x, $y) = explode('-', $this->configuration['anchor']);
|
||||
$x = image_filter_keyword($x, $image->getWidth() * $scale, $width);
|
||||
$y = image_filter_keyword($y, $image->getHeight() * $scale, $height);
|
||||
|
||||
if (!$image->apply('scale_and_crop', ['x' => $x, 'y' => $y, 'width' => $width, 'height' => $height])) {
|
||||
$this->logger->error('Image scale and crop failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', ['%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()]);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSummary() {
|
||||
$summary = [
|
||||
'#theme' => 'image_scale_and_crop_summary',
|
||||
'#data' => $this->configuration,
|
||||
];
|
||||
$summary += parent::getSummary();
|
||||
|
||||
return $summary;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ class ImageField extends FieldPluginBase {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processFieldValues(MigrationInterface $migration, $field_name, $data) {
|
||||
public function defineValueProcessPipeline(MigrationInterface $migration, $field_name, $data) {
|
||||
$process = [
|
||||
'plugin' => 'sub_process',
|
||||
'source' => $field_name,
|
||||
|
||||
@@ -20,7 +20,7 @@ class ImageStyleRoutes implements ContainerInjectionInterface {
|
||||
protected $streamWrapperManager;
|
||||
|
||||
/**
|
||||
* Constructs a new PathProcessorImageStyles object.
|
||||
* Constructs a new ImageStyleRoutes object.
|
||||
*
|
||||
* @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager
|
||||
* The stream wrapper manager service.
|
||||
|
||||
@@ -2,17 +2,20 @@
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
|
||||
@trigger_error('The ' . __NAMESPACE__ . '\ImageFieldTestBase class is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. Use \Drupal\Tests\image\Functional\ImageFieldTestBase instead. See https://www.drupal.org/node/2863626.', E_USER_DEPRECATED);
|
||||
|
||||
use Drupal\Tests\image\Kernel\ImageFieldCreationTrait;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* TODO: Test the following functions.
|
||||
*
|
||||
* image.effects.inc:
|
||||
* In file:
|
||||
* - image.effects.inc:
|
||||
* image_style_generate()
|
||||
* \Drupal\image\ImageStyleInterface::createDerivative()
|
||||
*
|
||||
* image.module:
|
||||
* - image.module:
|
||||
* image_style_options()
|
||||
* \Drupal\image\ImageStyleInterface::flush()
|
||||
* image_filter_keyword()
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation for a summary of an image scale and crop effect.
|
||||
*
|
||||
* Available variables:
|
||||
* - data: The current configuration for this resize effect, including:
|
||||
* - width: The width of the resized image.
|
||||
* - height: The height of the resized image.
|
||||
* - anchor: The part of the image that will be retained after cropping.
|
||||
* - anchor_label: The translated label of the crop anchor.
|
||||
* - effect: The effect information, including:
|
||||
* - id: The effect identifier.
|
||||
* - label: The effect name.
|
||||
* - description: The effect description.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
{% if data.width and data.height -%}
|
||||
{{ data.width }}×{{ data.height }}
|
||||
{%- else -%}
|
||||
{% if data.width %}
|
||||
{% trans %}
|
||||
width {{ data.width }}
|
||||
{% endtrans %}
|
||||
{% elseif data.height %}
|
||||
{% trans %}
|
||||
height {{ data.height }}
|
||||
{% endtrans %}
|
||||
{% endif %}
|
||||
{%- endif %}
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
langcode: en
|
||||
status: true
|
||||
name: test_scale_and_crop_add_anchor
|
||||
label: test_scale_and_crop_add_anchor
|
||||
effects:
|
||||
8c7170c9-5bcc-40f9-8698-f88a8be6d434:
|
||||
uuid: 8c7170c9-5bcc-40f9-8698-f88a8be6d434
|
||||
id: image_scale_and_crop
|
||||
weight: 1
|
||||
data:
|
||||
width: 100
|
||||
height: 100
|
||||
a8d83b12-abc6-40c8-9c2f-78a4e421cf97:
|
||||
uuid: a8d83b12-abc6-40c8-9c2f-78a4e421cf97
|
||||
id: image_scale_and_crop
|
||||
weight: 2
|
||||
data:
|
||||
width: 100
|
||||
height: 100
|
||||
anchor: left-top
|
||||
1bffd475-19d0-439a-b6a1-7e5850ce40f9:
|
||||
uuid: 1bffd475-19d0-439a-b6a1-7e5850ce40f9
|
||||
id: image_rotate
|
||||
weight: 3
|
||||
data:
|
||||
degrees: 180
|
||||
bgcolor: ''
|
||||
random: false
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test fixture.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Serialization\Yaml;
|
||||
|
||||
$connection = Database::getConnection();
|
||||
|
||||
$connection->insert('config')
|
||||
->fields([
|
||||
'collection' => '',
|
||||
'name' => 'image.style.test_scale_and_crop_add_anchor',
|
||||
'data' => serialize(Yaml::decode(file_get_contents('core/modules/image/tests/fixtures/update/image.image_style.test_scale_and_crop_add_anchor.yml'))),
|
||||
])
|
||||
->execute();
|
||||
@@ -5,5 +5,5 @@ package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- image
|
||||
- views
|
||||
- drupal:image
|
||||
- drupal:views
|
||||
|
||||
+9
-3
@@ -1,17 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the file move function for images and image styles.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class FileMoveTest extends WebTestBase {
|
||||
class FileMoveTest extends BrowserTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\image\Functional\Rest\ImageStyleResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class ImageStyleHalJsonAnonTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\image\Functional\Rest\ImageStyleResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class ImageStyleHalJsonBasicAuthTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal', 'basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\image\Functional\Rest\ImageStyleResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class ImageStyleHalJsonCookieTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
||||
+13
-48
@@ -1,13 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\image\ImageStyleInterface;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests creation, deletion, and editing of image styles and effects.
|
||||
@@ -16,6 +17,11 @@ use Drupal\file\Entity\File;
|
||||
*/
|
||||
class ImageAdminStylesTest extends ImageFieldTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an image style, generate an image.
|
||||
*/
|
||||
@@ -146,7 +152,7 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
|
||||
$uuids[$effect->getPluginId()] = $uuid;
|
||||
$effect_configuration = $effect->getConfiguration();
|
||||
foreach ($effect_edits[$effect->getPluginId()] as $field => $value) {
|
||||
$this->assertEqual($value, $effect_configuration['data'][$field], SafeMarkup::format('The %field field in the %effect effect has the correct value of %value.', ['%field' => $field, '%effect' => $effect->getPluginId(), '%value' => $value]));
|
||||
$this->assertEqual($value, $effect_configuration['data'][$field], new FormattableMarkup('The %field field in the %effect effect has the correct value of %value.', ['%field' => $field, '%effect' => $effect->getPluginId(), '%value' => $value]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +199,7 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
|
||||
$image_path = $this->createSampleImage($style);
|
||||
$this->assertEqual($this->getImageCount($style), 1, format_string('Image style %style image %file successfully generated.', ['%style' => $style->label(), '%file' => $image_path]));
|
||||
|
||||
$this->drupalPostForm($style_path, $edit, t('Update style'));
|
||||
$this->drupalPostForm($style_path, $edit, t('Save'));
|
||||
|
||||
// Note that after changing the style name, the style path is changed.
|
||||
$style_path = 'admin/config/media/image-styles/manage/' . $style_name;
|
||||
@@ -205,7 +211,7 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
|
||||
|
||||
// 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.');
|
||||
$this->assertEquals('Ajax test', $option[1]->getText(), '"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
|
||||
@@ -290,47 +296,6 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests editing Ajax-enabled image effect forms.
|
||||
*/
|
||||
public function testAjaxEnabledEffectForm() {
|
||||
$admin_path = 'admin/config/media/image-styles';
|
||||
|
||||
// Setup a style to be created and effects to add to it.
|
||||
$style_name = strtolower($this->randomMachineName(10));
|
||||
$style_label = $this->randomString();
|
||||
$style_path = $admin_path . '/manage/' . $style_name;
|
||||
$effect_edit = [
|
||||
'data[test_parameter]' => 100,
|
||||
];
|
||||
|
||||
// Add style form.
|
||||
$edit = [
|
||||
'name' => $style_name,
|
||||
'label' => $style_label,
|
||||
];
|
||||
$this->drupalPostForm($admin_path . '/add', $edit, t('Create new style'));
|
||||
$this->assertRaw(t('Style %name was created.', ['%name' => $style_label]));
|
||||
|
||||
// Add two Ajax-enabled test effects.
|
||||
$this->drupalPostForm($style_path, ['new' => 'image_module_test_ajax'], t('Add'));
|
||||
$this->drupalPostForm(NULL, $effect_edit, t('Add effect'));
|
||||
$this->drupalPostForm($style_path, ['new' => 'image_module_test_ajax'], t('Add'));
|
||||
$this->drupalPostForm(NULL, $effect_edit, t('Add effect'));
|
||||
|
||||
// Load the saved image style.
|
||||
$style = ImageStyle::load($style_name);
|
||||
|
||||
// Edit back the effects.
|
||||
foreach ($style->getEffects() as $uuid => $effect) {
|
||||
$effect_path = $admin_path . '/manage/' . $style_name . '/effects/' . $uuid;
|
||||
$this->drupalGet($effect_path);
|
||||
$this->drupalPostAjaxForm(NULL, $effect_edit, ['op' => t('Ajax refresh')]);
|
||||
$this->drupalPostForm(NULL, $effect_edit, t('Update effect'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test deleting a style and choosing a replacement style.
|
||||
*/
|
||||
@@ -372,7 +337,7 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
|
||||
'name' => $new_style_name,
|
||||
'label' => $new_style_label,
|
||||
];
|
||||
$this->drupalPostForm($style_path . $style_name, $edit, t('Update style'));
|
||||
$this->drupalPostForm($style_path . $style_name, $edit, t('Save'));
|
||||
$this->assertText(t('Changes to the style have been saved.'), format_string('Style %name was renamed to %new_name.', ['%name' => $style_name, '%new_name' => $new_style_name]));
|
||||
$this->drupalGet('node/' . $nid);
|
||||
|
||||
@@ -429,7 +394,7 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
|
||||
$rows = $this->xpath('//table/tbody/tr');
|
||||
$i = 0;
|
||||
foreach ($rows as $row) {
|
||||
if (((string) $row->td[0]) === 'Test style scale edit scale') {
|
||||
if ($row->find('css', 'td')->getText() === 'Test style scale edit scale') {
|
||||
$this->clickLink('Edit', $i);
|
||||
break;
|
||||
}
|
||||
+12
-6
@@ -1,16 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests that images have correct dimensions when styled.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageDimensionsTest extends WebTestBase {
|
||||
class ImageDimensionsTest extends BrowserTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
@@ -280,9 +286,9 @@ class ImageDimensionsTest extends WebTestBase {
|
||||
/**
|
||||
* Render an image style element.
|
||||
*
|
||||
* drupal_render() alters the passed $variables array by adding a new key
|
||||
* '#printed' => TRUE. This prevents next call to re-render the element. We
|
||||
* wrap drupal_render() in a helper protected method and pass each time a
|
||||
* Function drupal_render() alters the passed $variables array by adding a new
|
||||
* key '#printed' => TRUE. This prevents next call to re-render the element.
|
||||
* We wrap drupal_render() in a helper protected method and pass each time a
|
||||
* fresh array so that $variables won't get altered and the element is
|
||||
* re-rendered each time.
|
||||
*/
|
||||
@@ -111,8 +111,29 @@ class ImageEffectsTest extends ToolkitTestBase {
|
||||
|
||||
// Check the parameters.
|
||||
$calls = $this->imageTestGetAllCalls();
|
||||
$this->assertEqual($calls['scale_and_crop'][0][0], 5, 'Width was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][1], 10, 'Height was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][0], 7.5, 'X was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][1], 0, 'Y was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][2], 5, 'Width was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][3], 10, 'Height was computed and passed correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the image_scale_and_crop_effect() function with an anchor.
|
||||
*/
|
||||
public function testScaleAndCropEffectWithAnchor() {
|
||||
$this->assertImageEffect('image_scale_and_crop', [
|
||||
'anchor' => 'top-1',
|
||||
'width' => 5,
|
||||
'height' => 10,
|
||||
]);
|
||||
$this->assertToolkitOperationsCalled(['scale_and_crop']);
|
||||
|
||||
// Check the parameters.
|
||||
$calls = $this->imageTestGetAllCalls();
|
||||
$this->assertEqual($calls['scale_and_crop'][0][0], 0, 'X was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][1], 1, 'Y was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][2], 5, 'Width was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][3], 10, 'Height was computed and passed correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+14
-5
@@ -1,12 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\Tests\EntityViewTrait;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests setting up default images both to the field and field storage.
|
||||
@@ -15,6 +16,14 @@ use Drupal\field\Entity\FieldStorageConfig;
|
||||
*/
|
||||
class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
use EntityViewTrait {
|
||||
buildEntityView as drupalBuildEntityView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
@@ -359,13 +368,13 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
||||
*/
|
||||
public function testInvalidDefaultImage() {
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => Unicode::strtolower($this->randomMachineName()),
|
||||
'field_name' => mb_strtolower($this->randomMachineName()),
|
||||
'entity_type' => 'node',
|
||||
'type' => 'image',
|
||||
'settings' => [
|
||||
'default_image' => [
|
||||
'uuid' => 100000,
|
||||
]
|
||||
],
|
||||
],
|
||||
]);
|
||||
$field_storage->save();
|
||||
@@ -380,7 +389,7 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
||||
'settings' => [
|
||||
'default_image' => [
|
||||
'uuid' => 100000,
|
||||
]
|
||||
],
|
||||
],
|
||||
]);
|
||||
$field->save();
|
||||
+23
-11
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
|
||||
use Drupal\user\RoleInterface;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
|
||||
@@ -14,6 +16,12 @@ use Drupal\image\Entity\ImageStyle;
|
||||
*/
|
||||
class ImageFieldDisplayTest extends ImageFieldTestBase {
|
||||
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
protected $dumpHeaders = TRUE;
|
||||
|
||||
/**
|
||||
@@ -54,7 +62,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
||||
$this->drupalGet("admin/structure/types/manage/article/display");
|
||||
|
||||
// Test for existence of link to image styles configuration.
|
||||
$this->drupalPostAjaxForm(NULL, [], "{$field_name}_settings_edit");
|
||||
$this->drupalPostForm(NULL, [], "{$field_name}_settings_edit");
|
||||
$this->assertLinkByHref(\Drupal::url('entity.image_style.collection'), 0, 'Link to image styles configuration is found');
|
||||
|
||||
// Remove 'administer image styles' permission from testing admin user.
|
||||
@@ -65,7 +73,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
||||
$this->drupalGet("admin/structure/types/manage/article/display");
|
||||
|
||||
// Test for absence of link to image styles configuration.
|
||||
$this->drupalPostAjaxForm(NULL, [], "{$field_name}_settings_edit");
|
||||
$this->drupalPostForm(NULL, [], "{$field_name}_settings_edit");
|
||||
$this->assertNoLinkByHref(\Drupal::url('entity.image_style.collection'), 'Link to image styles configuration is absent when permissions are insufficient');
|
||||
|
||||
// Restore 'administer image styles' permission to testing admin user
|
||||
@@ -258,8 +266,11 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
||||
|
||||
$nid = $this->uploadNodeImage($test_image, $field_name, 'article', $alt);
|
||||
$this->drupalGet('node/' . $nid . '/edit');
|
||||
$this->assertFieldByName($field_name . '[0][alt]', '', 'Alt field displayed on article form.');
|
||||
|
||||
// Verify that the optional fields alt & title are saved & filled.
|
||||
$this->assertFieldByName($field_name . '[0][alt]', $alt, 'Alt field displayed on article form.');
|
||||
$this->assertFieldByName($field_name . '[0][title]', '', 'Title field displayed on article form.');
|
||||
|
||||
// Verify that the attached image is being previewed using the 'medium'
|
||||
// style.
|
||||
$node_storage->resetCache([$nid]);
|
||||
@@ -324,9 +335,9 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
||||
$edit = [
|
||||
'files[' . $field_name . '_2][]' => \Drupal::service('file_system')->realpath($test_image->uri),
|
||||
];
|
||||
$this->drupalPostAjaxForm(NULL, $edit, $field_name . '_2_upload_button');
|
||||
$this->assertNoRaw('<input multiple type="file" id="edit-' . strtr($field_name, '_', '-') . '-2-upload" name="files[' . $field_name . '_2][]" size="22" class="js-form-file form-file">');
|
||||
$this->assertRaw('<input multiple type="file" id="edit-' . strtr($field_name, '_', '-') . '-3-upload" name="files[' . $field_name . '_3][]" size="22" class="js-form-file form-file">');
|
||||
$this->drupalPostForm(NULL, $edit, $field_name . '_2_upload_button');
|
||||
$this->assertSession()->elementNotExists('css', 'input[name="files[' . $field_name . '_2][]"]');
|
||||
$this->assertSession()->elementExists('css', 'input[name="files[' . $field_name . '_3][]"]');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -410,10 +421,11 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
||||
$this->assertRaw($image_output, 'User supplied image is displayed.');
|
||||
|
||||
// Remove default image from the field and make sure it is no longer used.
|
||||
$edit = [
|
||||
'settings[default_image][uuid][fids]' => 0,
|
||||
];
|
||||
$this->drupalPostForm("admin/structure/types/manage/article/fields/node.article.$field_name/storage", $edit, t('Save field settings'));
|
||||
// Can't use fillField cause Mink can't fill hidden fields.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/node.article.$field_name/storage");
|
||||
$this->getSession()->getPage()->find('css', 'input[name="settings[default_image][uuid][fids]"]')->setValue(0);
|
||||
$this->getSession()->getPage()->pressButton(t('Save field settings'));
|
||||
|
||||
// Clear field definition cache so the new default image is detected.
|
||||
\Drupal::entityManager()->clearCachedFieldDefinitions();
|
||||
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
|
||||
@@ -8,11 +8,12 @@ use Drupal\Tests\BrowserTestBase;
|
||||
/**
|
||||
* TODO: Test the following functions.
|
||||
*
|
||||
* image.effects.inc:
|
||||
* In file:
|
||||
* - image.effects.inc:
|
||||
* image_style_generate()
|
||||
* \Drupal\image\ImageStyleInterface::createDerivative()
|
||||
*
|
||||
* image.module:
|
||||
* - image.module:
|
||||
* image_style_options()
|
||||
* \Drupal\image\ImageStyleInterface::flush()
|
||||
* image_filter_keyword()
|
||||
@@ -87,10 +88,10 @@ abstract class ImageFieldTestBase extends BrowserTestBase {
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$edit['files[' . $field_name . '_0]'] = \Drupal::service('file_system')->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.
|
||||
|
||||
+13
-75
@@ -1,9 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests validation functions such as min/max resolution.
|
||||
@@ -12,6 +11,11 @@ use Drupal\field\Entity\FieldConfig;
|
||||
*/
|
||||
class ImageFieldValidateTest extends ImageFieldTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test image validity.
|
||||
*/
|
||||
@@ -77,27 +81,27 @@ class ImageFieldValidateTest extends ImageFieldTestBase {
|
||||
];
|
||||
$min_resolution = [
|
||||
'width' => 50,
|
||||
'height' => 50
|
||||
'height' => 50,
|
||||
];
|
||||
$max_resolution = [
|
||||
'width' => 100,
|
||||
'height' => 100
|
||||
'height' => 100,
|
||||
];
|
||||
$no_height_min_resolution = [
|
||||
'width' => 50,
|
||||
'height' => NULL
|
||||
'height' => NULL,
|
||||
];
|
||||
$no_height_max_resolution = [
|
||||
'width' => 100,
|
||||
'height' => NULL
|
||||
'height' => NULL,
|
||||
];
|
||||
$no_width_min_resolution = [
|
||||
'width' => NULL,
|
||||
'height' => 50
|
||||
'height' => 50,
|
||||
];
|
||||
$no_width_max_resolution = [
|
||||
'width' => NULL,
|
||||
'height' => 100
|
||||
'height' => 100,
|
||||
];
|
||||
$field_settings = [
|
||||
0 => $this->getFieldSettings($min_resolution, $max_resolution),
|
||||
@@ -219,70 +223,4 @@ class ImageFieldValidateTest extends ImageFieldTestBase {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the validation message is displayed only once for ajax uploads.
|
||||
*/
|
||||
public function testAJAXValidationMessage() {
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$this->createImageField($field_name, 'article', ['cardinality' => -1]);
|
||||
|
||||
$this->drupalGet('node/add/article');
|
||||
/** @var \Drupal\file\FileInterface[] $text_files */
|
||||
$text_files = $this->drupalGetTestFiles('text');
|
||||
$text_file = reset($text_files);
|
||||
$edit = [
|
||||
'files[' . $field_name . '_0][]' => $this->container->get('file_system')->realpath($text_file->uri),
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalPostAjaxForm(NULL, $edit, $field_name . '_0_upload_button');
|
||||
$elements = $this->xpath('//div[contains(@class, :class)]', [
|
||||
':class' => 'messages--error',
|
||||
]);
|
||||
$this->assertEqual(count($elements), 1, 'Ajax validation messages are displayed once.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that image field validation works with other form submit handlers.
|
||||
*/
|
||||
public function testFriendlyAjaxValidation() {
|
||||
// Add a custom field to the Article content type that contains an AJAX
|
||||
// handler on a select field.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'field_dummy_select',
|
||||
'type' => 'image_module_test_dummy_ajax',
|
||||
'entity_type' => 'node',
|
||||
'cardinality' => 1,
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'article',
|
||||
'field_name' => 'field_dummy_select',
|
||||
'label' => t('Dummy select'),
|
||||
])->save();
|
||||
|
||||
\Drupal::entityTypeManager()
|
||||
->getStorage('entity_form_display')
|
||||
->load('node.article.default')
|
||||
->setComponent(
|
||||
'field_dummy_select',
|
||||
[
|
||||
'type' => 'image_module_test_dummy_ajax_widget',
|
||||
'weight' => 1,
|
||||
])
|
||||
->save();
|
||||
|
||||
// Then, add an image field.
|
||||
$this->createImageField('field_dummy_image', 'article');
|
||||
|
||||
// Open an article and trigger the AJAX handler.
|
||||
$this->drupalGet('node/add/article');
|
||||
$edit = [
|
||||
'field_dummy_select[select_widget]' => 'bam',
|
||||
];
|
||||
$this->drupalPostAjaxForm(NULL, $edit, 'field_dummy_select[select_widget]');
|
||||
}
|
||||
|
||||
}
|
||||
+7
-1
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Uploads images to translated nodes.
|
||||
@@ -11,6 +12,11 @@ use Drupal\file\Entity\File;
|
||||
*/
|
||||
class ImageOnTranslatedEntityTest extends ImageFieldTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
+8
-2
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests flushing of image styles.
|
||||
@@ -11,6 +12,11 @@ use Drupal\image\Entity\ImageStyle;
|
||||
*/
|
||||
class ImageStyleFlushTest extends ImageFieldTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an image style and a wrapper, generate an image.
|
||||
*/
|
||||
@@ -97,7 +103,7 @@ class ImageStyleFlushTest extends ImageFieldTestBase {
|
||||
}
|
||||
$this->drupalPostForm($style_path . '/effects/' . $uuids['image_scale'] . '/delete', [], t('Delete'));
|
||||
$this->assertResponse(200);
|
||||
$this->drupalPostForm($style_path, [], t('Update style'));
|
||||
$this->drupalPostForm($style_path, [], t('Save'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Post flush, expected 1 image in the 'public' wrapper (sample.png).
|
||||
+18
-10
@@ -1,17 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the functions for generating paths and URLs for image styles.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageStylesPathAndUrlTest extends WebTestBase {
|
||||
class ImageStylesPathAndUrlTest extends BrowserTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
@@ -170,10 +176,10 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
||||
}
|
||||
// Add some extra chars to the token.
|
||||
$this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', IMAGE_DERIVATIVE_TOKEN . '=Zo', $generate_url));
|
||||
$this->assertResponse(403, 'Image was inaccessible at the URL with an invalid token.');
|
||||
$this->assertResponse(404, 'Image was inaccessible at the URL with an invalid token.');
|
||||
// Change the parameter name so the token is missing.
|
||||
$this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', 'wrongparam=', $generate_url));
|
||||
$this->assertResponse(403, 'Image was inaccessible at the URL with a missing token.');
|
||||
$this->assertResponse(404, 'Image was inaccessible at the URL with a missing token.');
|
||||
|
||||
// Check that the generated URL is the same when we pass in a relative path
|
||||
// rather than a URI. We need to temporarily switch the default scheme to
|
||||
@@ -189,7 +195,8 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
||||
$this->drupalGet($generate_url);
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$this->assertRaw(file_get_contents($generated_uri), 'URL returns expected file.');
|
||||
// assertRaw can't be used with string containing non UTF-8 chars.
|
||||
$this->assertNotEmpty(file_get_contents($generated_uri), 'URL returns expected file.');
|
||||
$image = $this->container->get('image.factory')->get($generated_uri);
|
||||
$this->assertEqual($this->drupalGetHeader('Content-Type'), $image->getMimeType(), 'Expected Content-Type was reported.');
|
||||
$this->assertEqual($this->drupalGetHeader('Content-Length'), $image->getFileSize(), 'Expected Content-Length was reported.');
|
||||
@@ -240,7 +247,8 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
||||
// 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.');
|
||||
$raw = $this->getSession()->getPage()->getContent();
|
||||
$this->assertFalse(strpos($raw, chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -291,13 +299,13 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
||||
$this->assertTrue($matches_expected_url_format, "URL for a derivative of an image style matches expected format.");
|
||||
$nested_url_with_wrong_token = str_replace(IMAGE_DERIVATIVE_TOKEN . '=', 'wrongparam=', $nested_url);
|
||||
$this->drupalGet($nested_url_with_wrong_token);
|
||||
$this->assertResponse(403, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token.');
|
||||
$this->assertResponse(404, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token.');
|
||||
// Check that this restriction cannot be bypassed by adding extra slashes
|
||||
// to the URL.
|
||||
$this->drupalGet(substr_replace($nested_url_with_wrong_token, '//styles/', strrpos($nested_url_with_wrong_token, '/styles/'), strlen('/styles/')));
|
||||
$this->assertResponse(403, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token, even with an extra forward slash in the URL.');
|
||||
$this->assertResponse(404, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token, even with an extra forward slash in the URL.');
|
||||
$this->drupalGet(substr_replace($nested_url_with_wrong_token, '////styles/', strrpos($nested_url_with_wrong_token, '/styles/'), strlen('/styles/')));
|
||||
$this->assertResponse(403, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token, even with multiple forward slashes in the URL.');
|
||||
$this->assertResponse(404, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token, even with multiple forward slashes in the URL.');
|
||||
// Make sure the image can still be generated if a correct token is used.
|
||||
$this->drupalGet($nested_url);
|
||||
$this->assertResponse(200, 'Image was accessible when a correct token was provided in the URL.');
|
||||
+25
-25
@@ -1,18 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\image\Kernel\ImageFieldCreationTrait;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the endpoints used by the "image" in-place editor.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class QuickEditImageControllerTest extends WebTestBase {
|
||||
class QuickEditImageControllerTest extends BrowserTestBase {
|
||||
|
||||
use ImageFieldCreationTrait;
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -77,8 +82,11 @@ class QuickEditImageControllerTest extends WebTestBase {
|
||||
]);
|
||||
$this->drupalGet('quickedit/image/info/node/' . $node->id() . '/' . $this->fieldName . '/' . $node->language()->getId() . '/default');
|
||||
$this->assertResponse('403');
|
||||
$this->drupalPost('quickedit/image/upload/node/' . $node->id() . '/' . $this->fieldName . '/' . $node->language()->getId() . '/default', 'application/json', []);
|
||||
$this->assertResponse('403');
|
||||
|
||||
/** @var \Symfony\Component\BrowserKit\Client $client */
|
||||
$client = $this->getSession()->getDriver()->getClient();
|
||||
$client->request('POST', '/quickedit/image/upload/node/' . $node->id() . '/' . $this->fieldName . '/' . $node->language()->getId() . '/default');
|
||||
$this->assertEquals('403', $client->getResponse()->getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +98,8 @@ class QuickEditImageControllerTest extends WebTestBase {
|
||||
'type' => 'article',
|
||||
'title' => t('Test Node'),
|
||||
]);
|
||||
$info = $this->drupalGetJSON('quickedit/image/info/node/' . $node->id() . '/' . $this->fieldName . '/' . $node->language()->getId() . '/default');
|
||||
$json = $this->drupalGet('quickedit/image/info/node/' . $node->id() . '/' . $this->fieldName . '/' . $node->language()->getId() . '/default', ['query' => ['_format' => 'json']]);
|
||||
$info = Json::decode($json);
|
||||
// Assert that the default settings for our field are respected by our JSON
|
||||
// endpoint.
|
||||
$this->assertTrue($info['alt_field']);
|
||||
@@ -118,8 +127,10 @@ class QuickEditImageControllerTest extends WebTestBase {
|
||||
}
|
||||
}
|
||||
$this->assertTrue($valid_image);
|
||||
|
||||
$this->drupalLogin($this->contentAuthorUser);
|
||||
$this->uploadImage($valid_image, $node->id(), $this->fieldName, $node->language()->getId());
|
||||
$this->assertText('fid', t('Valid upload completed successfully.'));
|
||||
$this->assertContains('"fid":"1"', $this->getSession()->getPage()->getContent(), 'Valid upload completed successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,8 +156,10 @@ class QuickEditImageControllerTest extends WebTestBase {
|
||||
}
|
||||
}
|
||||
$this->assertTrue($invalid_image);
|
||||
|
||||
$this->drupalLogin($this->contentAuthorUser);
|
||||
$this->uploadImage($invalid_image, $node->id(), $this->fieldName, $node->language()->getId());
|
||||
$this->assertText('main_error', t('Invalid upload returned errors.'));
|
||||
$this->assertContains('"main_error":"The image failed validation."', $this->getSession()->getPage()->getContent(), 'Invalid upload returned errors.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,27 +173,14 @@ class QuickEditImageControllerTest extends WebTestBase {
|
||||
* The target field machine name.
|
||||
* @param string $langcode
|
||||
* The langcode to use when setting the field's value.
|
||||
*
|
||||
* @return mixed
|
||||
* The content returned from the call to $this->curlExec().
|
||||
*/
|
||||
public function uploadImage($image, $nid, $field_name, $langcode) {
|
||||
$filepath = $this->container->get('file_system')->realpath($image->uri);
|
||||
$data = [
|
||||
'files[image]' => curl_file_create($filepath),
|
||||
];
|
||||
$path = 'quickedit/image/upload/node/' . $nid . '/' . $field_name . '/' . $langcode . '/default';
|
||||
// We assemble the curl request ourselves as drupalPost cannot process file
|
||||
// uploads, and drupalPostForm only works with typical Drupal forms.
|
||||
return $this->curlExec([
|
||||
CURLOPT_URL => $this->buildUrl($path, []),
|
||||
CURLOPT_POST => TRUE,
|
||||
CURLOPT_POSTFIELDS => $data,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Accept: application/json',
|
||||
'Content-Type: multipart/form-data',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->prepareRequest();
|
||||
$client = $this->getSession()->getDriver()->getClient();
|
||||
$client->request('POST', $this->buildUrl($path, []), [], ['files[image]' => $filepath]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ImageStyleJsonAnonTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ImageStyleJsonBasicAuthTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ImageStyleJsonCookieTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Rest;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
|
||||
/**
|
||||
* ResourceTestBase for ImageStyle entity.
|
||||
*/
|
||||
abstract class ImageStyleResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['image'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'image_style';
|
||||
|
||||
/**
|
||||
* The ImageStyle entity.
|
||||
*
|
||||
* @var \Drupal\image\ImageStyleInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* The effect UUID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $effectUuid;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer image styles']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
// Create a "Camelids" image style.
|
||||
$camelids = ImageStyle::create([
|
||||
'name' => 'camelids',
|
||||
'label' => 'Camelids',
|
||||
]);
|
||||
|
||||
// Add an image effect.
|
||||
$effect = [
|
||||
'id' => 'image_scale_and_crop',
|
||||
'data' => [
|
||||
'anchor' => 'center-center',
|
||||
'width' => 120,
|
||||
'height' => 121,
|
||||
],
|
||||
'weight' => 0,
|
||||
];
|
||||
$this->effectUuid = $camelids->addImageEffect($effect);
|
||||
|
||||
$camelids->save();
|
||||
|
||||
return $camelids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'dependencies' => [],
|
||||
'effects' => [
|
||||
$this->effectUuid => [
|
||||
'uuid' => $this->effectUuid,
|
||||
'id' => 'image_scale_and_crop',
|
||||
'weight' => 0,
|
||||
'data' => [
|
||||
'anchor' => 'center-center',
|
||||
'width' => 120,
|
||||
'height' => 121,
|
||||
],
|
||||
],
|
||||
],
|
||||
'label' => 'Camelids',
|
||||
'langcode' => 'en',
|
||||
'name' => 'camelids',
|
||||
'status' => TRUE,
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
return "The 'administer image styles' permission is required.";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ImageStyleXmlAnonTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testGet() {
|
||||
// @todo Remove this method override in https://www.drupal.org/node/2905655
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ImageStyleXmlBasicAuthTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testGet() {
|
||||
// @todo Remove this method override in https://www.drupal.org/node/2905655
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ImageStyleXmlCookieTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testGet() {
|
||||
// @todo Remove this method override in https://www.drupal.org/node/2905655
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,6 +8,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
* Tests Image update path.
|
||||
*
|
||||
* @group image
|
||||
* @group legacy
|
||||
*/
|
||||
class ImageUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Update;
|
||||
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests adding an 'anchor' setting to existing scale and crop image effects.
|
||||
*
|
||||
* @see image_post_update_scale_and_crop_effect_add_anchor()
|
||||
*
|
||||
* @group Update
|
||||
* @group legacy
|
||||
*/
|
||||
class ScaleAndCropAddAnchorUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.4.0.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../fixtures/update/test_scale_and_crop_add_anchor.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that 'anchor' setting is properly added.
|
||||
*/
|
||||
public function testImagePostUpdateScaleAndCropEffectAddAnchor() {
|
||||
// Test that the first effect does not have an 'anchor' setting.
|
||||
$effect_data = $this->config('image.style.test_scale_and_crop_add_anchor')->get('effects.8c7170c9-5bcc-40f9-8698-f88a8be6d434.data');
|
||||
$this->assertFalse(array_key_exists('anchor', $effect_data));
|
||||
|
||||
// Test that the second effect has an 'anchor' setting.
|
||||
$effect_data = $this->config('image.style.test_scale_and_crop_add_anchor')->get('effects.a8d83b12-abc6-40c8-9c2f-78a4e421cf97.data');
|
||||
$this->assertTrue(array_key_exists('anchor', $effect_data));
|
||||
|
||||
// Test that the third effect does not have an 'anchor' setting.
|
||||
$effect_data = $this->config('image.style.test_scale_and_crop_add_anchor')->get('effects.1bffd475-19d0-439a-b6a1-7e5850ce40f9.data');
|
||||
$this->assertFalse(array_key_exists('anchor', $effect_data));
|
||||
|
||||
$this->runUpdates();
|
||||
|
||||
// Test that the first effect now has an 'anchor' setting.
|
||||
$effect_data = $this->config('image.style.test_scale_and_crop_add_anchor')->get('effects.8c7170c9-5bcc-40f9-8698-f88a8be6d434.data');
|
||||
$this->assertTrue(array_key_exists('anchor', $effect_data));
|
||||
$this->assertEquals('center-center', $effect_data['anchor']);
|
||||
|
||||
// Test that the second effect's 'anchor' setting is unchanged.
|
||||
$effect_data = $this->config('image.style.test_scale_and_crop_add_anchor')->get('effects.a8d83b12-abc6-40c8-9c2f-78a4e421cf97.data');
|
||||
$this->assertTrue(array_key_exists('anchor', $effect_data));
|
||||
$this->assertEquals('left-top', $effect_data['anchor']);
|
||||
|
||||
// Test that the third effect still does not have an 'anchor' setting.
|
||||
$effect_data = $this->config('image.style.test_scale_and_crop_add_anchor')->get('effects.1bffd475-19d0-439a-b6a1-7e5850ce40f9.data');
|
||||
$this->assertFalse(array_key_exists('anchor', $effect_data));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\FunctionalJavascript;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
|
||||
/**
|
||||
* Tests creation, deletion, and editing of image styles and effects.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageAdminStylesTest extends ImageFieldTestBase {
|
||||
|
||||
/**
|
||||
* Tests editing Ajax-enabled image effect forms.
|
||||
*/
|
||||
public function testAjaxEnabledEffectForm() {
|
||||
$admin_path = 'admin/config/media/image-styles';
|
||||
|
||||
// Setup a style to be created and effects to add to it.
|
||||
$style_name = strtolower($this->randomMachineName(10));
|
||||
$style_label = $this->randomString();
|
||||
$style_path = $admin_path . '/manage/' . $style_name;
|
||||
$effect_edit = [
|
||||
'data[test_parameter]' => 100,
|
||||
];
|
||||
|
||||
// Add style form.
|
||||
$page = $this->getSession()->getPage();
|
||||
$assert = $this->assertSession();
|
||||
$this->drupalGet($admin_path . '/add');
|
||||
$page->findField('label')->setValue($style_label);
|
||||
$assert->waitForElementVisible('named', ['button', 'Edit'])->press();
|
||||
$assert->waitForElementVisible('named', ['id_or_name', 'name'])->setValue($style_name);
|
||||
$page->pressButton('Create new style');
|
||||
$assert->pageTextContains("Style $style_label was created.");
|
||||
|
||||
// Add two Ajax-enabled test effects.
|
||||
$this->drupalPostForm($style_path, ['new' => 'image_module_test_ajax'], t('Add'));
|
||||
$this->drupalPostForm(NULL, $effect_edit, t('Add effect'));
|
||||
$this->drupalPostForm($style_path, ['new' => 'image_module_test_ajax'], t('Add'));
|
||||
$this->drupalPostForm(NULL, $effect_edit, t('Add effect'));
|
||||
|
||||
// Load the saved image style.
|
||||
$style = ImageStyle::load($style_name);
|
||||
|
||||
// Edit back the effects.
|
||||
foreach ($style->getEffects() as $uuid => $effect) {
|
||||
$effect_path = $admin_path . '/manage/' . $style_name . '/effects/' . $uuid;
|
||||
$this->drupalGet($effect_path);
|
||||
$page->findField('data[test_parameter]')->setValue(111);
|
||||
$ajax_value = $page->find('css', '#ajax-value')->getText();
|
||||
$this->assertSame('Ajax value bar', $ajax_value);
|
||||
$this->getSession()->getPage()->pressButton('Ajax refresh');
|
||||
$this->assertTrue($page->waitFor(10, function ($page) {
|
||||
$ajax_value = $page->find('css', '#ajax-value')->getText();
|
||||
return preg_match('/^Ajax value [0-9.]+ [0-9.]+$/', $ajax_value);
|
||||
}));
|
||||
$page->pressButton('Update effect');
|
||||
$assert->pageTextContains('The image effect was successfully applied.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\FunctionalJavascript;
|
||||
|
||||
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
|
||||
use Drupal\Tests\image\Kernel\ImageFieldCreationTrait;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* This class provides methods specifically for testing Image's field handling.
|
||||
*/
|
||||
abstract class ImageFieldTestBase extends WebDriverTestBase {
|
||||
|
||||
use ImageFieldCreationTrait;
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'node',
|
||||
'image',
|
||||
'field_ui',
|
||||
'image_module_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* An user with permissions to administer content types and image styles.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create Basic page and Article node types.
|
||||
if ($this->profile !== 'standard') {
|
||||
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
|
||||
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
|
||||
}
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'access administration pages',
|
||||
'administer site configuration',
|
||||
'administer content types',
|
||||
'administer node fields',
|
||||
'administer nodes',
|
||||
'create article content',
|
||||
'edit any article content',
|
||||
'delete any article content',
|
||||
'administer image styles',
|
||||
'administer node display',
|
||||
]);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\FunctionalJavascript;
|
||||
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
|
||||
/**
|
||||
* Tests validation functions such as min/max resolution.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageFieldValidateTest extends ImageFieldTestBase {
|
||||
|
||||
/**
|
||||
* Test the validation message is displayed only once for ajax uploads.
|
||||
*/
|
||||
public function testAJAXValidationMessage() {
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$this->createImageField($field_name, 'article', ['cardinality' => -1]);
|
||||
|
||||
$this->drupalGet('node/add/article');
|
||||
/** @var \Drupal\file\FileInterface[] $text_files */
|
||||
$text_files = $this->drupalGetTestFiles('text');
|
||||
$text_file = reset($text_files);
|
||||
|
||||
$field = $this->getSession()->getPage()->findField('files[' . $field_name . '_0][]');
|
||||
$field->attachFile($this->container->get('file_system')->realpath($text_file->uri));
|
||||
$this->assertSession()->waitForElement('css', '.messages--error');
|
||||
|
||||
$elements = $this->xpath('//div[contains(@class, :class)]', [
|
||||
':class' => 'messages--error',
|
||||
]);
|
||||
$this->assertEqual(count($elements), 1, 'Ajax validation messages are displayed once.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that image field validation works with other form submit handlers.
|
||||
*/
|
||||
public function testFriendlyAjaxValidation() {
|
||||
// Add a custom field to the Article content type that contains an AJAX
|
||||
// handler on a select field.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'field_dummy_select',
|
||||
'type' => 'image_module_test_dummy_ajax',
|
||||
'entity_type' => 'node',
|
||||
'cardinality' => 1,
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'article',
|
||||
'field_name' => 'field_dummy_select',
|
||||
'label' => t('Dummy select'),
|
||||
])->save();
|
||||
|
||||
\Drupal::entityTypeManager()
|
||||
->getStorage('entity_form_display')
|
||||
->load('node.article.default')
|
||||
->setComponent(
|
||||
'field_dummy_select',
|
||||
[
|
||||
'type' => 'image_module_test_dummy_ajax_widget',
|
||||
'weight' => 1,
|
||||
])
|
||||
->save();
|
||||
|
||||
// Then, add an image field.
|
||||
$this->createImageField('field_dummy_image', 'article');
|
||||
|
||||
// Open an article and trigger the AJAX handler.
|
||||
$this->drupalGet('node/add/article');
|
||||
$id = $this->getSession()->getPage()->find('css', '[name="form_build_id"]')->getValue();
|
||||
$field = $this->getSession()->getPage()->findField('field_dummy_select[select_widget]');
|
||||
$field->setValue('bam');
|
||||
// Make sure that the operation did not end with an exception.
|
||||
$this->assertSession()->waitForElement('css', "[name='form_build_id']:not([value='$id'])");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\FunctionalJavascript;
|
||||
|
||||
/**
|
||||
* @see \Drupal\image\Plugin\InPlaceEditor\Image
|
||||
* @see \Drupal\Tests\quickedit\FunctionalJavascript\QuickEditJavascriptTestBase
|
||||
*/
|
||||
trait QuickEditImageEditorTestTrait {
|
||||
|
||||
/**
|
||||
* Awaits the 'image' in-place editor.
|
||||
*/
|
||||
protected function awaitImageEditor() {
|
||||
$this->assertJsCondition('document.querySelector(".quickedit-image-field-info") !== null', 10000);
|
||||
|
||||
$quickedit_entity_toolbar = $this->getSession()->getPage()->findById('quickedit-entity-toolbar');
|
||||
$this->assertNotNull($quickedit_entity_toolbar->find('css', 'form.quickedit-image-field-info input[name="alt"]'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates typing in the 'image' in-place editor 'alt' attribute text input.
|
||||
*
|
||||
* @param string $text
|
||||
* The text to type.
|
||||
*/
|
||||
protected function typeInImageEditorAltTextInput($text) {
|
||||
$quickedit_entity_toolbar = $this->getSession()->getPage()->findById('quickedit-entity-toolbar');
|
||||
$input = $quickedit_entity_toolbar->find('css', 'form.quickedit-image-field-info input[name="alt"]');
|
||||
$input->setValue($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates dragging and dropping an image on the 'image' in-place editor.
|
||||
*
|
||||
* @param string $file_uri
|
||||
* The URI of the image file to drag and drop.
|
||||
*/
|
||||
protected function dropImageOnImageEditor($file_uri) {
|
||||
// Our headless browser can't drag+drop files, but we can mock the event.
|
||||
// Append a hidden upload element to the DOM.
|
||||
$script = 'jQuery("<input id=\"quickedit-image-test-input\" type=\"file\" />").appendTo("body")';
|
||||
$this->getSession()->executeScript($script);
|
||||
|
||||
// Find the element, and set its value to our new image.
|
||||
$input = $this->assertSession()->elementExists('css', '#quickedit-image-test-input');
|
||||
$filepath = $this->container->get('file_system')->realpath($file_uri);
|
||||
$input->attachFile($filepath);
|
||||
|
||||
// Trigger the upload logic with a mock "drop" event.
|
||||
$script = 'var e = jQuery.Event("drop");'
|
||||
. 'e.originalEvent = {dataTransfer: {files: jQuery("#quickedit-image-test-input").get(0).files}};'
|
||||
. 'e.preventDefault = e.stopPropagation = function () {};'
|
||||
. 'jQuery(".quickedit-image-dropzone").trigger(e);';
|
||||
$this->getSession()->executeScript($script);
|
||||
|
||||
// Wait for the dropzone element to be removed (i.e. loading is done).
|
||||
$js_condition = <<<JS
|
||||
function () {
|
||||
var activeFieldID = Drupal.quickedit.collections.entities
|
||||
.findWhere({state:'opened'})
|
||||
.get('fields')
|
||||
.filter(function (fieldModel) {
|
||||
var state = fieldModel.get('state');
|
||||
return state === 'active' || state === 'changed';
|
||||
})[0]
|
||||
.get('fieldID')
|
||||
return document.querySelector('[data-quickedit-field-id="' + activeFieldID + '"] .quickedit-image-dropzone') === null;
|
||||
}();
|
||||
JS;
|
||||
|
||||
$this->assertJsCondition($js_condition, 20000);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,24 +3,24 @@
|
||||
namespace Drupal\Tests\image\FunctionalJavascript;
|
||||
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
|
||||
use Drupal\Tests\image\Kernel\ImageFieldCreationTrait;
|
||||
use Drupal\Tests\quickedit\FunctionalJavascript\QuickEditJavascriptTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the JavaScript functionality of the "image" in-place editor.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\image\Plugin\InPlaceEditor\Image
|
||||
* @group image
|
||||
*/
|
||||
class QuickEditImageTest extends JavascriptTestBase {
|
||||
class QuickEditImageTest extends QuickEditJavascriptTestBase {
|
||||
|
||||
use ImageFieldCreationTrait;
|
||||
use TestFileCreationTrait;
|
||||
use QuickEditImageEditorTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['node', 'image', 'field_ui', 'contextual', 'quickedit', 'toolbar'];
|
||||
public static $modules = ['node', 'image', 'field_ui'];
|
||||
|
||||
/**
|
||||
* A user with permissions to edit Articles and use Quick Edit.
|
||||
@@ -52,9 +52,12 @@ class QuickEditImageTest extends JavascriptTestBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if an image can be uploaded inline with Quick Edit.
|
||||
* Test that quick editor works correctly with images.
|
||||
*
|
||||
* @covers ::isCompatible
|
||||
* @covers ::getAttachments
|
||||
*/
|
||||
public function testUpload() {
|
||||
public function testImageInPlaceEditor() {
|
||||
// Create a field with a basic filetype restriction.
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$field_settings = [
|
||||
@@ -114,52 +117,82 @@ class QuickEditImageTest extends JavascriptTestBase {
|
||||
// Assert that the initial image is present.
|
||||
$this->assertSession()->elementExists('css', $entity_selector . ' ' . $field_selector . ' ' . $original_image_selector);
|
||||
|
||||
// Wait until Quick Edit loads.
|
||||
$condition = "jQuery('" . $entity_selector . " .quickedit').length > 0";
|
||||
$this->assertJsCondition($condition, 10000);
|
||||
// Initial state.
|
||||
$this->awaitQuickEditForEntity('node', 1);
|
||||
$this->assertEntityInstanceStates([
|
||||
'node/1[0]' => 'closed',
|
||||
]);
|
||||
$this->assertEntityInstanceFieldStates('node', 1, 0, [
|
||||
'node/1/title/en/full' => 'inactive',
|
||||
'node/1/uid/en/full' => 'inactive',
|
||||
'node/1/created/en/full' => 'inactive',
|
||||
'node/1/body/en/full' => 'inactive',
|
||||
'node/1/' . $field_name . '/en/full' => 'inactive',
|
||||
]);
|
||||
|
||||
// Initiate Quick Editing.
|
||||
$this->click('.contextual-toolbar-tab button');
|
||||
$this->click($entity_selector . ' [data-contextual-id] > button');
|
||||
$this->click($entity_selector . ' [data-contextual-id] .quickedit > a');
|
||||
// Start in-place editing of the article node.
|
||||
$this->startQuickEditViaToolbar('node', 1, 0);
|
||||
$this->assertEntityInstanceStates([
|
||||
'node/1[0]' => 'opened',
|
||||
]);
|
||||
$this->assertQuickEditEntityToolbar((string) $node->label(), NULL);
|
||||
$this->assertEntityInstanceFieldStates('node', 1, 0, [
|
||||
'node/1/title/en/full' => 'candidate',
|
||||
'node/1/uid/en/full' => 'candidate',
|
||||
'node/1/created/en/full' => 'candidate',
|
||||
'node/1/body/en/full' => 'candidate',
|
||||
'node/1/' . $field_name . '/en/full' => 'candidate',
|
||||
]);
|
||||
|
||||
// Click the image field.
|
||||
$this->click($field_selector);
|
||||
|
||||
// Wait for the field info to load and set new alt text.
|
||||
$condition = "jQuery('.quickedit-image-field-info').length > 0";
|
||||
$this->assertJsCondition($condition, 10000);
|
||||
$input = $this->assertSession()->elementExists('css', '.quickedit-image-field-info input[name="alt"]');
|
||||
$input->setValue('New text');
|
||||
|
||||
// Check that our Dropzone element exists.
|
||||
$this->awaitImageEditor();
|
||||
$this->assertSession()->elementExists('css', $field_selector . ' .quickedit-image-dropzone');
|
||||
$this->assertEntityInstanceFieldStates('node', 1, 0, [
|
||||
'node/1/title/en/full' => 'candidate',
|
||||
'node/1/uid/en/full' => 'candidate',
|
||||
'node/1/created/en/full' => 'candidate',
|
||||
'node/1/body/en/full' => 'candidate',
|
||||
'node/1/' . $field_name . '/en/full' => 'active',
|
||||
]);
|
||||
|
||||
// Our headless browser can't drag+drop files, but we can mock the event.
|
||||
// Append a hidden upload element to the DOM.
|
||||
$script = 'jQuery("<input id=\"quickedit-image-test-input\" type=\"file\" />").appendTo("body")';
|
||||
$this->getSession()->executeScript($script);
|
||||
// Type new 'alt' text.
|
||||
$this->typeInImageEditorAltTextInput('New text');
|
||||
$this->assertEntityInstanceFieldStates('node', 1, 0, [
|
||||
'node/1/title/en/full' => 'candidate',
|
||||
'node/1/uid/en/full' => 'candidate',
|
||||
'node/1/created/en/full' => 'candidate',
|
||||
'node/1/body/en/full' => 'candidate',
|
||||
'node/1/' . $field_name . '/en/full' => 'changed',
|
||||
]);
|
||||
|
||||
// Find the element, and set its value to our new image.
|
||||
$input = $this->assertSession()->elementExists('css', '#quickedit-image-test-input');
|
||||
$filepath = $this->container->get('file_system')->realpath($valid_images[1]->uri);
|
||||
$input->attachFile($filepath);
|
||||
|
||||
// Trigger the upload logic with a mock "drop" event.
|
||||
$script = 'var e = jQuery.Event("drop");'
|
||||
. 'e.originalEvent = {dataTransfer: {files: jQuery("#quickedit-image-test-input").get(0).files}};'
|
||||
. 'e.preventDefault = e.stopPropagation = function () {};'
|
||||
. 'jQuery(".quickedit-image-dropzone").trigger(e);';
|
||||
$this->getSession()->executeScript($script);
|
||||
|
||||
// Wait for the dropzone element to be removed (i.e. loading is done).
|
||||
$condition = "jQuery('" . $field_selector . " .quickedit-image-dropzone').length == 0";
|
||||
$this->assertJsCondition($condition, 20000);
|
||||
// Drag and drop an image.
|
||||
$this->dropImageOnImageEditor($valid_images[1]->uri);
|
||||
|
||||
// To prevent 403s on save, we re-set our request (cookie) state.
|
||||
$this->prepareRequest();
|
||||
|
||||
// Save the change.
|
||||
$this->click('.quickedit-button.action-save');
|
||||
$this->assertSession()->assertWaitOnAjaxRequest();
|
||||
// Click 'Save'.
|
||||
$this->saveQuickEdit();
|
||||
$this->assertEntityInstanceStates([
|
||||
'node/1[0]' => 'committing',
|
||||
]);
|
||||
$this->assertEntityInstanceFieldStates('node', 1, 0, [
|
||||
'node/1/title/en/full' => 'candidate',
|
||||
'node/1/uid/en/full' => 'candidate',
|
||||
'node/1/created/en/full' => 'candidate',
|
||||
'node/1/body/en/full' => 'candidate',
|
||||
'node/1/' . $field_name . '/en/full' => 'saving',
|
||||
]);
|
||||
$this->assertEntityInstanceFieldMarkup('node', 1, 0, [
|
||||
'node/1/' . $field_name . '/en/full' => '.quickedit-changed',
|
||||
]);
|
||||
|
||||
// Wait for the saving of the image field to complete.
|
||||
$this->assertJsCondition("Drupal.quickedit.collections.entities.get('node/1[0]').get('state') === 'closed'");
|
||||
$this->assertEntityInstanceStates([
|
||||
'node/1[0]' => 'closed',
|
||||
]);
|
||||
|
||||
// Re-visit the page to make sure the edit worked.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Drupal\Tests\image\Kernel;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
@@ -58,7 +57,7 @@ class ImageFormatterTest extends FieldKernelTestBase {
|
||||
|
||||
$this->entityType = 'entity_test';
|
||||
$this->bundle = $this->entityType;
|
||||
$this->fieldName = Unicode::strtolower($this->randomMachineName());
|
||||
$this->fieldName = mb_strtolower($this->randomMachineName());
|
||||
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => $this->entityType,
|
||||
|
||||
@@ -22,7 +22,7 @@ class ImageImportTest extends KernelTestBase {
|
||||
*/
|
||||
public function testImport() {
|
||||
$style = ImageStyle::create([
|
||||
'name' => 'test'
|
||||
'name' => 'test',
|
||||
]);
|
||||
|
||||
$style->addImageEffect(['id' => 'image_module_test_null']);
|
||||
|
||||
@@ -67,7 +67,7 @@ class ImageItemTest extends FieldKernelTestBase {
|
||||
'file_extensions' => 'jpg',
|
||||
],
|
||||
])->save();
|
||||
file_unmanaged_copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://example.jpg');
|
||||
file_unmanaged_copy($this->root . '/core/misc/druplicon.png', 'public://example.jpg');
|
||||
$this->image = File::create([
|
||||
'uri' => 'public://example.jpg',
|
||||
]);
|
||||
@@ -100,7 +100,7 @@ class ImageItemTest extends FieldKernelTestBase {
|
||||
$this->assertEqual($entity->image_test->entity->uuid(), $this->image->uuid());
|
||||
|
||||
// Make sure the computed entity reflects updates to the referenced file.
|
||||
file_unmanaged_copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://example-2.jpg');
|
||||
file_unmanaged_copy($this->root . '/core/misc/druplicon.png', 'public://example-2.jpg');
|
||||
$image2 = File::create([
|
||||
'uri' => 'public://example-2.jpg',
|
||||
]);
|
||||
|
||||
+22
-11
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
namespace Drupal\Tests\image\Kernel;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Url;
|
||||
@@ -8,22 +8,28 @@ use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests image theme functions.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageThemeFunctionTest extends WebTestBase {
|
||||
class ImageThemeFunctionTest extends KernelTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['image', 'entity_test'];
|
||||
public static $modules = ['entity_test', 'field', 'file', 'image', 'system', 'simpletest', 'user'];
|
||||
|
||||
/**
|
||||
* Created file entity.
|
||||
@@ -40,6 +46,11 @@ class ImageThemeFunctionTest extends WebTestBase {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test');
|
||||
$this->installEntitySchema('file');
|
||||
$this->installSchema('file', ['file_usage']);
|
||||
$this->installEntitySchema('user');
|
||||
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => 'entity_test',
|
||||
'field_name' => 'image_test',
|
||||
@@ -51,7 +62,7 @@ class ImageThemeFunctionTest extends WebTestBase {
|
||||
'field_name' => 'image_test',
|
||||
'bundle' => 'entity_test',
|
||||
])->save();
|
||||
file_unmanaged_copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://example.jpg');
|
||||
file_unmanaged_copy($this->root . '/core/misc/druplicon.png', 'public://example.jpg');
|
||||
$this->image = File::create([
|
||||
'uri' => 'public://example.jpg',
|
||||
]);
|
||||
@@ -96,7 +107,7 @@ class ImageThemeFunctionTest extends WebTestBase {
|
||||
// Test using theme_image_formatter() with a NULL value for the alt option.
|
||||
$element = $base_element;
|
||||
$this->setRawContent($renderer->renderRoot($element));
|
||||
$elements = $this->xpath('//a[@href=:path]/img[@class="image-style-test" and @src=:url and @width=:width and @height=:height]', [':path' => base_path() . $path, ':url' => $url, ':width' => $image->getWidth(), ':height' => $image->getHeight()]);
|
||||
$elements = $this->xpath('//a[@href=:path]/img[@src=:url and @width=:width and @height=:height]', [':path' => base_path() . $path, ':url' => $url, ':width' => $image->getWidth(), ':height' => $image->getHeight()]);
|
||||
$this->assertEqual(count($elements), 1, 'theme_image_formatter() correctly renders with a NULL value for the alt option.');
|
||||
|
||||
// Test using theme_image_formatter() without an image title, alt text, or
|
||||
@@ -104,7 +115,7 @@ class ImageThemeFunctionTest extends WebTestBase {
|
||||
$element = $base_element;
|
||||
$element['#item']->alt = '';
|
||||
$this->setRawContent($renderer->renderRoot($element));
|
||||
$elements = $this->xpath('//a[@href=:path]/img[@class="image-style-test" and @src=:url and @width=:width and @height=:height and @alt=""]', [':path' => base_path() . $path, ':url' => $url, ':width' => $image->getWidth(), ':height' => $image->getHeight()]);
|
||||
$elements = $this->xpath('//a[@href=:path]/img[@src=:url and @width=:width and @height=:height and @alt=""]', [':path' => base_path() . $path, ':url' => $url, ':width' => $image->getWidth(), ':height' => $image->getHeight()]);
|
||||
$this->assertEqual(count($elements), 1, 'theme_image_formatter() correctly renders without title, alt, or path options.');
|
||||
|
||||
// Link the image to a fragment on the page, and not a full URL.
|
||||
@@ -112,11 +123,11 @@ class ImageThemeFunctionTest extends WebTestBase {
|
||||
$element = $base_element;
|
||||
$element['#url'] = Url::fromRoute('<none>', [], ['fragment' => $fragment]);
|
||||
$this->setRawContent($renderer->renderRoot($element));
|
||||
$elements = $this->xpath('//a[@href=:fragment]/img[@class="image-style-test" and @src=:url and @width=:width and @height=:height and @alt=""]', [
|
||||
$elements = $this->xpath('//a[@href=:fragment]/img[@src=:url and @width=:width and @height=:height and @alt=""]', [
|
||||
':fragment' => '#' . $fragment,
|
||||
':url' => $url,
|
||||
':width' => $image->getWidth(),
|
||||
':height' => $image->getHeight()
|
||||
':height' => $image->getHeight(),
|
||||
]);
|
||||
$this->assertEqual(count($elements), 1, 'theme_image_formatter() correctly renders a link fragment.');
|
||||
}
|
||||
@@ -147,14 +158,14 @@ class ImageThemeFunctionTest extends WebTestBase {
|
||||
|
||||
$element = $base_element;
|
||||
$this->setRawContent($renderer->renderRoot($element));
|
||||
$elements = $this->xpath('//img[@class="image-style-image-test" and @src=:url and @alt=""]', [':url' => $url]);
|
||||
$elements = $this->xpath('//img[@src=:url and @alt=""]', [':url' => $url]);
|
||||
$this->assertEqual(count($elements), 1, 'theme_image_style() renders an image correctly.');
|
||||
|
||||
// Test using theme_image_style() with a NULL value for the alt option.
|
||||
$element = $base_element;
|
||||
$element['#alt'] = NULL;
|
||||
$this->setRawContent($renderer->renderRoot($element));
|
||||
$elements = $this->xpath('//img[@class="image-style-image-test" and @src=:url]', [':url' => $url]);
|
||||
$elements = $this->xpath('//img[@src=:url]', [':url' => $url]);
|
||||
$this->assertEqual(count($elements), 1, 'theme_image_style() renders an image correctly with a NULL value for the alt option.');
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ class MigrateImageStylesTest extends MigrateDrupal7TestBase {
|
||||
* Test the image styles migration.
|
||||
*/
|
||||
public function testImageStylesMigration() {
|
||||
$this->assertEntity('custom_image_style_1', "Custom image style 1", ['image_scale_and_crop', 'image_desaturate'], [['width' => 55, 'height' => 55], []]);
|
||||
$this->assertEntity('custom_image_style_1', "Custom image style 1", ['image_scale_and_crop', 'image_desaturate'], [['width' => 55, 'height' => 55, 'anchor' => 'center-center'], []]);
|
||||
$this->assertEntity('custom_image_style_2', "Custom image style 2", ['image_resize', 'image_rotate'], [['width' => 55, 'height' => 100], ['degrees' => 45, 'bgcolor' => '#FFFFFF', 'random' => FALSE]]);
|
||||
$this->assertEntity('custom_image_style_3', "Custom image style 3", ['image_scale', 'image_crop'], [['width' => 150, 'height' => NULL, 'upscale' => FALSE], ['width' => 50, 'height' => 50, 'anchor' => 'left-top']]);
|
||||
}
|
||||
|
||||
+12
-5
@@ -1,10 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\image\Tests\Views;
|
||||
namespace Drupal\Tests\image\Kernel\Views;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\views\Views;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
@@ -14,14 +15,14 @@ use Drupal\field\Entity\FieldStorageConfig;
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class RelationshipUserImageDataTest extends ViewTestBase {
|
||||
class RelationshipUserImageDataTest extends ViewsKernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['image', 'image_test_views', 'user'];
|
||||
public static $modules = ['file', 'field', 'image', 'image_test_views', 'system', 'user'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
@@ -33,6 +34,10 @@ class RelationshipUserImageDataTest extends ViewTestBase {
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
$this->installEntitySchema('file');
|
||||
$this->installSchema('file', ['file_usage']);
|
||||
$this->installEntitySchema('user');
|
||||
|
||||
// Create the user profile field and instance.
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => 'user',
|
||||
@@ -70,7 +75,9 @@ class RelationshipUserImageDataTest extends ViewTestBase {
|
||||
file_put_contents($file->getFileUri(), file_get_contents('core/modules/simpletest/files/image-1.png'));
|
||||
$file->save();
|
||||
|
||||
$account = $this->drupalCreateUser();
|
||||
$account = User::create([
|
||||
'name' => 'foo',
|
||||
]);
|
||||
$account->user_picture->target_id = 2;
|
||||
$account->save();
|
||||
|
||||
@@ -10,6 +10,7 @@ use Prophecy\Argument;
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\image\Plugin\migrate\field\d7\ImageField
|
||||
* @group image
|
||||
* @group legacy
|
||||
*/
|
||||
class ImageFieldTest extends UnitTestCase {
|
||||
|
||||
@@ -44,6 +45,7 @@ class ImageFieldTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::processFieldValues
|
||||
* @expectedDeprecation Deprecated in Drupal 8.6.0, to be removed before Drupal 9.0.0. Use defineValueProcessPipeline() instead. See https://www.drupal.org/node/2944598.
|
||||
*/
|
||||
public function testProcessFieldValues() {
|
||||
$this->plugin->processFieldValues($this->migration, 'somefieldname', []);
|
||||
|
||||
Reference in New Issue
Block a user