Update Composer, update everything
This commit is contained in:
parent
ea3e94409f
commit
dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions
|
@ -14,8 +14,7 @@ field_default_image:
|
|||
type: mapping
|
||||
mapping:
|
||||
uuid:
|
||||
type: string
|
||||
label: 'Image'
|
||||
type: uuid
|
||||
alt:
|
||||
type: label
|
||||
label: 'Alternative text'
|
||||
|
|
|
@ -21,7 +21,7 @@ image.style.*:
|
|||
weight:
|
||||
type: integer
|
||||
uuid:
|
||||
type: string
|
||||
type: uuid
|
||||
|
||||
image.effect.*:
|
||||
type: mapping
|
||||
|
@ -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,12 +5,12 @@
|
|||
|
||||
.quickedit-image-dropzone {
|
||||
background: rgba(116, 183, 255, 0.8);
|
||||
transition: background .2s;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.quickedit-image-icon {
|
||||
margin: 0 0 10px 0;
|
||||
transition: margin .5s;
|
||||
transition: margin 0.5s;
|
||||
}
|
||||
|
||||
.quickedit-image-dropzone.hover {
|
||||
|
@ -51,9 +51,9 @@
|
|||
}
|
||||
|
||||
@keyframes quickedit-image-spin {
|
||||
0% {transform: rotate(0deg);}
|
||||
50% {transform: rotate(180deg);}
|
||||
100% {transform: rotate(360deg);}
|
||||
0% { transform: rotate(0deg); }
|
||||
50% { transform: rotate(180deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.quickedit-image-text {
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\file\FileInterface;
|
||||
use Drupal\field\FieldStorageConfigInterface;
|
||||
use Drupal\field\FieldConfigInterface;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
|
@ -16,6 +16,8 @@ use Drupal\image\Entity\ImageStyle;
|
|||
* Image style constant for user presets in the database.
|
||||
*
|
||||
* @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.0.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1820974
|
||||
*/
|
||||
const IMAGE_STORAGE_NORMAL = 1;
|
||||
|
||||
|
@ -23,6 +25,8 @@ const IMAGE_STORAGE_NORMAL = 1;
|
|||
* Image style constant for user presets that override module-defined presets.
|
||||
*
|
||||
* @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.0.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1820974
|
||||
*/
|
||||
const IMAGE_STORAGE_OVERRIDE = 2;
|
||||
|
||||
|
@ -30,6 +34,8 @@ const IMAGE_STORAGE_OVERRIDE = 2;
|
|||
* Image style constant for module-defined presets in code.
|
||||
*
|
||||
* @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.0.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1820974
|
||||
*/
|
||||
const IMAGE_STORAGE_DEFAULT = 4;
|
||||
|
||||
|
@ -37,6 +43,8 @@ const IMAGE_STORAGE_DEFAULT = 4;
|
|||
* Image style constant to represent an editable preset.
|
||||
*
|
||||
* @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.0.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1820974
|
||||
*/
|
||||
define('IMAGE_STORAGE_EDITABLE', IMAGE_STORAGE_NORMAL | IMAGE_STORAGE_OVERRIDE);
|
||||
|
||||
|
@ -44,6 +52,8 @@ define('IMAGE_STORAGE_EDITABLE', IMAGE_STORAGE_NORMAL | IMAGE_STORAGE_OVERRIDE);
|
|||
* Image style constant to represent any module-based preset.
|
||||
*
|
||||
* @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.0.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1820974
|
||||
*/
|
||||
define('IMAGE_STORAGE_MODULE', IMAGE_STORAGE_OVERRIDE | IMAGE_STORAGE_DEFAULT);
|
||||
|
||||
|
@ -142,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' => []],
|
||||
],
|
||||
|
@ -200,7 +213,7 @@ function image_file_download($uri) {
|
|||
/**
|
||||
* Implements hook_file_move().
|
||||
*/
|
||||
function image_file_move(File $file, File $source) {
|
||||
function image_file_move(FileInterface $file, FileInterface $source) {
|
||||
// Delete any image derivatives at the original image path.
|
||||
image_path_flush($source->getFileUri());
|
||||
}
|
||||
|
@ -208,7 +221,7 @@ function image_file_move(File $file, File $source) {
|
|||
/**
|
||||
* Implements hook_ENTITY_TYPE_predelete() for file entities.
|
||||
*/
|
||||
function image_file_predelete(File $file) {
|
||||
function image_file_predelete(FileInterface $file) {
|
||||
// Delete any image derivatives of this image.
|
||||
image_path_flush($file->getFileUri());
|
||||
}
|
||||
|
@ -292,10 +305,28 @@ function template_preprocess_image_style(&$variables) {
|
|||
'#width' => $dimensions['width'],
|
||||
'#height' => $dimensions['height'],
|
||||
'#attributes' => $variables['attributes'],
|
||||
'#uri' => $style->buildUrl($variables['uri']),
|
||||
'#style_name' => $variables['style_name'],
|
||||
];
|
||||
|
||||
// If the current image toolkit supports this file type, prepare the URI for
|
||||
// the derivative image. If not, just use the original image resized to the
|
||||
// dimensions specified by the style.
|
||||
if ($style->supportsUri($variables['uri'])) {
|
||||
$variables['image']['#uri'] = $style->buildUrl($variables['uri']);
|
||||
}
|
||||
else {
|
||||
$variables['image']['#uri'] = $variables['uri'];
|
||||
// Don't render the image by default, but allow other preprocess functions
|
||||
// to override that if they need to.
|
||||
$variables['image']['#access'] = FALSE;
|
||||
|
||||
// Inform the site builders why their image didn't work.
|
||||
\Drupal::logger('image')->warning('Could not apply @style image style to @uri because the style does not support it.', [
|
||||
'@style' => $style->label(),
|
||||
'@uri' => $variables['uri'],
|
||||
]);
|
||||
}
|
||||
|
||||
if (isset($variables['alt']) || array_key_exists('alt', $variables)) {
|
||||
$variables['image']['#alt'] = $variables['alt'];
|
||||
}
|
||||
|
@ -404,7 +435,7 @@ function image_field_storage_config_update(FieldStorageConfigInterface $field_st
|
|||
if ($file_new && (file_uri_scheme($file_new->getFileUri()) != $field_storage->getSetting('uri_scheme'))) {
|
||||
$directory = $field_storage->getSetting('uri_scheme') . '://default_images/';
|
||||
file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
|
||||
file_move($file_new, $directory . $file_new->filename);
|
||||
file_move($file_new, $directory . $file_new->getFilename());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -442,7 +473,7 @@ function image_field_config_update(FieldConfigInterface $field) {
|
|||
if ($file_new && (file_uri_scheme($file_new->getFileUri()) != $field_storage->getSetting('uri_scheme'))) {
|
||||
$directory = $field_storage->getSetting('uri_scheme') . '://default_images/';
|
||||
file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
|
||||
file_move($file_new, $directory . $file_new->filename);
|
||||
file_move($file_new, $directory . $file_new->getFilename());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
376
web/core/modules/image/js/editors/image.es6.js
Normal file
376
web/core/modules/image/js/editors/image.es6.js
Normal file
|
@ -0,0 +1,376 @@
|
|||
/**
|
||||
* @file
|
||||
* Drag+drop based in-place editor for images.
|
||||
*/
|
||||
|
||||
(function($, _, Drupal) {
|
||||
Drupal.quickedit.editors.image = Drupal.quickedit.EditorView.extend(
|
||||
/** @lends Drupal.quickedit.editors.image# */ {
|
||||
/**
|
||||
* @constructs
|
||||
*
|
||||
* @augments Drupal.quickedit.EditorView
|
||||
*
|
||||
* @param {object} options
|
||||
* Options for the image editor.
|
||||
*/
|
||||
initialize(options) {
|
||||
Drupal.quickedit.EditorView.prototype.initialize.call(this, options);
|
||||
// Set our original value to our current HTML (for reverting).
|
||||
this.model.set('originalValue', this.$el.html().trim());
|
||||
// $.val() callback function for copying input from our custom form to
|
||||
// the Quick Edit Field Form.
|
||||
this.model.set('currentValue', function(index, value) {
|
||||
const matches = $(this)
|
||||
.attr('name')
|
||||
.match(/(alt|title)]$/);
|
||||
if (matches) {
|
||||
const name = matches[1];
|
||||
const $toolgroup = $(
|
||||
`#${options.fieldModel.toolbarView.getMainWysiwygToolgroupId()}`,
|
||||
);
|
||||
const $input = $toolgroup.find(
|
||||
`.quickedit-image-field-info input[name="${name}"]`,
|
||||
);
|
||||
if ($input.length) {
|
||||
return $input.val();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {Drupal.quickedit.FieldModel} fieldModel
|
||||
* The field model that holds the state.
|
||||
* @param {string} state
|
||||
* The state to change to.
|
||||
* @param {object} options
|
||||
* State options, if needed by the state change.
|
||||
*/
|
||||
stateChange(fieldModel, state, options) {
|
||||
const from = fieldModel.previous('state');
|
||||
switch (state) {
|
||||
case 'inactive':
|
||||
break;
|
||||
|
||||
case 'candidate':
|
||||
if (from !== 'inactive') {
|
||||
this.$el.find('.quickedit-image-dropzone').remove();
|
||||
this.$el.removeClass('quickedit-image-element');
|
||||
}
|
||||
if (from === 'invalid') {
|
||||
this.removeValidationErrors();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'highlighted':
|
||||
break;
|
||||
|
||||
case 'activating':
|
||||
// Defer updating the field model until the current state change has
|
||||
// propagated, to not trigger a nested state change event.
|
||||
_.defer(() => {
|
||||
fieldModel.set('state', 'active');
|
||||
});
|
||||
break;
|
||||
|
||||
case 'active': {
|
||||
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);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Utility function to make an AJAX request to the server.
|
||||
*
|
||||
* In addition to formatting the correct request, this also handles error
|
||||
* codes and messages by displaying them visually inline with the image.
|
||||
*
|
||||
* Drupal.ajax is not called here as the Form API is unused by this
|
||||
* in-place editor, and our JSON requests/responses try to be
|
||||
* editor-agnostic. Ideally similar logic and routes could be used by
|
||||
* modules like CKEditor for drag+drop file uploads as well.
|
||||
*
|
||||
* @param {object} options
|
||||
* Ajax options.
|
||||
* @param {string} options.type
|
||||
* The type of request (i.e. GET, POST, PUT, DELETE, etc.)
|
||||
* @param {string} options.url
|
||||
* The URL for the request.
|
||||
* @param {*} options.data
|
||||
* The data to send to the server.
|
||||
* @param {function} options.success
|
||||
* A callback function used when a request is successful, without errors.
|
||||
*/
|
||||
ajax(options) {
|
||||
const defaultOptions = {
|
||||
context: this,
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
error() {
|
||||
this.renderDropzone(
|
||||
'error',
|
||||
Drupal.t('A server error has occurred.'),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
const ajaxOptions = $.extend(defaultOptions, options);
|
||||
const successCallback = ajaxOptions.success;
|
||||
|
||||
// Handle the success callback.
|
||||
ajaxOptions.success = function(response) {
|
||||
if (response.main_error) {
|
||||
this.renderDropzone('error', response.main_error);
|
||||
if (response.errors.length) {
|
||||
this.model.set('validationErrors', response.errors);
|
||||
}
|
||||
this.showValidationErrors();
|
||||
} else {
|
||||
successCallback(response);
|
||||
}
|
||||
};
|
||||
|
||||
$.ajax(ajaxOptions);
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders our toolbar form for editing metadata.
|
||||
*
|
||||
* @param {Drupal.quickedit.FieldModel} fieldModel
|
||||
* The current Field Model.
|
||||
*/
|
||||
renderToolbar(fieldModel) {
|
||||
const $toolgroup = $(
|
||||
`#${fieldModel.toolbarView.getMainWysiwygToolgroupId()}`,
|
||||
);
|
||||
let $toolbar = $toolgroup.find('.quickedit-image-field-info');
|
||||
if ($toolbar.length === 0) {
|
||||
// Perform an AJAX request for extra image info (alt/title).
|
||||
const fieldID = fieldModel.get('fieldID');
|
||||
const url = Drupal.quickedit.util.buildUrl(
|
||||
fieldID,
|
||||
Drupal.url(
|
||||
'quickedit/image/info/!entity_type/!id/!field_name/!langcode/!view_mode',
|
||||
),
|
||||
);
|
||||
const self = this;
|
||||
self.ajax({
|
||||
type: 'GET',
|
||||
url,
|
||||
success(response) {
|
||||
$toolbar = $(Drupal.theme.quickeditImageToolbar(response));
|
||||
$toolgroup.append($toolbar);
|
||||
$toolbar.on('keyup paste', () => {
|
||||
fieldModel.set('state', 'changed');
|
||||
});
|
||||
// Re-position the toolbar, which could have changed size.
|
||||
fieldModel.get('entity').toolbarView.position();
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders our dropzone element.
|
||||
*
|
||||
* @param {string} state
|
||||
* The current state of our editor. Only used for visual styling.
|
||||
* @param {string} text
|
||||
* The text to display in the dropzone area.
|
||||
*
|
||||
* @return {jQuery}
|
||||
* The rendered dropzone.
|
||||
*/
|
||||
renderDropzone(state, text) {
|
||||
let $dropzone = this.$el.find('.quickedit-image-dropzone');
|
||||
// If the element already exists, modify its contents.
|
||||
if ($dropzone.length) {
|
||||
$dropzone
|
||||
.removeClass('upload error hover loading')
|
||||
.addClass(`.quickedit-image-dropzone ${state}`)
|
||||
.children('.quickedit-image-text')
|
||||
.html(text);
|
||||
} else {
|
||||
$dropzone = $(
|
||||
Drupal.theme('quickeditImageDropzone', {
|
||||
state,
|
||||
text,
|
||||
}),
|
||||
);
|
||||
this.$el.append($dropzone);
|
||||
}
|
||||
|
||||
return $dropzone;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
revert() {
|
||||
this.$el.html(this.model.get('originalValue'));
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getQuickEditUISettings() {
|
||||
return {
|
||||
padding: false,
|
||||
unifiedToolbar: true,
|
||||
fullWidthToolbar: true,
|
||||
popup: false,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
showValidationErrors() {
|
||||
const errors = Drupal.theme('quickeditImageErrors', {
|
||||
errors: this.model.get('validationErrors'),
|
||||
});
|
||||
$(`#${this.fieldModel.toolbarView.getMainWysiwygToolgroupId()}`).append(
|
||||
errors,
|
||||
);
|
||||
this.getEditedElement().addClass('quickedit-validation-error');
|
||||
// Re-position the toolbar, which could have changed size.
|
||||
this.fieldModel.get('entity').toolbarView.position();
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
removeValidationErrors() {
|
||||
$(`#${this.fieldModel.toolbarView.getMainWysiwygToolgroupId()}`)
|
||||
.find('.quickedit-image-errors')
|
||||
.remove();
|
||||
this.getEditedElement().removeClass('quickedit-validation-error');
|
||||
},
|
||||
},
|
||||
);
|
||||
})(jQuery, _, Drupal);
|
|
@ -1,28 +1,17 @@
|
|||
/**
|
||||
* @file
|
||||
* Drag+drop based in-place editor for images.
|
||||
*/
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function ($, _, Drupal) {
|
||||
|
||||
'use strict';
|
||||
|
||||
Drupal.quickedit.editors.image = Drupal.quickedit.EditorView.extend(/** @lends Drupal.quickedit.editors.image# */{
|
||||
|
||||
/**
|
||||
* @constructs
|
||||
*
|
||||
* @augments Drupal.quickedit.EditorView
|
||||
*
|
||||
* @param {object} options
|
||||
* Options for the image editor.
|
||||
*/
|
||||
initialize: function (options) {
|
||||
Drupal.quickedit.editors.image = Drupal.quickedit.EditorView.extend({
|
||||
initialize: function initialize(options) {
|
||||
Drupal.quickedit.EditorView.prototype.initialize.call(this, options);
|
||||
// Set our original value to our current HTML (for reverting).
|
||||
|
||||
this.model.set('originalValue', this.$el.html().trim());
|
||||
// $.val() callback function for copying input from our custom form to
|
||||
// the Quick Edit Field Form.
|
||||
|
||||
this.model.set('currentValue', function (index, value) {
|
||||
var matches = $(this).attr('name').match(/(alt|title)]$/);
|
||||
if (matches) {
|
||||
|
@ -35,18 +24,7 @@
|
|||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {Drupal.quickedit.FieldModel} fieldModel
|
||||
* The field model that holds the state.
|
||||
* @param {string} state
|
||||
* The state to change to.
|
||||
* @param {object} options
|
||||
* State options, if needed by the state change.
|
||||
*/
|
||||
stateChange: function (fieldModel, state, options) {
|
||||
stateChange: function stateChange(fieldModel, state, options) {
|
||||
var from = fieldModel.previous('state');
|
||||
switch (state) {
|
||||
case 'inactive':
|
||||
|
@ -66,60 +44,49 @@
|
|||
break;
|
||||
|
||||
case 'activating':
|
||||
// Defer updating the field model until the current state change has
|
||||
// propagated, to not trigger a nested state change event.
|
||||
_.defer(function () {
|
||||
fieldModel.set('state', 'active');
|
||||
});
|
||||
break;
|
||||
|
||||
case 'active':
|
||||
var self = this;
|
||||
{
|
||||
var self = this;
|
||||
|
||||
// Indicate that this element is being edited by Quick Edit Image.
|
||||
this.$el.addClass('quickedit-image-element');
|
||||
this.$el.addClass('quickedit-image-element');
|
||||
|
||||
// Render our initial dropzone element. Once the user reverts changes
|
||||
// or saves a new image, this element is removed.
|
||||
var $dropzone = this.renderDropzone('upload', Drupal.t('Drop file here or click to upload'));
|
||||
var $dropzone = this.renderDropzone('upload', Drupal.t('Drop file here or click to upload'));
|
||||
|
||||
$dropzone.on('dragenter', function (e) {
|
||||
$(this).addClass('hover');
|
||||
});
|
||||
$dropzone.on('dragleave', function (e) {
|
||||
$(this).removeClass('hover');
|
||||
});
|
||||
|
||||
$dropzone.on('drop', function (e) {
|
||||
// Only respond when a file is dropped (could be another element).
|
||||
if (e.originalEvent.dataTransfer && e.originalEvent.dataTransfer.files.length) {
|
||||
$dropzone.on('dragenter', function (e) {
|
||||
$(this).addClass('hover');
|
||||
});
|
||||
$dropzone.on('dragleave', function (e) {
|
||||
$(this).removeClass('hover');
|
||||
self.uploadImage(e.originalEvent.dataTransfer.files[0]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$dropzone.on('click', function (e) {
|
||||
// Create an <input> element without appending it to the DOM, and
|
||||
// trigger a click event. This is the easiest way to arbitrarily
|
||||
// open the browser's upload dialog.
|
||||
$('<input type="file">')
|
||||
.trigger('click')
|
||||
.on('change', function () {
|
||||
$dropzone.on('drop', function (e) {
|
||||
if (e.originalEvent.dataTransfer && e.originalEvent.dataTransfer.files.length) {
|
||||
$(this).removeClass('hover');
|
||||
self.uploadImage(e.originalEvent.dataTransfer.files[0]);
|
||||
}
|
||||
});
|
||||
|
||||
$dropzone.on('click', function (e) {
|
||||
$('<input type="file">').trigger('click').on('change', function () {
|
||||
if (this.files.length) {
|
||||
self.uploadImage(this.files[0]);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Prevent the browser's default behavior when dragging files onto
|
||||
// the document (usually opens them in the same tab).
|
||||
$dropzone.on('dragover dragenter dragleave drop click', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
$dropzone.on('dragover dragenter dragleave drop click', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
this.renderToolbar(fieldModel);
|
||||
break;
|
||||
this.renderToolbar(fieldModel);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'changed':
|
||||
break;
|
||||
|
@ -140,78 +107,40 @@
|
|||
break;
|
||||
}
|
||||
},
|
||||
uploadImage: function uploadImage(file) {
|
||||
this.renderDropzone('upload loading', Drupal.t('Uploading <i>@file</i>…', { '@file': file.name }));
|
||||
|
||||
/**
|
||||
* Validates/uploads a given file.
|
||||
*
|
||||
* @param {File} file
|
||||
* The file to upload.
|
||||
*/
|
||||
uploadImage: function (file) {
|
||||
// Indicate loading by adding a special class to our icon.
|
||||
this.renderDropzone('upload loading', Drupal.t('Uploading <i>@file</i>…', {'@file': file.name}));
|
||||
|
||||
// Build a valid URL for our endpoint.
|
||||
var fieldID = this.fieldModel.get('fieldID');
|
||||
var url = Drupal.quickedit.util.buildUrl(fieldID, Drupal.url('quickedit/image/upload/!entity_type/!id/!field_name/!langcode/!view_mode'));
|
||||
|
||||
// Construct form data that our endpoint can consume.
|
||||
var data = new FormData();
|
||||
data.append('files[image]', file);
|
||||
|
||||
// Construct a POST request to our endpoint.
|
||||
var self = this;
|
||||
this.ajax({
|
||||
type: 'POST',
|
||||
url: url,
|
||||
data: data,
|
||||
success: function (response) {
|
||||
success: function success(response) {
|
||||
var $el = $(self.fieldModel.get('el'));
|
||||
// Indicate that the field has changed - this enables the
|
||||
// "Save" button.
|
||||
|
||||
self.fieldModel.set('state', 'changed');
|
||||
self.fieldModel.get('entity').set('inTempStore', true);
|
||||
self.removeValidationErrors();
|
||||
|
||||
// Replace our html with the new image. If we replaced our entire
|
||||
// element with data.html, we would have to implement complicated logic
|
||||
// like what's in Drupal.quickedit.AppView.renderUpdatedField.
|
||||
var $content = $(response.html).closest('[data-quickedit-field-id]').children();
|
||||
$el.empty().append($content);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Utility function to make an AJAX request to the server.
|
||||
*
|
||||
* In addition to formatting the correct request, this also handles error
|
||||
* codes and messages by displaying them visually inline with the image.
|
||||
*
|
||||
* Drupal.ajax is not called here as the Form API is unused by this
|
||||
* in-place editor, and our JSON requests/responses try to be
|
||||
* editor-agnostic. Ideally similar logic and routes could be used by
|
||||
* modules like CKEditor for drag+drop file uploads as well.
|
||||
*
|
||||
* @param {object} options
|
||||
* Ajax options.
|
||||
* @param {string} options.type
|
||||
* The type of request (i.e. GET, POST, PUT, DELETE, etc.)
|
||||
* @param {string} options.url
|
||||
* The URL for the request.
|
||||
* @param {*} options.data
|
||||
* The data to send to the server.
|
||||
* @param {function} options.success
|
||||
* A callback function used when a request is successful, without errors.
|
||||
*/
|
||||
ajax: function (options) {
|
||||
ajax: function ajax(options) {
|
||||
var defaultOptions = {
|
||||
context: this,
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
error: function () {
|
||||
error: function error() {
|
||||
this.renderDropzone('error', Drupal.t('A server error has occurred.'));
|
||||
}
|
||||
};
|
||||
|
@ -219,7 +148,6 @@
|
|||
var ajaxOptions = $.extend(defaultOptions, options);
|
||||
var successCallback = ajaxOptions.success;
|
||||
|
||||
// Handle the success callback.
|
||||
ajaxOptions.success = function (response) {
|
||||
if (response.main_error) {
|
||||
this.renderDropzone('error', response.main_error);
|
||||
|
@ -227,67 +155,41 @@
|
|||
this.model.set('validationErrors', response.errors);
|
||||
}
|
||||
this.showValidationErrors();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
successCallback(response);
|
||||
}
|
||||
};
|
||||
|
||||
$.ajax(ajaxOptions);
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders our toolbar form for editing metadata.
|
||||
*
|
||||
* @param {Drupal.quickedit.FieldModel} fieldModel
|
||||
* The current Field Model.
|
||||
*/
|
||||
renderToolbar: function (fieldModel) {
|
||||
renderToolbar: function renderToolbar(fieldModel) {
|
||||
var $toolgroup = $('#' + fieldModel.toolbarView.getMainWysiwygToolgroupId());
|
||||
var $toolbar = $toolgroup.find('.quickedit-image-field-info');
|
||||
if ($toolbar.length === 0) {
|
||||
// Perform an AJAX request for extra image info (alt/title).
|
||||
var fieldID = fieldModel.get('fieldID');
|
||||
var url = Drupal.quickedit.util.buildUrl(fieldID, Drupal.url('quickedit/image/info/!entity_type/!id/!field_name/!langcode/!view_mode'));
|
||||
var self = this;
|
||||
self.ajax({
|
||||
type: 'GET',
|
||||
url: url,
|
||||
success: function (response) {
|
||||
success: function success(response) {
|
||||
$toolbar = $(Drupal.theme.quickeditImageToolbar(response));
|
||||
$toolgroup.append($toolbar);
|
||||
$toolbar.on('keyup paste', function () {
|
||||
fieldModel.set('state', 'changed');
|
||||
});
|
||||
// Re-position the toolbar, which could have changed size.
|
||||
|
||||
fieldModel.get('entity').toolbarView.position();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders our dropzone element.
|
||||
*
|
||||
* @param {string} state
|
||||
* The current state of our editor. Only used for visual styling.
|
||||
* @param {string} text
|
||||
* The text to display in the dropzone area.
|
||||
*
|
||||
* @return {jQuery}
|
||||
* The rendered dropzone.
|
||||
*/
|
||||
renderDropzone: function (state, text) {
|
||||
renderDropzone: function renderDropzone(state, text) {
|
||||
var $dropzone = this.$el.find('.quickedit-image-dropzone');
|
||||
// If the element already exists, modify its contents.
|
||||
|
||||
if ($dropzone.length) {
|
||||
$dropzone
|
||||
.removeClass('upload error hover loading')
|
||||
.addClass('.quickedit-image-dropzone ' + state)
|
||||
.children('.quickedit-image-text')
|
||||
.html(text);
|
||||
}
|
||||
else {
|
||||
$dropzone.removeClass('upload error hover loading').addClass('.quickedit-image-dropzone ' + state).children('.quickedit-image-text').html(text);
|
||||
} else {
|
||||
$dropzone = $(Drupal.theme('quickeditImageDropzone', {
|
||||
state: state,
|
||||
text: text
|
||||
|
@ -297,46 +199,29 @@
|
|||
|
||||
return $dropzone;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
revert: function () {
|
||||
revert: function revert() {
|
||||
this.$el.html(this.model.get('originalValue'));
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getQuickEditUISettings: function () {
|
||||
return {padding: false, unifiedToolbar: true, fullWidthToolbar: true, popup: false};
|
||||
getQuickEditUISettings: function getQuickEditUISettings() {
|
||||
return {
|
||||
padding: false,
|
||||
unifiedToolbar: true,
|
||||
fullWidthToolbar: true,
|
||||
popup: false
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
showValidationErrors: function () {
|
||||
showValidationErrors: function showValidationErrors() {
|
||||
var errors = Drupal.theme('quickeditImageErrors', {
|
||||
errors: this.model.get('validationErrors')
|
||||
});
|
||||
$('#' + this.fieldModel.toolbarView.getMainWysiwygToolgroupId())
|
||||
.append(errors);
|
||||
this.getEditedElement()
|
||||
.addClass('quickedit-validation-error');
|
||||
// Re-position the toolbar, which could have changed size.
|
||||
$('#' + this.fieldModel.toolbarView.getMainWysiwygToolgroupId()).append(errors);
|
||||
this.getEditedElement().addClass('quickedit-validation-error');
|
||||
|
||||
this.fieldModel.get('entity').toolbarView.position();
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
removeValidationErrors: function () {
|
||||
$('#' + this.fieldModel.toolbarView.getMainWysiwygToolgroupId())
|
||||
.find('.quickedit-image-errors').remove();
|
||||
this.getEditedElement()
|
||||
.removeClass('quickedit-validation-error');
|
||||
removeValidationErrors: function removeValidationErrors() {
|
||||
$('#' + this.fieldModel.toolbarView.getMainWysiwygToolgroupId()).find('.quickedit-image-errors').remove();
|
||||
this.getEditedElement().removeClass('quickedit-validation-error');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})(jQuery, _, Drupal);
|
||||
})(jQuery, _, Drupal);
|
92
web/core/modules/image/js/theme.es6.js
Normal file
92
web/core/modules/image/js/theme.es6.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* @file
|
||||
* Provides theme functions for image Quick Edit's client-side HTML.
|
||||
*/
|
||||
|
||||
(function(Drupal) {
|
||||
/**
|
||||
* Theme function for validation errors of the Image in-place editor.
|
||||
*
|
||||
* @param {object} settings
|
||||
* Settings object used to construct the markup.
|
||||
* @param {string} settings.errors
|
||||
* Already escaped HTML representing error messages.
|
||||
*
|
||||
* @return {string}
|
||||
* The corresponding HTML.
|
||||
*/
|
||||
Drupal.theme.quickeditImageErrors = function(settings) {
|
||||
return `<div class="quickedit-image-errors">${settings.errors}</div>`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme function for the dropzone element of the Image module's in-place
|
||||
* editor.
|
||||
*
|
||||
* @param {object} settings
|
||||
* Settings object used to construct the markup.
|
||||
* @param {string} settings.state
|
||||
* State of the upload.
|
||||
* @param {string} settings.text
|
||||
* Text to display inline with the dropzone element.
|
||||
*
|
||||
* @return {string}
|
||||
* The corresponding HTML.
|
||||
*/
|
||||
Drupal.theme.quickeditImageDropzone = function(settings) {
|
||||
return (
|
||||
`<div class="quickedit-image-dropzone ${settings.state}">` +
|
||||
' <i class="quickedit-image-icon"></i>' +
|
||||
` <span class="quickedit-image-text">${settings.text}</span>` +
|
||||
'</div>'
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme function for the toolbar of the Image module's in-place editor.
|
||||
*
|
||||
* @param {object} settings
|
||||
* Settings object used to construct the markup.
|
||||
* @param {bool} settings.alt_field
|
||||
* Whether or not the "Alt" field is enabled for this field.
|
||||
* @param {bool} settings.alt_field_required
|
||||
* Whether or not the "Alt" field is required for this field.
|
||||
* @param {string} settings.alt
|
||||
* The current value for the "Alt" field.
|
||||
* @param {bool} settings.title_field
|
||||
* Whether or not the "Title" field is enabled for this field.
|
||||
* @param {bool} settings.title_field_required
|
||||
* Whether or not the "Title" field is required for this field.
|
||||
* @param {string} settings.title
|
||||
* The current value for the "Title" field.
|
||||
*
|
||||
* @return {string}
|
||||
* The corresponding HTML.
|
||||
*/
|
||||
Drupal.theme.quickeditImageToolbar = function(settings) {
|
||||
let html = '<form class="quickedit-image-field-info">';
|
||||
if (settings.alt_field) {
|
||||
html +=
|
||||
`<div><label for="alt" class="${
|
||||
settings.alt_field_required ? 'required' : ''
|
||||
}">${Drupal.t('Alternative text')}</label>` +
|
||||
`<input type="text" placeholder="${settings.alt}" value="${
|
||||
settings.alt
|
||||
}" name="alt" ${settings.alt_field_required ? 'required' : ''}/>` +
|
||||
' </div>';
|
||||
}
|
||||
if (settings.title_field) {
|
||||
html +=
|
||||
`<div><label for="title" class="${
|
||||
settings.title_field_required ? 'form-required' : ''
|
||||
}">${Drupal.t('Title')}</label>` +
|
||||
`<input type="text" placeholder="${settings.title}" value="${
|
||||
settings.title
|
||||
}" name="title" ${settings.title_field_required ? 'required' : ''}/>` +
|
||||
'</div>';
|
||||
}
|
||||
html += '</form>';
|
||||
|
||||
return html;
|
||||
};
|
||||
})(Drupal);
|
|
@ -1,86 +1,29 @@
|
|||
/**
|
||||
* @file
|
||||
* Provides theme functions for image Quick Edit's client-side HTML.
|
||||
*/
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function (Drupal) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Theme function for validation errors of the Image in-place editor.
|
||||
*
|
||||
* @param {object} settings
|
||||
* Settings object used to construct the markup.
|
||||
* @param {string} settings.errors
|
||||
* Already escaped HTML representing error messages.
|
||||
*
|
||||
* @return {string}
|
||||
* The corresponding HTML.
|
||||
*/
|
||||
Drupal.theme.quickeditImageErrors = function (settings) {
|
||||
return '<div class="quickedit-image-errors">' + settings.errors + '</div>';
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme function for the dropzone element of the Image module's in-place
|
||||
* editor.
|
||||
*
|
||||
* @param {object} settings
|
||||
* Settings object used to construct the markup.
|
||||
* @param {string} settings.state
|
||||
* State of the upload.
|
||||
* @param {string} settings.text
|
||||
* Text to display inline with the dropzone element.
|
||||
*
|
||||
* @return {string}
|
||||
* The corresponding HTML.
|
||||
*/
|
||||
Drupal.theme.quickeditImageDropzone = function (settings) {
|
||||
return '<div class="quickedit-image-dropzone ' + settings.state + '">' +
|
||||
' <i class="quickedit-image-icon"></i>' +
|
||||
' <span class="quickedit-image-text">' + settings.text + '</span>' +
|
||||
'</div>';
|
||||
return '<div class="quickedit-image-dropzone ' + settings.state + '">' + ' <i class="quickedit-image-icon"></i>' + (' <span class="quickedit-image-text">' + settings.text + '</span>') + '</div>';
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme function for the toolbar of the Image module's in-place editor.
|
||||
*
|
||||
* @param {object} settings
|
||||
* Settings object used to construct the markup.
|
||||
* @param {bool} settings.alt_field
|
||||
* Whether or not the "Alt" field is enabled for this field.
|
||||
* @param {bool} settings.alt_field_required
|
||||
* Whether or not the "Alt" field is required for this field.
|
||||
* @param {string} settings.alt
|
||||
* The current value for the "Alt" field.
|
||||
* @param {bool} settings.title_field
|
||||
* Whether or not the "Title" field is enabled for this field.
|
||||
* @param {bool} settings.title_field_required
|
||||
* Whether or not the "Title" field is required for this field.
|
||||
* @param {string} settings.title
|
||||
* The current value for the "Title" field.
|
||||
*
|
||||
* @return {string}
|
||||
* The corresponding HTML.
|
||||
*/
|
||||
Drupal.theme.quickeditImageToolbar = function (settings) {
|
||||
var html = '<form class="quickedit-image-field-info">';
|
||||
if (settings.alt_field) {
|
||||
html += ' <div>' +
|
||||
' <label for="alt" class="' + (settings.alt_field_required ? 'required' : '') + '">' + Drupal.t('Alternative text') + '</label>' +
|
||||
' <input type="text" placeholder="' + settings.alt + '" value="' + settings.alt + '" name="alt" ' + (settings.alt_field_required ? 'required' : '') + '/>' +
|
||||
' </div>';
|
||||
html += '<div><label for="alt" class="' + (settings.alt_field_required ? 'required' : '') + '">' + Drupal.t('Alternative text') + '</label>' + ('<input type="text" placeholder="' + settings.alt + '" value="' + settings.alt + '" name="alt" ' + (settings.alt_field_required ? 'required' : '') + '/>') + ' </div>';
|
||||
}
|
||||
if (settings.title_field) {
|
||||
html += ' <div>' +
|
||||
' <label for="title" class="' + (settings.title_field_required ? 'form-required' : '') + '">' + Drupal.t('Title') + '</label>' +
|
||||
' <input type="text" placeholder="' + settings.title + '" value="' + settings.title + '" name="title" ' + (settings.title_field_required ? 'required' : '') + '/>' +
|
||||
' </div>';
|
||||
html += '<div><label for="title" class="' + (settings.title_field_required ? 'form-required' : '') + '">' + Drupal.t('Title') + '</label>' + ('<input type="text" placeholder="' + settings.title + '" value="' + settings.title + '" name="title" ' + (settings.title_field_required ? 'required' : '') + '/>') + '</div>';
|
||||
}
|
||||
html += '</form>';
|
||||
|
||||
return html;
|
||||
};
|
||||
|
||||
})(Drupal);
|
||||
})(Drupal);
|
|
@ -2,6 +2,7 @@ id: d6_imagecache_presets
|
|||
label: ImageCache Presets
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
- Configuration
|
||||
source:
|
||||
plugin: d6_imagecache_presets
|
||||
process:
|
|
@ -2,12 +2,14 @@ id: d7_image_settings
|
|||
label: Image configuration
|
||||
migration_tags:
|
||||
- Drupal 7
|
||||
- Configuration
|
||||
source:
|
||||
plugin: variable
|
||||
variables:
|
||||
- allow_insecure_derivatives
|
||||
- suppress_itok_output
|
||||
- image_style_preview_image
|
||||
source_module: image
|
||||
process:
|
||||
suppress_itok_output: suppress_itok_output
|
||||
allow_insecure_derivatives: allow_insecure_derivatives
|
|
@ -2,13 +2,14 @@ id: d7_image_styles
|
|||
label: Image styles
|
||||
migration_tags:
|
||||
- Drupal 7
|
||||
- Configuration
|
||||
source:
|
||||
plugin: d7_image_styles
|
||||
process:
|
||||
name: name
|
||||
label: label
|
||||
effects:
|
||||
plugin: iterator
|
||||
plugin: sub_process
|
||||
source: effects
|
||||
process:
|
||||
id: name
|
|
@ -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);
|
||||
|
|
|
@ -10,7 +10,7 @@ use Drupal\Core\Image\ImageFactory;
|
|||
use Drupal\Core\Render\Element\StatusMessages;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\image\Plugin\Field\FieldType\ImageItem;
|
||||
use Drupal\user\PrivateTempStoreFactory;
|
||||
use Drupal\Core\TempStore\PrivateTempStoreFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
|
@ -23,7 +23,7 @@ class QuickEditImageController extends ControllerBase {
|
|||
/**
|
||||
* Stores The Quick Edit tempstore.
|
||||
*
|
||||
* @var \Drupal\user\PrivateTempStore
|
||||
* @var \Drupal\Core\TempStore\PrivateTempStore
|
||||
*/
|
||||
protected $tempStore;
|
||||
|
||||
|
@ -48,7 +48,7 @@ class QuickEditImageController extends ControllerBase {
|
|||
* The renderer.
|
||||
* @param \Drupal\Core\Image\ImageFactory $image_factory
|
||||
* The image factory.
|
||||
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
|
||||
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
|
||||
* The tempstore factory.
|
||||
*/
|
||||
public function __construct(RendererInterface $renderer, ImageFactory $image_factory, PrivateTempStoreFactory $temp_store_factory) {
|
||||
|
@ -64,7 +64,7 @@ class QuickEditImageController extends ControllerBase {
|
|||
return new static(
|
||||
$container->get('renderer'),
|
||||
$container->get('image.factory'),
|
||||
$container->get('user.private_tempstore')
|
||||
$container->get('tempstore.private')
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,12 +17,20 @@ use Drupal\Component\Utility\UrlHelper;
|
|||
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
|
||||
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
|
||||
/**
|
||||
* Defines an image style configuration entity.
|
||||
*
|
||||
* @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",
|
||||
|
@ -274,9 +282,8 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, Entity
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function createDerivative($original_uri, $derivative_uri) {
|
||||
|
||||
// If the source file doesn't exist, return FALSE without creating folders.
|
||||
$image = \Drupal::service('image.factory')->get($original_uri);
|
||||
$image = $this->getImageFactory()->get($original_uri);
|
||||
if (!$image->isValid()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -340,6 +347,18 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, Entity
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsUri($uri) {
|
||||
// Only support the URI if its extension is supported by the current image
|
||||
// toolkit.
|
||||
return in_array(
|
||||
mb_strtolower(pathinfo($uri, PATHINFO_EXTENSION)),
|
||||
$this->getImageFactory()->getSupportedExtensions()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -408,6 +427,16 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, Entity
|
|||
return \Drupal::service('plugin.manager.image.effect');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the image factory.
|
||||
*
|
||||
* @return \Drupal\Core\Image\ImageFactory
|
||||
* The image factory.
|
||||
*/
|
||||
protected function getImageFactory() {
|
||||
return \Drupal::service('image.factory');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Drupal private key.
|
||||
*
|
||||
|
|
|
@ -9,6 +9,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
|
||||
/**
|
||||
* Provides an add form for image effects.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ImageEffectAddForm extends ImageEffectFormBase {
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ use Drupal\image\ImageStyleInterface;
|
|||
|
||||
/**
|
||||
* Form for deleting an image effect.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ImageEffectDeleteForm extends ConfirmFormBase {
|
||||
|
||||
|
@ -68,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'));
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ use Drupal\image\ImageStyleInterface;
|
|||
|
||||
/**
|
||||
* Provides an edit form for image effects.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ImageEffectEditForm extends ImageEffectFormBase {
|
||||
|
||||
|
|
|
@ -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'));
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
|
||||
/**
|
||||
* Controller for image style addition forms.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ImageStyleAddForm extends ImageStyleFormBase {
|
||||
|
||||
|
@ -14,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()]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,8 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
|
||||
/**
|
||||
* Creates a form to delete an image style.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ImageStyleDeleteForm extends EntityDeleteForm {
|
||||
|
||||
|
@ -23,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}
|
||||
*/
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\image\Form;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
@ -11,6 +12,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
|
||||
/**
|
||||
* Controller for image style edit form.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ImageStyleEditForm extends ImageStyleFormBase {
|
||||
|
||||
|
@ -58,7 +61,7 @@ class ImageStyleEditForm extends ImageStyleFormBase {
|
|||
$form['preview'] = [
|
||||
'#type' => 'item',
|
||||
'#title' => $this->t('Preview'),
|
||||
'#markup' => drupal_render($preview_arguments),
|
||||
'#markup' => \Drupal::service('renderer')->render($preview_arguments),
|
||||
// Render preview above parent elements.
|
||||
'#weight' => -5,
|
||||
];
|
||||
|
@ -143,7 +146,7 @@ class ImageStyleEditForm extends ImageStyleFormBase {
|
|||
$new_effect_options = [];
|
||||
$effects = $this->imageEffectManager->getDefinitions();
|
||||
uasort($effects, function ($a, $b) {
|
||||
return strcasecmp($a['id'], $b['id']);
|
||||
return Unicode::strcasecmp($a['label'], $b['label']);
|
||||
});
|
||||
foreach ($effects as $effect => $definition) {
|
||||
$new_effect_options[$effect] = $definition['label'];
|
||||
|
@ -228,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.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -251,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.'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,8 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
|
||||
/**
|
||||
* Form controller for image style flush.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ImageStyleFlushForm extends EntityConfirmFormBase {
|
||||
|
||||
|
@ -43,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());
|
||||
}
|
||||
|
||||
|
|
|
@ -50,8 +50,7 @@ interface ImageEffectInterface extends PluginInspectionInterface, ConfigurablePl
|
|||
public function transformDimensions(array &$dimensions, $uri);
|
||||
|
||||
/**
|
||||
* Returns the extension the derivative would have have after applying this
|
||||
* image effect.
|
||||
* Returns the extension of the derivative after applying this image effect.
|
||||
*
|
||||
* @param string $extension
|
||||
* The file extension the derivative has before applying.
|
||||
|
|
|
@ -42,7 +42,6 @@ interface ImageStyleInterface extends ConfigEntityInterface {
|
|||
*/
|
||||
public function setName($name);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the URI of this image when using this style.
|
||||
*
|
||||
|
@ -194,4 +193,15 @@ interface ImageStyleInterface extends ConfigEntityInterface {
|
|||
*/
|
||||
public function deleteImageEffect(ImageEffectInterface $effect);
|
||||
|
||||
/**
|
||||
* Determines if this style can be applied to a given image.
|
||||
*
|
||||
* @param string $uri
|
||||
* The URI of the image.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the image is supported, FALSE otherwise.
|
||||
*/
|
||||
public function supportsUri($uri);
|
||||
|
||||
}
|
||||
|
|
|
@ -39,9 +39,17 @@ class ImageStyleListBuilder extends ConfigEntityListBuilder {
|
|||
'url' => $entity->urlInfo('flush-form'),
|
||||
];
|
||||
|
||||
return parent::getDefaultOperations($entity) + [
|
||||
$operations = parent::getDefaultOperations($entity) + [
|
||||
'flush' => $flush,
|
||||
];
|
||||
|
||||
// Remove destination URL from the edit link to allow editing image
|
||||
// effects.
|
||||
if (isset($operations['edit'])) {
|
||||
$operations['edit']['url'] = $entity->toUrl('edit-form');
|
||||
}
|
||||
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -48,8 +48,11 @@ class PathProcessorImageStyles implements InboundPathProcessorInterface {
|
|||
if (strpos($path, '/' . $directory_path . '/styles/') === 0) {
|
||||
$path_prefix = '/' . $directory_path . '/styles/';
|
||||
}
|
||||
elseif (strpos($path, '/system/files/styles/') === 0) {
|
||||
// Check if the string '/system/files/styles/' exists inside the path,
|
||||
// that means we have a case of private file's image style.
|
||||
elseif (strpos($path, '/system/files/styles/') !== FALSE) {
|
||||
$path_prefix = '/system/files/styles/';
|
||||
$path = substr($path, strpos($path, $path_prefix), strlen($path));
|
||||
}
|
||||
else {
|
||||
return $path;
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\image\Plugin\Field\FieldType;
|
||||
|
||||
use Drupal\Component\Utility\Random;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
@ -262,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'] = [
|
||||
|
@ -313,13 +314,18 @@ class ImageItem extends FileItem {
|
|||
$height = $this->height;
|
||||
|
||||
// Determine the dimensions if necessary.
|
||||
if (empty($width) || empty($height)) {
|
||||
$image = \Drupal::service('image.factory')->get($this->entity->getFileUri());
|
||||
if ($image->isValid()) {
|
||||
$this->width = $image->getWidth();
|
||||
$this->height = $image->getHeight();
|
||||
if ($this->entity && $this->entity instanceof EntityInterface) {
|
||||
if (empty($width) || empty($height)) {
|
||||
$image = \Drupal::service('image.factory')->get($this->entity->getFileUri());
|
||||
if ($image->isValid()) {
|
||||
$this->width = $image->getWidth();
|
||||
$this->height = $image->getHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
trigger_error(sprintf("Missing file with ID %s.", $this->target_id), E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -338,8 +344,8 @@ class ImageItem extends FileItem {
|
|||
if (!isset($images[$extension][$min_resolution][$max_resolution]) || count($images[$extension][$min_resolution][$max_resolution]) <= 5) {
|
||||
$tmp_file = drupal_tempnam('temporary://', 'generateImage_');
|
||||
$destination = $tmp_file . '.' . $extension;
|
||||
file_unmanaged_move($tmp_file, $destination, FILE_CREATE_DIRECTORY);
|
||||
if ($path = $random->image(drupal_realpath($destination), $min_resolution, $max_resolution)) {
|
||||
file_unmanaged_move($tmp_file, $destination);
|
||||
if ($path = $random->image(\Drupal::service('file_system')->realpath($destination), $min_resolution, $max_resolution)) {
|
||||
$image = File::create();
|
||||
$image->setFileUri($path);
|
||||
$image->setOwnerId(\Drupal::currentUser()->id());
|
||||
|
@ -348,7 +354,7 @@ class ImageItem extends FileItem {
|
|||
$destination_dir = static::doGetUploadLocation($settings);
|
||||
file_prepare_directory($destination_dir, FILE_CREATE_DIRECTORY);
|
||||
$destination = $destination_dir . '/' . basename($path);
|
||||
$file = file_move($image, $destination, FILE_CREATE_DIRECTORY);
|
||||
$file = file_move($image, $destination);
|
||||
$images[$extension][$min_resolution][$max_resolution][$file->id()] = $file;
|
||||
}
|
||||
else {
|
||||
|
@ -427,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,
|
||||
];
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
|
||||
namespace Drupal\image\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Image\ImageFactory;
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\ElementInfoManagerInterface;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\file\Plugin\Field\FieldWidget\FileWidget;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
|
@ -22,6 +25,36 @@ use Drupal\image\Entity\ImageStyle;
|
|||
*/
|
||||
class ImageWidget extends FileWidget {
|
||||
|
||||
/**
|
||||
* The image factory service.
|
||||
*
|
||||
* @var \Drupal\Core\Image\ImageFactory
|
||||
*/
|
||||
protected $imageFactory;
|
||||
|
||||
/**
|
||||
* Constructs an ImageWidget object.
|
||||
*
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the widget.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
|
||||
* The definition of the field to which the widget is associated.
|
||||
* @param array $settings
|
||||
* The widget settings.
|
||||
* @param array $third_party_settings
|
||||
* Any third party settings.
|
||||
* @param \Drupal\Core\Render\ElementInfoManagerInterface $element_info
|
||||
* The element info manager service.
|
||||
* @param \Drupal\Core\Image\ImageFactory $image_factory
|
||||
* The image factory service.
|
||||
*/
|
||||
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, ElementInfoManagerInterface $element_info, ImageFactory $image_factory = NULL) {
|
||||
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings, $element_info);
|
||||
$this->imageFactory = $image_factory ?: \Drupal::service('image.factory');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -112,15 +145,21 @@ class ImageWidget extends FileWidget {
|
|||
|
||||
$field_settings = $this->getFieldSettings();
|
||||
|
||||
// Add image validation.
|
||||
$element['#upload_validators']['file_validate_is_image'] = [];
|
||||
|
||||
// Add upload resolution validation.
|
||||
if ($field_settings['max_resolution'] || $field_settings['min_resolution']) {
|
||||
$element['#upload_validators']['file_validate_image_resolution'] = [$field_settings['max_resolution'], $field_settings['min_resolution']];
|
||||
}
|
||||
|
||||
// If not using custom extension validation, ensure this is an image.
|
||||
$supported_extensions = ['png', 'gif', 'jpg', 'jpeg'];
|
||||
$extensions = isset($element['#upload_validators']['file_validate_extensions'][0]) ? $element['#upload_validators']['file_validate_extensions'][0] : implode(' ', $supported_extensions);
|
||||
$extensions = array_intersect(explode(' ', $extensions), $supported_extensions);
|
||||
$extensions = $field_settings['file_extensions'];
|
||||
$supported_extensions = $this->imageFactory->getSupportedExtensions();
|
||||
|
||||
// If using custom extension validation, ensure that the extensions are
|
||||
// supported by the current image toolkit. Otherwise, validate against all
|
||||
// toolkit supported extensions.
|
||||
$extensions = !empty($extensions) ? array_intersect(explode(' ', $extensions), $supported_extensions) : $supported_extensions;
|
||||
$element['#upload_validators']['file_validate_extensions'][0] = implode(' ', $extensions);
|
||||
|
||||
// Add mobile device image capture acceptance.
|
||||
|
@ -224,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,
|
||||
|
@ -256,7 +295,8 @@ class ImageWidget extends FileWidget {
|
|||
public static function validateRequiredFields($element, FormStateInterface $form_state) {
|
||||
// Only do validation if the function is triggered from other places than
|
||||
// the image process form.
|
||||
if (!in_array('file_managed_file_submit', $form_state->getTriggeringElement()['#submit'])) {
|
||||
$triggering_element = $form_state->getTriggeringElement();
|
||||
if (empty($triggering_element['#submit']) || !in_array('file_managed_file_submit', $triggering_element['#submit'])) {
|
||||
// If the image is not there, we do not check for empty values.
|
||||
$parents = $element['#parents'];
|
||||
$field = array_pop($parents);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image\Plugin\migrate\cckfield\d7;
|
||||
|
||||
@trigger_error('ImageField is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.x. Use \Drupal\image\Plugin\migrate\field\d7\ImageField instead.', E_USER_DEPRECATED);
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\cckfield\CckFieldPluginBase;
|
||||
|
||||
/**
|
||||
* @MigrateCckField(
|
||||
* id = "image",
|
||||
* core = {7},
|
||||
* source_module = "image",
|
||||
* destination_module = "file"
|
||||
* )
|
||||
*
|
||||
* @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.x. Use
|
||||
* \Drupal\image\Plugin\migrate\field\d7\ImageField instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2751897
|
||||
*/
|
||||
class ImageField extends CckFieldPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processCckFieldValues(MigrationInterface $migration, $field_name, $data) {
|
||||
$process = [
|
||||
'plugin' => 'sub_process',
|
||||
'source' => $field_name,
|
||||
'process' => [
|
||||
'target_id' => 'fid',
|
||||
'alt' => 'alt',
|
||||
'title' => 'title',
|
||||
'width' => 'width',
|
||||
'height' => 'height',
|
||||
],
|
||||
];
|
||||
$migration->mergeProcessOfProperty($field_name, $process);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image\Plugin\migrate\field\d6;
|
||||
|
||||
use Drupal\file\Plugin\migrate\field\d6\FileField;
|
||||
|
||||
/**
|
||||
* @MigrateField(
|
||||
* id = "imagefield",
|
||||
* core = {6},
|
||||
* source_module = "imagefield",
|
||||
* destination_module = "image"
|
||||
* )
|
||||
*/
|
||||
class ImageField extends FileField {}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image\Plugin\migrate\field\d7;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase;
|
||||
|
||||
/**
|
||||
* @MigrateField(
|
||||
* id = "image",
|
||||
* core = {7},
|
||||
* source_module = "image",
|
||||
* destination_module = "image"
|
||||
* )
|
||||
*/
|
||||
class ImageField extends FieldPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defineValueProcessPipeline(MigrationInterface $migration, $field_name, $data) {
|
||||
$process = [
|
||||
'plugin' => 'sub_process',
|
||||
'source' => $field_name,
|
||||
'process' => [
|
||||
'target_id' => 'fid',
|
||||
'alt' => 'alt',
|
||||
'title' => 'title',
|
||||
'width' => 'width',
|
||||
'height' => 'height',
|
||||
],
|
||||
];
|
||||
$migration->mergeProcessOfProperty($field_name, $process);
|
||||
}
|
||||
|
||||
}
|
|
@ -10,7 +10,7 @@ use Drupal\migrate\Row;
|
|||
*
|
||||
* @MigrateSource(
|
||||
* id = "d6_imagecache_presets",
|
||||
* source_provider = "imagecache"
|
||||
* source_module = "imagecache"
|
||||
* )
|
||||
*/
|
||||
class ImageCachePreset extends DrupalSqlBase {
|
||||
|
|
|
@ -10,7 +10,7 @@ use Drupal\migrate\Row;
|
|||
*
|
||||
* @MigrateSource(
|
||||
* id = "d7_image_styles",
|
||||
* source_provider = "image"
|
||||
* source_module = "image"
|
||||
* )
|
||||
*/
|
||||
class ImageStyles extends DrupalSqlBase {
|
||||
|
|
|
@ -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()
|
||||
|
@ -69,7 +72,7 @@ abstract class ImageFieldTestBase extends WebTestBase {
|
|||
$edit = [
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$edit['files[' . $field_name . '_0]'] = drupal_realpath($image->uri);
|
||||
$edit['files[' . $field_name . '_0]'] = \Drupal::service('file_system')->realpath($image->uri);
|
||||
$this->drupalPostForm('node/add/' . $type, $edit, t('Preview'));
|
||||
}
|
||||
|
||||
|
@ -89,11 +92,11 @@ abstract class ImageFieldTestBase extends WebTestBase {
|
|||
$edit = [
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$edit['files[' . $field_name . '_0]'] = drupal_realpath($image->uri);
|
||||
$this->drupalPostForm('node/add/' . $type, $edit, t('Save and publish'));
|
||||
$edit['files[' . $field_name . '_0]'] = \Drupal::service('file_system')->realpath($image->uri);
|
||||
$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.
|
||||
|
|
|
@ -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 %}
|
|
@ -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
|
19
web/core/modules/image/tests/fixtures/update/test_scale_and_crop_add_anchor.php
vendored
Normal file
19
web/core/modules/image/tests/fixtures/update/test_scale_and_crop_add_anchor.php
vendored
Normal file
|
@ -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();
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image_module_test\Plugin\Field\FieldFormatter;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FormatterBase;
|
||||
|
||||
/**
|
||||
* Empty renderer for a dummy field with an AJAX handler.
|
||||
*
|
||||
* @FieldFormatter(
|
||||
* id = "image_module_test_dummy_ajax_formatter",
|
||||
* module = "image_module_test",
|
||||
* label = @Translation("Dummy AJAX"),
|
||||
* field_types= {
|
||||
* "image_module_test_dummy_ajax"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class DummyAjaxFormatter extends FormatterBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsSummary() {
|
||||
$summary = [];
|
||||
$summary[] = t('Renders nothing');
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function viewElements(FieldItemListInterface $items, $langcode) {
|
||||
$element = [];
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image_module_test\Plugin\Field\FieldType;
|
||||
|
||||
use Drupal\Core\Field\FieldItemBase;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
|
||||
/**
|
||||
* Defines a dummy field containing an AJAX handler.
|
||||
*
|
||||
* @FieldType(
|
||||
* id = "image_module_test_dummy_ajax",
|
||||
* label = @Translation("Dummy AJAX"),
|
||||
* description = @Translation("A field containing an AJAX handler."),
|
||||
* category = @Translation("Field"),
|
||||
* default_widget = "image_module_test_dummy_ajax_widget",
|
||||
* default_formatter = "image_module_test_dummy_ajax_formatter"
|
||||
* )
|
||||
*/
|
||||
class DummyAjaxItem extends FieldItemBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function schema(FieldStorageDefinitionInterface $field_definition) {
|
||||
return [
|
||||
'columns' => [
|
||||
'value' => [
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isEmpty() {
|
||||
return empty($this->get('value')->getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('string')
|
||||
->setLabel(new TranslatableMarkup('Dummy string value'));
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image_module_test\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\WidgetBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Default widget for Dummy AJAX test.
|
||||
*
|
||||
* @FieldWidget(
|
||||
* id = "image_module_test_dummy_ajax_widget",
|
||||
* label = @Translation("Dummy AJAX widget"),
|
||||
* field_types = {
|
||||
* "image_module_test_dummy_ajax"
|
||||
* },
|
||||
* multiple_values = TRUE,
|
||||
* )
|
||||
*/
|
||||
class DummyAjaxWidget extends WidgetBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
$element['select_widget'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Dummy select'),
|
||||
'#options' => ['pow' => 'Pow!', 'bam' => 'Bam!'],
|
||||
'#required' => TRUE,
|
||||
'#ajax' => [
|
||||
'callback' => get_called_class() . '::dummyAjaxCallback',
|
||||
'effect' => 'fade',
|
||||
],
|
||||
];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax callback for Dummy AJAX test.
|
||||
*
|
||||
* @param array $form
|
||||
* The build form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The form state.
|
||||
*
|
||||
* @return \Drupal\Core\Ajax\AjaxResponse
|
||||
* Ajax response.
|
||||
*/
|
||||
public static function dummyAjaxCallback(array &$form, FormStateInterface $form_state) {
|
||||
return new AjaxResponse();
|
||||
}
|
||||
|
||||
}
|
|
@ -5,5 +5,5 @@ package: Testing
|
|||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- image
|
||||
- views
|
||||
- drupal:image
|
||||
- drupal:views
|
||||
|
|
|
@ -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';
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
@ -203,6 +209,10 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
|
|||
$this->assertTitle(t('Edit style @name | Drupal', ['@name' => $style_label]));
|
||||
$this->assertResponse(200, format_string('Image style %original renamed to %new', ['%original' => $style->id(), '%new' => $style_name]));
|
||||
|
||||
// Check that the available image effects are properly sorted.
|
||||
$option = $this->xpath('//select[@id=:id]//option', [':id' => 'edit-new--2']);
|
||||
$this->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
|
||||
// the old image directory has been deleted.
|
||||
|
@ -286,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.
|
||||
*/
|
||||
|
@ -368,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);
|
||||
|
||||
|
@ -418,9 +387,20 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
|
|||
// Edit the scale effect that was just added.
|
||||
$this->clickLink(t('Edit'));
|
||||
$this->drupalPostForm(NULL, ['data[width]' => '24', 'data[height]' => '19'], t('Update effect'));
|
||||
$this->drupalPostForm(NULL, ['new' => 'image_scale'], t('Add'));
|
||||
|
||||
// Add another scale effect and make sure both exist.
|
||||
// Add another scale effect and make sure both exist. Click through from
|
||||
// the overview to make sure that it is possible to add new effect then.
|
||||
$this->drupalGet('admin/config/media/image-styles');
|
||||
$rows = $this->xpath('//table/tbody/tr');
|
||||
$i = 0;
|
||||
foreach ($rows as $row) {
|
||||
if ($row->find('css', 'td')->getText() === 'Test style scale edit scale') {
|
||||
$this->clickLink('Edit', $i);
|
||||
break;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
$this->drupalPostForm(NULL, ['new' => 'image_scale'], t('Add'));
|
||||
$this->drupalPostForm(NULL, ['data[width]' => '12', 'data[height]' => '19'], t('Add effect'));
|
||||
$this->assertText(t('Scale 24×19'));
|
||||
$this->assertText(t('Scale 12×19'));
|
|
@ -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.
|
||||
|
@ -173,7 +179,6 @@ class ImageDimensionsTest extends WebTestBase {
|
|||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
|
||||
|
||||
// Add a crop effect.
|
||||
$effect = [
|
||||
'id' => 'image_crop',
|
||||
|
@ -208,14 +213,18 @@ class ImageDimensionsTest extends WebTestBase {
|
|||
|
||||
$effect_id = $style->addImageEffect($effect);
|
||||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="41" height="41" alt="" class="image-style-test" />');
|
||||
// @todo Uncomment this once
|
||||
// https://www.drupal.org/project/drupal/issues/2670966 is resolved.
|
||||
// $this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="41" height="41" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
$this->assertEqual($image_file->getWidth(), 41);
|
||||
$this->assertEqual($image_file->getHeight(), 41);
|
||||
// @todo Uncomment this once
|
||||
// https://www.drupal.org/project/drupal/issues/2670966 is resolved.
|
||||
// $this->assertEqual($image_file->getWidth(), 41);
|
||||
// $this->assertEqual($image_file->getHeight(), 41);
|
||||
|
||||
$effect_plugin = $style->getEffect($effect_id);
|
||||
$style->deleteImageEffect($effect_plugin);
|
||||
|
@ -277,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.
|
||||
*/
|
|
@ -3,7 +3,7 @@
|
|||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\system\Tests\Image\ToolkitTestBase;
|
||||
use Drupal\FunctionalTests\Image\ToolkitTestBase;
|
||||
|
||||
/**
|
||||
* Tests that the image effects pass parameters to the toolkit correctly.
|
||||
|
@ -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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,19 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
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 field.
|
||||
* Tests setting up default images both to the field and field storage.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
use EntityViewTrait {
|
||||
buildEntityView as drupalBuildEntityView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
|
@ -22,7 +32,7 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
|||
public static $modules = ['field_ui'];
|
||||
|
||||
/**
|
||||
* Tests CRUD for fields and fields fields with default images.
|
||||
* Tests CRUD for fields and field storages with default images.
|
||||
*/
|
||||
public function testDefaultImages() {
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
|
@ -37,16 +47,17 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
|||
$file->save();
|
||||
}
|
||||
$default_images = [];
|
||||
foreach (['field', 'field', 'field2', 'field_new', 'field_new'] as $image_target) {
|
||||
foreach (['field_storage', 'field', 'field2', 'field_storage_new', 'field_new', 'field_storage_private', 'field_private'] as $image_target) {
|
||||
$file = File::create((array) array_pop($files));
|
||||
$file->save();
|
||||
$default_images[$image_target] = $file;
|
||||
}
|
||||
|
||||
// Create an image field and add an field to the article content type.
|
||||
// Create an image field storage and add a field to the article content
|
||||
// type.
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$storage_settings['default_image'] = [
|
||||
'uuid' => $default_images['field']->uuid(),
|
||||
'uuid' => $default_images['field_storage']->uuid(),
|
||||
'alt' => '',
|
||||
'title' => '',
|
||||
'width' => 0,
|
||||
|
@ -72,11 +83,11 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
|||
|
||||
$field_storage = $field->getFieldStorageDefinition();
|
||||
|
||||
// The field default image id should be 1.
|
||||
$this->assertEqual($field_storage->getSetting('default_image')['uuid'], $default_images['field']->uuid());
|
||||
// The field storage default image id should be 1.
|
||||
$this->assertEqual($field_storage->getSetting('default_image')['uuid'], $default_images['field_storage']->uuid());
|
||||
|
||||
// Also test \Drupal\field\Entity\FieldStorageConfig::getSettings().
|
||||
$this->assertEqual($field_storage->getSettings()['default_image']['uuid'], $default_images['field']->uuid());
|
||||
$this->assertEqual($field_storage->getSettings()['default_image']['uuid'], $default_images['field_storage']->uuid());
|
||||
|
||||
// Add another field with another default image to the page content type.
|
||||
$field2 = FieldConfig::create([
|
||||
|
@ -104,15 +115,16 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
|||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Confirm the defaults are present on the article field settings form.
|
||||
// Confirm the defaults are present on the article field storage settings
|
||||
// form.
|
||||
$field_id = $field->id();
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id/storage");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field']->id(),
|
||||
$default_images['field_storage']->id(),
|
||||
format_string(
|
||||
'Article image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field']->id()]
|
||||
'Article image field storage default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_storage']->id()]
|
||||
)
|
||||
);
|
||||
// Confirm the defaults are present on the article field edit form.
|
||||
|
@ -121,19 +133,19 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
|||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field']->id(),
|
||||
format_string(
|
||||
'Article image field field default equals expected file ID of @fid.',
|
||||
'Article image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
// Confirm the defaults are present on the page field settings form.
|
||||
// Confirm the defaults are present on the page field storage settings form.
|
||||
$this->drupalGet("admin/structure/types/manage/page/fields/$field_id/storage");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field']->id(),
|
||||
$default_images['field_storage']->id(),
|
||||
format_string(
|
||||
'Page image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field']->id()]
|
||||
'Page image field storage default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_storage']->id()]
|
||||
)
|
||||
);
|
||||
// Confirm the defaults are present on the page field edit form.
|
||||
|
@ -143,7 +155,7 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
|||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field2']->id(),
|
||||
format_string(
|
||||
'Page image field field default equals expected file ID of @fid.',
|
||||
'Page image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field2']->id()]
|
||||
)
|
||||
);
|
||||
|
@ -181,22 +193,23 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
|||
|
||||
// Upload a new default for the field storage.
|
||||
$default_image_settings = $field_storage->getSetting('default_image');
|
||||
$default_image_settings['uuid'] = $default_images['field_new']->uuid();
|
||||
$default_image_settings['uuid'] = $default_images['field_storage_new']->uuid();
|
||||
$field_storage->setSetting('default_image', $default_image_settings);
|
||||
$field_storage->save();
|
||||
|
||||
// Confirm that the new default is used on the article field settings form.
|
||||
// Confirm that the new default is used on the article field storage
|
||||
// settings form.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id/storage");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field_new']->id(),
|
||||
$default_images['field_storage_new']->id(),
|
||||
format_string(
|
||||
'Updated image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_new']->id()]
|
||||
'Updated image field storage default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_storage_new']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
// Reload the nodes and confirm the field field defaults are used.
|
||||
// Reload the nodes and confirm the field defaults are used.
|
||||
$node_storage->resetCache([$article->id(), $page->id()]);
|
||||
$article_built = $this->drupalBuildEntityView($article = $node_storage->load($article->id()));
|
||||
$page_built = $this->drupalBuildEntityView($page = $node_storage->load($page->id()));
|
||||
|
@ -217,20 +230,19 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
|||
)
|
||||
);
|
||||
|
||||
// Upload a new default for the article's field field.
|
||||
// Upload a new default for the article's field.
|
||||
$default_image_settings = $field->getSetting('default_image');
|
||||
$default_image_settings['uuid'] = $default_images['field_new']->uuid();
|
||||
$field->setSetting('default_image', $default_image_settings);
|
||||
$field->save();
|
||||
|
||||
// Confirm the new field field default is used on the article field
|
||||
// admin form.
|
||||
// Confirm the new field default is used on the article field admin form.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field_new']->id(),
|
||||
format_string(
|
||||
'Updated article image field field default equals expected file ID of @fid.',
|
||||
'Updated article image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_new']->id()]
|
||||
)
|
||||
);
|
||||
|
@ -264,31 +276,31 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
|||
$this->drupalGet('node/add/article');
|
||||
$this->assertRaw($file->getFilename());
|
||||
|
||||
// Remove the instance default from articles.
|
||||
// Remove the field default from articles.
|
||||
$default_image_settings = $field->getSetting('default_image');
|
||||
$default_image_settings['uuid'] = 0;
|
||||
$field->setSetting('default_image', $default_image_settings);
|
||||
$field->save();
|
||||
|
||||
// Confirm the article field field default has been removed.
|
||||
// Confirm the article field default has been removed.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
'',
|
||||
'Updated article image field field default has been successfully removed.'
|
||||
'Updated article image field default has been successfully removed.'
|
||||
);
|
||||
|
||||
// Reload the nodes.
|
||||
$node_storage->resetCache([$article->id(), $page->id()]);
|
||||
$article_built = $this->drupalBuildEntityView($article = $node_storage->load($article->id()));
|
||||
$page_built = $this->drupalBuildEntityView($page = $node_storage->load($page->id()));
|
||||
// Confirm the article uses the new field (not field) default.
|
||||
// Confirm the article uses the new field storage (not field) default.
|
||||
$this->assertEqual(
|
||||
$article_built[$field_name][0]['#item']->target_id,
|
||||
$default_images['field_new']->id(),
|
||||
$default_images['field_storage_new']->id(),
|
||||
format_string(
|
||||
'An existing article node without an image has the expected default image file ID of @fid.',
|
||||
['@fid' => $default_images['field_new']->id()]
|
||||
['@fid' => $default_images['field_storage_new']->id()]
|
||||
)
|
||||
);
|
||||
// Confirm the page remains unchanged.
|
||||
|
@ -302,28 +314,67 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
|||
);
|
||||
|
||||
$non_image = $this->drupalGetTestFiles('text');
|
||||
$this->drupalPostForm(NULL, ['files[settings_default_image_uuid]' => drupal_realpath($non_image[0]->uri)], t("Upload"));
|
||||
$this->drupalPostForm(NULL, ['files[settings_default_image_uuid]' => \Drupal::service('file_system')->realpath($non_image[0]->uri)], t("Upload"));
|
||||
$this->assertText('The specified file text-0.txt could not be uploaded.');
|
||||
$this->assertText('Only files with the following extensions are allowed: png gif jpg jpeg.');
|
||||
|
||||
// Confirm the default image is shown on the node form.
|
||||
$file = File::load($default_images['field_new']->id());
|
||||
$file = File::load($default_images['field_storage_new']->id());
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertRaw($file->getFilename());
|
||||
|
||||
// Change the default image for the field storage and also change the upload
|
||||
// destination to the private filesystem at the same time.
|
||||
$default_image_settings = $field_storage->getSetting('default_image');
|
||||
$default_image_settings['uuid'] = $default_images['field_storage_private']->uuid();
|
||||
$field_storage->setSetting('default_image', $default_image_settings);
|
||||
$field_storage->setSetting('uri_scheme', 'private');
|
||||
$field_storage->save();
|
||||
|
||||
// Confirm that the new default is used on the article field storage
|
||||
// settings form.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id/storage");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field_storage_private']->id(),
|
||||
format_string(
|
||||
'Updated image field storage default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_storage_private']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
// Upload a new default for the article's field after setting the field
|
||||
// storage upload destination to 'private'.
|
||||
$default_image_settings = $field->getSetting('default_image');
|
||||
$default_image_settings['uuid'] = $default_images['field_private']->uuid();
|
||||
$field->setSetting('default_image', $default_image_settings);
|
||||
$field->save();
|
||||
|
||||
// Confirm the new field field default is used on the article field
|
||||
// admin form.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field_private']->id(),
|
||||
format_string(
|
||||
'Updated article image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_private']->id()]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests image field and field having an invalid default image.
|
||||
* Tests image field and field storage having an invalid default image.
|
||||
*/
|
||||
public function testInvalidDefaultImage() {
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'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();
|
||||
|
@ -338,7 +389,7 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
|||
'settings' => [
|
||||
'default_image' => [
|
||||
'uuid' => 100000,
|
||||
]
|
||||
],
|
||||
],
|
||||
]);
|
||||
$field->save();
|
|
@ -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]);
|
||||
|
@ -282,7 +293,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
|||
$field_name . '[0][alt]' => $image['#alt'],
|
||||
$field_name . '[0][title]' => $image['#title'],
|
||||
];
|
||||
$this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save and keep published'));
|
||||
$this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save'));
|
||||
$default_output = str_replace("\n", NULL, $renderer->renderRoot($image));
|
||||
$this->assertRaw($default_output, 'Image displayed using user supplied alt and title attributes.');
|
||||
|
||||
|
@ -292,7 +303,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
|||
$field_name . '[0][alt]' => $this->randomMachineName($test_size),
|
||||
$field_name . '[0][title]' => $this->randomMachineName($test_size),
|
||||
];
|
||||
$this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save and keep published'));
|
||||
$this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save'));
|
||||
$schema = $field->getFieldStorageDefinition()->getSchema();
|
||||
$this->assertRaw(t('Alternative text cannot be longer than %max characters but is currently %length characters long.', [
|
||||
'%max' => $schema['columns']['alt']['length'],
|
||||
|
@ -312,21 +323,21 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
|||
// @see FileWidget::formMultipleElements().
|
||||
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.' . $field_name . '/storage', ['cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED], t('Save field settings'));
|
||||
$edit = [
|
||||
'files[' . $field_name . '_1][]' => drupal_realpath($test_image->uri),
|
||||
'files[' . $field_name . '_1][]' => \Drupal::service('file_system')->realpath($test_image->uri),
|
||||
];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
// Add the required alt text.
|
||||
$this->drupalPostForm(NULL, [$field_name . '[1][alt]' => $alt], t('Save and keep published'));
|
||||
$this->drupalPostForm(NULL, [$field_name . '[1][alt]' => $alt], t('Save'));
|
||||
$this->assertText(format_string('Article @title has been updated.', ['@title' => $node->getTitle()]));
|
||||
|
||||
// Assert ImageWidget::process() calls FieldWidget::process().
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$edit = [
|
||||
'files[' . $field_name . '_2][]' => drupal_realpath($test_image->uri),
|
||||
'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][]"]');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -357,7 +368,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
|||
$title = $this->randomString(1024);
|
||||
$edit = [
|
||||
// Get the path of the 'image-test.png' file.
|
||||
'files[settings_default_image_uuid]' => drupal_realpath($images[0]->uri),
|
||||
'files[settings_default_image_uuid]' => \Drupal::service('file_system')->realpath($images[0]->uri),
|
||||
'settings[default_image][alt]' => $alt,
|
||||
'settings[default_image][title]' => $title,
|
||||
];
|
||||
|
@ -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);
|
||||
|
@ -426,7 +438,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
|||
// Add a default image to the new field.
|
||||
$edit = [
|
||||
// Get the path of the 'image-test.gif' file.
|
||||
'files[settings_default_image_uuid]' => drupal_realpath($images[2]->uri),
|
||||
'files[settings_default_image_uuid]' => \Drupal::service('file_system')->realpath($images[2]->uri),
|
||||
'settings[default_image][alt]' => $alt,
|
||||
'settings[default_image][title]' => $title,
|
||||
];
|
|
@ -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()
|
||||
|
@ -66,7 +67,7 @@ abstract class ImageFieldTestBase extends BrowserTestBase {
|
|||
$edit = [
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$edit['files[' . $field_name . '_0]'] = drupal_realpath($image->uri);
|
||||
$edit['files[' . $field_name . '_0]'] = \Drupal::service('file_system')->realpath($image->uri);
|
||||
$this->drupalPostForm('node/add/' . $type, $edit, t('Preview'));
|
||||
}
|
||||
|
||||
|
@ -86,11 +87,11 @@ abstract class ImageFieldTestBase extends BrowserTestBase {
|
|||
$edit = [
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$edit['files[' . $field_name . '_0]'] = drupal_realpath($image->uri);
|
||||
$this->drupalPostForm('node/add/' . $type, $edit, t('Save and publish'));
|
||||
$edit['files[' . $field_name . '_0]'] = \Drupal::service('file_system')->realpath($image->uri);
|
||||
$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.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests validation functions such as min/max resolution.
|
||||
|
@ -8,6 +10,66 @@ namespace Drupal\image\Tests;
|
|||
* @group image
|
||||
*/
|
||||
class ImageFieldValidateTest extends ImageFieldTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test image validity.
|
||||
*/
|
||||
public function testValid() {
|
||||
$file_system = $this->container->get('file_system');
|
||||
$image_files = $this->drupalGetTestFiles('image');
|
||||
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$this->createImageField($field_name, 'article', [], ['file_directory' => 'test-upload']);
|
||||
$expected_path = 'public://test-upload';
|
||||
|
||||
// Create alt text for the image.
|
||||
$alt = $this->randomMachineName();
|
||||
|
||||
// Create a node with a valid image.
|
||||
$node = $this->uploadNodeImage($image_files[0], $field_name, 'article', $alt);
|
||||
$this->assertTrue(file_exists($expected_path . '/' . $image_files[0]->filename));
|
||||
|
||||
// Remove the image.
|
||||
$this->drupalPostForm('node/' . $node . '/edit', [], t('Remove'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
|
||||
// Get invalid image test files from simpletest.
|
||||
$files = file_scan_directory(drupal_get_path('module', 'simpletest') . '/files', '/invalid-img-.*/');
|
||||
$invalid_image_files = [];
|
||||
foreach ($files as $file) {
|
||||
$invalid_image_files[$file->filename] = $file;
|
||||
}
|
||||
|
||||
// Try uploading a zero-byte image.
|
||||
$zero_size_image = $invalid_image_files['invalid-img-zero-size.png'];
|
||||
$edit = [
|
||||
'files[' . $field_name . '_0]' => $file_system->realpath($zero_size_image->uri),
|
||||
];
|
||||
$this->drupalPostForm('node/' . $node . '/edit', $edit, t('Upload'));
|
||||
$this->assertFalse(file_exists($expected_path . '/' . $zero_size_image->filename));
|
||||
|
||||
// Try uploading an invalid image.
|
||||
$invalid_image = $invalid_image_files['invalid-img-test.png'];
|
||||
$edit = [
|
||||
'files[' . $field_name . '_0]' => $file_system->realpath($invalid_image->uri),
|
||||
];
|
||||
$this->drupalPostForm('node/' . $node . '/edit', $edit, t('Upload'));
|
||||
$this->assertFalse(file_exists($expected_path . '/' . $invalid_image->filename));
|
||||
|
||||
// Upload a valid image again.
|
||||
$valid_image = $image_files[0];
|
||||
$edit = [
|
||||
'files[' . $field_name . '_0]' => $file_system->realpath($valid_image->uri),
|
||||
];
|
||||
$this->drupalPostForm('node/' . $node . '/edit', $edit, t('Upload'));
|
||||
$this->assertTrue(file_exists($expected_path . '/' . $valid_image->filename));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test min/max resolution settings.
|
||||
*/
|
||||
|
@ -19,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),
|
||||
|
@ -62,6 +124,7 @@ class ImageFieldValidateTest extends ImageFieldTestBase {
|
|||
}
|
||||
if ($image_file->getWidth() < $min_resolution['width']) {
|
||||
$image_that_is_too_small = $image;
|
||||
$image_that_is_too_small_file = $image_file;
|
||||
}
|
||||
if ($image_that_is_too_small && $image_that_is_too_big) {
|
||||
break;
|
||||
|
@ -69,7 +132,11 @@ class ImageFieldValidateTest extends ImageFieldTestBase {
|
|||
}
|
||||
$this->uploadNodeImage($image_that_is_too_small, $field_names[0], 'article');
|
||||
$this->assertRaw(t('The specified file %name could not be uploaded.', ['%name' => $image_that_is_too_small->filename]));
|
||||
$this->assertRaw(t('The image is too small; the minimum dimensions are %dimensions pixels.', ['%dimensions' => '50x50']));
|
||||
$this->assertRaw(t('The image is too small. The minimum dimensions are %dimensions pixels and the image size is %widthx%height pixels.', [
|
||||
'%dimensions' => '50x50',
|
||||
'%width' => $image_that_is_too_small_file->getWidth(),
|
||||
'%height' => $image_that_is_too_small_file->getHeight(),
|
||||
]));
|
||||
$this->uploadNodeImage($image_that_is_too_big, $field_names[0], 'article');
|
||||
$this->assertText(t('The image was resized to fit within the maximum allowed dimensions of 100x100 pixels.'));
|
||||
$this->uploadNodeImage($image_that_is_too_small, $field_names[1], 'article');
|
||||
|
@ -119,7 +186,7 @@ class ImageFieldValidateTest extends ImageFieldTestBase {
|
|||
$edit = [
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save and publish'));
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
|
||||
$this->assertNoText(t('Alternative text field is required.'));
|
||||
$this->assertNoText(t('Title field is required.'));
|
||||
|
@ -132,7 +199,7 @@ class ImageFieldValidateTest extends ImageFieldTestBase {
|
|||
$edit = [
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save and publish'));
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
|
||||
$this->assertNoText(t('Alternative text field is required.'));
|
||||
$this->assertNoText(t('Title field is required.'));
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
|
||||
/**
|
||||
* Tests the image field widget.
|
||||
*
|
||||
|
@ -27,6 +29,22 @@ class ImageFieldWidgetTest extends ImageFieldTestBase {
|
|||
$this->assertNotEqual(0, count($this->xpath('//div[contains(@class, "field--widget-image-image")]')), 'Image field widget found on add/node page', 'Browser');
|
||||
$this->assertNotEqual(0, count($this->xpath('//input[contains(@accept, "image/*")]')), 'Image field widget limits accepted files.', 'Browser');
|
||||
$this->assertNoText('Image test on [site:name]');
|
||||
|
||||
// Check for allowed image file extensions - default.
|
||||
$this->assertText('Allowed types: png gif jpg jpeg.');
|
||||
|
||||
// Try adding to the field config an unsupported extension, should not
|
||||
// appear in the allowed types.
|
||||
$field_config = FieldConfig::loadByName('node', 'article', $field_name);
|
||||
$field_config->setSetting('file_extensions', 'png gif jpg jpeg tiff')->save();
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertText('Allowed types: png gif jpg jpeg.');
|
||||
|
||||
// Add a supported extension and remove some supported ones, we should see
|
||||
// the intersect of those entered in field config with those supported.
|
||||
$field_config->setSetting('file_extensions', 'png jpe tiff')->save();
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertText('Allowed types: png jpe.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
*/
|
||||
|
@ -29,6 +35,9 @@ class ImageOnTranslatedEntityTest extends ImageFieldTestBase {
|
|||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// This test expects unused managed files to be marked as a temporary file.
|
||||
$this->config('file.settings')->set('make_unused_managed_files_temporary', TRUE)->save();
|
||||
|
||||
// Create the "Basic page" node type.
|
||||
// @todo Remove the disabling of new revision creation in
|
||||
// https://www.drupal.org/node/1239558.
|
||||
|
@ -92,7 +101,7 @@ class ImageOnTranslatedEntityTest extends ImageFieldTestBase {
|
|||
// Edit the node to upload a file.
|
||||
$edit = [];
|
||||
$name = 'files[' . $this->fieldName . '_0]';
|
||||
$edit[$name] = drupal_realpath($this->drupalGetTestFiles('image')[0]->uri);
|
||||
$edit[$name] = \Drupal::service('file_system')->realpath($this->drupalGetTestFiles('image')[0]->uri);
|
||||
$this->drupalPostForm('node/' . $default_language_node->id() . '/edit', $edit, t('Save'));
|
||||
$edit = [$this->fieldName . '[0][alt]' => 'Lost in translation image', $this->fieldName . '[0][title]' => 'Lost in translation image title'];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
@ -105,7 +114,7 @@ class ImageOnTranslatedEntityTest extends ImageFieldTestBase {
|
|||
$edit = [];
|
||||
$edit['title[0][value]'] = 'Scarlett Johansson';
|
||||
$name = 'files[' . $this->fieldName . '_0]';
|
||||
$edit[$name] = drupal_realpath($this->drupalGetTestFiles('image')[1]->uri);
|
||||
$edit[$name] = \Drupal::service('file_system')->realpath($this->drupalGetTestFiles('image')[1]->uri);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
|
||||
$edit = [$this->fieldName . '[0][alt]' => 'Scarlett Johansson image', $this->fieldName . '[0][title]' => 'Scarlett Johansson image title'];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
|
||||
|
@ -137,7 +146,7 @@ class ImageOnTranslatedEntityTest extends ImageFieldTestBase {
|
|||
$edit = [];
|
||||
$edit['title[0][value]'] = 'Akiko Takeshita';
|
||||
$name = 'files[' . $this->fieldName . '_0]';
|
||||
$edit[$name] = drupal_realpath($this->drupalGetTestFiles('image')[2]->uri);
|
||||
$edit[$name] = \Drupal::service('file_system')->realpath($this->drupalGetTestFiles('image')[2]->uri);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
|
||||
$edit = [$this->fieldName . '[0][alt]' => 'Akiko Takeshita image', $this->fieldName . '[0][title]' => 'Akiko Takeshita image title'];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
|
||||
|
@ -172,7 +181,7 @@ class ImageOnTranslatedEntityTest extends ImageFieldTestBase {
|
|||
$edit = [];
|
||||
$edit['title[0][value]'] = 'Giovanni Ribisi';
|
||||
$name = 'files[' . $this->fieldName . '_0]';
|
||||
$edit[$name] = drupal_realpath($this->drupalGetTestFiles('image')[3]->uri);
|
||||
$edit[$name] = \Drupal::service('file_system')->realpath($this->drupalGetTestFiles('image')[3]->uri);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
|
||||
$name = $this->fieldName . '[0][alt]';
|
||||
|
|
@ -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).
|
|
@ -1,34 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
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.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['image', 'image_module_test'];
|
||||
public static $modules = ['image', 'image_module_test', 'language'];
|
||||
|
||||
/**
|
||||
* The image style.
|
||||
*
|
||||
* @var \Drupal\image\ImageStyleInterface
|
||||
*/
|
||||
protected $style;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->style = ImageStyle::create(['name' => 'style_foo', 'label' => $this->randomString()]);
|
||||
$this->style = ImageStyle::create([
|
||||
'name' => 'style_foo',
|
||||
'label' => $this->randomString(),
|
||||
]);
|
||||
$this->style->save();
|
||||
|
||||
// Create a new language.
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,6 +91,20 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
|||
$this->doImageStyleUrlAndPathTests('private', FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an image style URL with the "public://" schema and language prefix.
|
||||
*/
|
||||
public function testImageStyleUrlAndPathPublicLanguage() {
|
||||
$this->doImageStyleUrlAndPathTests('public', TRUE, TRUE, 'fr');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an image style URL with the "private://" schema and language prefix.
|
||||
*/
|
||||
public function testImageStyleUrlAndPathPrivateLanguage() {
|
||||
$this->doImageStyleUrlAndPathTests('private', TRUE, TRUE, 'fr');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an image style URL with a file URL that has an extra slash in it.
|
||||
*/
|
||||
|
@ -93,7 +125,7 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
|||
/**
|
||||
* Tests building an image style URL.
|
||||
*/
|
||||
public function doImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_slash = FALSE) {
|
||||
public function doImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_slash = FALSE, $langcode = FALSE) {
|
||||
$this->prepareRequestForGenerator($clean_url);
|
||||
|
||||
// Make the default scheme neither "public" nor "private" to verify the
|
||||
|
@ -105,6 +137,13 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
|||
$status = file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
|
||||
$this->assertNotIdentical(FALSE, $status, 'Created the directory for the generated images for the test style.');
|
||||
|
||||
// Override the language to build the URL for the correct language.
|
||||
if ($langcode) {
|
||||
$language_manager = \Drupal::service('language_manager');
|
||||
$language = $language_manager->getLanguage($langcode);
|
||||
$language_manager->setConfigOverrideLanguage($language);
|
||||
}
|
||||
|
||||
// Create a working copy of the file.
|
||||
$files = $this->drupalGetTestFiles('image');
|
||||
$file = array_shift($files);
|
||||
|
@ -119,6 +158,11 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
|||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$generate_url = $this->style->buildUrl($original_uri, $clean_url);
|
||||
|
||||
// Make sure that language prefix is never added to the image style URL.
|
||||
if ($langcode) {
|
||||
$this->assertTrue(strpos($generate_url, "/$langcode/") === FALSE, 'Langcode was not found in the image style URL.');
|
||||
}
|
||||
|
||||
// Ensure that the tests still pass when the file is generated by accessing
|
||||
// a poorly constructed (but still valid) file URL that has an extra slash
|
||||
// in it.
|
||||
|
@ -132,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
|
||||
|
@ -151,13 +195,15 @@ 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.');
|
||||
|
||||
// Check that we did not download the original file.
|
||||
$original_image = $this->container->get('image.factory')->get($original_uri);
|
||||
$original_image = $this->container->get('image.factory')
|
||||
->get($original_uri);
|
||||
$this->assertNotEqual($this->drupalGetHeader('Content-Length'), $original_image->getFileSize());
|
||||
|
||||
if ($scheme == 'private') {
|
||||
|
@ -192,14 +238,17 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
|||
|
||||
$this->drupalGet($generate_url_noaccess);
|
||||
$this->assertResponse(403, 'Confirmed that access is denied for the private image style.');
|
||||
// Verify that images are not appended to the response. Currently this test only uses PNG images.
|
||||
if (strpos($generate_url, '.png') === FALSE ) {
|
||||
// Verify that images are not appended to the response.
|
||||
// Currently this test only uses PNG images.
|
||||
if (strpos($generate_url, '.png') === FALSE) {
|
||||
$this->fail('Confirming that private image styles are not appended require PNG file.');
|
||||
}
|
||||
else {
|
||||
// Check for PNG-Signature (cf. http://www.libpng.org/pub/png/book/chapter08.html#png.ch08.div.2) in the
|
||||
// response body.
|
||||
$this->assertNoRaw( chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10), 'No PNG signature found in the response body.');
|
||||
// Check for PNG-Signature
|
||||
// (cf. http://www.libpng.org/pub/png/book/chapter08.html#png.ch08.div.2)
|
||||
// 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 {
|
||||
|
@ -215,7 +264,9 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
|||
|
||||
// Allow insecure image derivatives to be created for the remainder of this
|
||||
// test.
|
||||
$this->config('image.settings')->set('allow_insecure_derivatives', TRUE)->save();
|
||||
$this->config('image.settings')
|
||||
->set('allow_insecure_derivatives', TRUE)
|
||||
->save();
|
||||
|
||||
// Create another working copy of the file.
|
||||
$files = $this->drupalGetTestFiles('image');
|
||||
|
@ -236,10 +287,11 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
|||
$this->drupalGet($generate_url);
|
||||
$this->assertResponse(200, 'Image was accessible at the URL with a missing token.');
|
||||
|
||||
// Stop supressing the security token in the URL.
|
||||
// Stop suppressing the security token in the URL.
|
||||
$this->config('image.settings')->set('suppress_itok_output', FALSE)->save();
|
||||
// Ensure allow_insecure_derivatives is enabled.
|
||||
$this->assertEqual($this->config('image.settings')->get('allow_insecure_derivatives'), TRUE);
|
||||
$this->assertEqual($this->config('image.settings')
|
||||
->get('allow_insecure_derivatives'), TRUE);
|
||||
// Check that a security token is still required when generating a second
|
||||
// image derivative using the first one as a source.
|
||||
$nested_url = $this->style->buildUrl($generated_uri, $clean_url);
|
||||
|
@ -247,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.');
|
|
@ -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.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -139,14 +150,16 @@ class QuickEditImageControllerTest extends WebTestBase {
|
|||
foreach ($this->drupalGetTestFiles('image') as $image) {
|
||||
/** @var \Drupal\Core\Image\ImageInterface $image_file */
|
||||
$image_file = $image_factory->get($image->uri);
|
||||
if ($image_file->getWidth() < 50 || $image_file->getWidth() > 100 ) {
|
||||
if ($image_file->getWidth() < 50 || $image_file->getWidth() > 100) {
|
||||
$invalid_image = $image;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$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();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,13 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image\Tests\Update;
|
||||
namespace Drupal\Tests\image\Functional\Update;
|
||||
|
||||
use Drupal\system\Tests\Update\UpdatePathTestBase;
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests Image update path.
|
||||
*
|
||||
* @group image
|
||||
* @group legacy
|
||||
*/
|
||||
class ImageUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
|
@ -16,7 +17,7 @@ class ImageUpdateTest extends UpdatePathTestBase {
|
|||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8-rc1.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8-rc1.bare.standard.php.gz',
|
||||
];
|
||||
}
|
||||
|
|
@ -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,11 +2,12 @@
|
|||
|
||||
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;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\Tests\field\Kernel\FieldKernelTestBase;
|
||||
|
||||
/**
|
||||
|
@ -56,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,
|
||||
|
@ -99,4 +100,89 @@ class ImageFormatterTest extends FieldKernelTestBase {
|
|||
$this->assertEquals($entity->{$this->fieldName}[1]->entity->getCacheTags(), $build[$this->fieldName][1]['#cache']['tags'], 'Second image cache tags is as expected');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests ImageFormatter's handling of SVG images.
|
||||
*
|
||||
* @requires extension gd
|
||||
*/
|
||||
public function testImageFormatterSvg() {
|
||||
// Install the default image styles.
|
||||
$this->installConfig(['image']);
|
||||
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
|
||||
$png = File::create([
|
||||
'uri' => 'public://test-image.png',
|
||||
]);
|
||||
$png->save();
|
||||
|
||||
// We need to create an actual empty PNG, or the GD toolkit will not
|
||||
// consider the image valid.
|
||||
$png_resource = imagecreate(300, 300);
|
||||
imagefill($png_resource, 0, 0, imagecolorallocate($png_resource, 0, 0, 0));
|
||||
imagepng($png_resource, $png->getFileUri());
|
||||
|
||||
$svg = File::create([
|
||||
'uri' => 'public://test-image.svg',
|
||||
]);
|
||||
$svg->save();
|
||||
// We don't have to put any real SVG data in here, because the GD toolkit
|
||||
// won't be able to load it anyway.
|
||||
touch($svg->getFileUri());
|
||||
|
||||
$entity = EntityTest::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
$this->fieldName => [$png, $svg],
|
||||
]);
|
||||
$entity->save();
|
||||
|
||||
// Ensure that the display is using the medium image style.
|
||||
$component = $this->display->getComponent($this->fieldName);
|
||||
$component['settings']['image_style'] = 'medium';
|
||||
$this->display->setComponent($this->fieldName, $component)->save();
|
||||
|
||||
$build = $this->display->build($entity);
|
||||
|
||||
// The first image is a PNG, so it is supported by the GD image toolkit.
|
||||
// The image style should be applied with its cache tags, image derivative
|
||||
// computed with its URI and dimensions.
|
||||
$this->assertCacheTags($build[$this->fieldName][0], ImageStyle::load('medium')->getCacheTags());
|
||||
$renderer->renderRoot($build[$this->fieldName][0]);
|
||||
$this->assertEquals('medium', $build[$this->fieldName][0]['#image_style']);
|
||||
// We check that the image URL contains the expected style directory
|
||||
// structure.
|
||||
$this->assertTrue(strpos($build[$this->fieldName][0]['#markup'], 'styles/medium/public/test-image.png') !== FALSE);
|
||||
$this->assertTrue(strpos($build[$this->fieldName][0]['#markup'], 'width="220"') !== FALSE);
|
||||
$this->assertTrue(strpos($build[$this->fieldName][0]['#markup'], 'height="220"') !== FALSE);
|
||||
|
||||
// The second image is an SVG, which is not supported by the GD toolkit.
|
||||
// The image style should still be applied with its cache tags, but image
|
||||
// derivative will not be available so <img> tag will point to the original
|
||||
// image.
|
||||
$this->assertCacheTags($build[$this->fieldName][1], ImageStyle::load('medium')->getCacheTags());
|
||||
$renderer->renderRoot($build[$this->fieldName][1]);
|
||||
$this->assertEquals('medium', $build[$this->fieldName][1]['#image_style']);
|
||||
// We check that the image URL does not contain the style directory
|
||||
// structure.
|
||||
$this->assertFalse(strpos($build[$this->fieldName][1]['#markup'], 'styles/medium/public/test-image.svg'));
|
||||
// Since we did not store original image dimensions, width and height
|
||||
// HTML attributes will not be present.
|
||||
$this->assertFalse(strpos($build[$this->fieldName][1]['#markup'], 'width'));
|
||||
$this->assertFalse(strpos($build[$this->fieldName][1]['#markup'], 'height'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a renderable array has a set of cache tags.
|
||||
*
|
||||
* @param array $renderable
|
||||
* The renderable array. Must have a #cache[tags] element.
|
||||
* @param array $cache_tags
|
||||
* The expected cache tags.
|
||||
*/
|
||||
protected function assertCacheTags(array $renderable, array $cache_tags) {
|
||||
$diff = array_diff($cache_tags, $renderable['#cache']['tags']);
|
||||
$this->assertEmpty($diff);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\Tests\image\Kernel;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
@ -10,6 +11,7 @@ use Drupal\field\Entity\FieldConfig;
|
|||
use Drupal\Tests\field\Kernel\FieldKernelTestBase;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
/**
|
||||
* Tests using entity fields of the image field type.
|
||||
|
@ -40,6 +42,14 @@ class ImageItemTest extends FieldKernelTestBase {
|
|||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('user');
|
||||
$this->installConfig(['user']);
|
||||
// Give anonymous users permission to access content, so that we can view
|
||||
// and download public file.
|
||||
$anonymous_role = Role::load(Role::ANONYMOUS_ID);
|
||||
$anonymous_role->grantPermission('access content');
|
||||
$anonymous_role->save();
|
||||
|
||||
$this->installEntitySchema('file');
|
||||
$this->installSchema('file', ['file_usage']);
|
||||
|
||||
|
@ -57,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',
|
||||
]);
|
||||
|
@ -90,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',
|
||||
]);
|
||||
|
@ -129,4 +139,26 @@ class ImageItemTest extends FieldKernelTestBase {
|
|||
$this->assertEqual($entity->image_test->entity->get('filemime')->value, 'image/jpeg');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a malformed image.
|
||||
*/
|
||||
public function testImageItemMalformed() {
|
||||
// Validate entity is an image and don't gather dimensions if it is not.
|
||||
$entity = EntityTest::create();
|
||||
$entity->image_test = NULL;
|
||||
$entity->image_test->target_id = 9999;
|
||||
// PHPUnit re-throws E_USER_WARNING as an exception.
|
||||
try {
|
||||
$entity->save();
|
||||
$this->fail('Exception did not fail');
|
||||
}
|
||||
catch (EntityStorageException $exception) {
|
||||
$this->assertInstanceOf(\PHPUnit_Framework_Error_Warning::class, $exception->getPrevious());
|
||||
$this->assertEquals($exception->getMessage(), 'Missing file with ID 9999.');
|
||||
$this->assertEmpty($entity->image_test->width);
|
||||
$this->assertEmpty($entity->image_test->height);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.');
|
||||
}
|
||||
|
|
@ -141,9 +141,11 @@ class MigrateImageCacheTest extends MigrateDrupal6TestBase {
|
|||
|
||||
$this->startCollectingMessages();
|
||||
$this->executeMigration('d6_imagecache_presets');
|
||||
$this->assertEqual(['error' => [
|
||||
'The Drupal 8 image crop effect does not support numeric values for x and y offsets. Use keywords to set crop effect offsets instead.'
|
||||
]], $this->migrateMessages);
|
||||
$this->assertEqual([
|
||||
'error' => [
|
||||
'The Drupal 8 image crop effect does not support numeric values for x and y offsets. Use keywords to set crop effect offsets instead.',
|
||||
],
|
||||
], $this->migrateMessages);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Kernel\Migrate\d6;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\Tests\node\Kernel\Migrate\d6\MigrateNodeTestBase;
|
||||
use Drupal\Tests\file\Kernel\Migrate\d6\FileMigrationTestTrait;
|
||||
|
||||
/**
|
||||
* Image migration test.
|
||||
*
|
||||
* This extends the node test, because the D6 fixture has images; they just
|
||||
* need to be migrated into D8.
|
||||
*
|
||||
* @group migrate_drupal_6
|
||||
*/
|
||||
class MigrateImageTest extends MigrateNodeTestBase {
|
||||
|
||||
use FileMigrationTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['menu_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->setUpMigratedFiles();
|
||||
$this->installSchema('file', ['file_usage']);
|
||||
$this->executeMigrations([
|
||||
'd6_node',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test image migration from Drupal 6 to 8.
|
||||
*/
|
||||
public function testNode() {
|
||||
$node = Node::load(9);
|
||||
// Test the image field sub fields.
|
||||
$this->assertSame('2', $node->field_test_imagefield->target_id);
|
||||
$this->assertSame('Test alt', $node->field_test_imagefield->alt);
|
||||
$this->assertSame('Test title', $node->field_test_imagefield->title);
|
||||
$this->assertSame('80', $node->field_test_imagefield->width);
|
||||
$this->assertSame('60', $node->field_test_imagefield->height);
|
||||
}
|
||||
|
||||
}
|
|
@ -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']]);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -30,8 +31,12 @@ class RelationshipUserImageDataTest extends ViewTestBase {
|
|||
*/
|
||||
public static $testViews = ['test_image_user_image_data'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
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([
|
||||
|
@ -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();
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Unit\Plugin\migrate\field\d7;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\image\Plugin\migrate\field\d7\ImageField;
|
||||
use Prophecy\Argument;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\image\Plugin\migrate\field\d7\ImageField
|
||||
* @group image
|
||||
* @group legacy
|
||||
*/
|
||||
class ImageFieldTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\migrate_drupal\Plugin\MigrateFieldInterface
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* @var \Drupal\migrate\Plugin\MigrationInterface
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->plugin = new ImageField([], 'image', []);
|
||||
|
||||
$migration = $this->prophesize(MigrationInterface::class);
|
||||
|
||||
// The plugin's processFieldValues() method will call
|
||||
// mergeProcessOfProperty() and return nothing. So, in order to examine the
|
||||
// process pipeline created by the plugin, we need to ensure that
|
||||
// getProcess() always returns the last input to mergeProcessOfProperty().
|
||||
$migration->mergeProcessOfProperty(Argument::type('string'), Argument::type('array'))
|
||||
->will(function ($arguments) use ($migration) {
|
||||
$migration->getProcess()->willReturn($arguments[1]);
|
||||
});
|
||||
$this->migration = $migration->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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', []);
|
||||
|
||||
$expected = [
|
||||
'plugin' => 'sub_process',
|
||||
'source' => 'somefieldname',
|
||||
'process' => [
|
||||
'target_id' => 'fid',
|
||||
'alt' => 'alt',
|
||||
'title' => 'title',
|
||||
'width' => 'width',
|
||||
'height' => 'height',
|
||||
],
|
||||
];
|
||||
$this->assertSame($expected, $this->migration->getProcess());
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue