updated core to 8.6.1 via composer

This commit is contained in:
2018-09-12 13:58:26 +02:00
parent a9a219f2ed
commit ea56b9fba3
4443 changed files with 112098 additions and 40708 deletions
+91 -42
View File
@@ -7,7 +7,7 @@
* prevents separate file fields from accidentally uploading files).
*/
(function ($, Drupal) {
(function($, Drupal) {
/**
* Attach behaviors to the file fields passed in the settings.
*
@@ -24,9 +24,14 @@
let elements;
function initFileValidation(selector) {
$context.find(selector)
$context
.find(selector)
.once('fileValidate')
.on('change.fileValidate', { extensions: elements[selector] }, Drupal.file.validateExtension);
.on(
'change.fileValidate',
{ extensions: elements[selector] },
Drupal.file.validateExtension,
);
}
if (settings.file && settings.file.elements) {
@@ -39,7 +44,8 @@
let elements;
function removeFileValidation(selector) {
$context.find(selector)
$context
.find(selector)
.removeOnce('fileValidate')
.off('change.fileValidate', Drupal.file.validateExtension);
}
@@ -63,11 +69,17 @@
*/
Drupal.behaviors.fileAutoUpload = {
attach(context) {
$(context).find('input[type="file"]').once('auto-file-upload').on('change.autoFileUpload', Drupal.file.triggerUploadButton);
$(context)
.find('input[type="file"]')
.once('auto-file-upload')
.on('change.autoFileUpload', Drupal.file.triggerUploadButton);
},
detach(context, setting, trigger) {
detach(context, settings, trigger) {
if (trigger === 'unload') {
$(context).find('input[type="file"]').removeOnce('auto-file-upload').off('.autoFileUpload');
$(context)
.find('input[type="file"]')
.removeOnce('auto-file-upload')
.off('.autoFileUpload');
}
},
};
@@ -85,13 +97,23 @@
Drupal.behaviors.fileButtons = {
attach(context) {
const $context = $(context);
$context.find('.js-form-submit').on('mousedown', Drupal.file.disableFields);
$context.find('.js-form-managed-file .js-form-submit').on('mousedown', Drupal.file.progressBar);
$context
.find('.js-form-submit')
.on('mousedown', Drupal.file.disableFields);
$context
.find('.js-form-managed-file .js-form-submit')
.on('mousedown', Drupal.file.progressBar);
},
detach(context) {
const $context = $(context);
$context.find('.js-form-submit').off('mousedown', Drupal.file.disableFields);
$context.find('.js-form-managed-file .js-form-submit').off('mousedown', Drupal.file.progressBar);
detach(context, settings, trigger) {
if (trigger === 'unload') {
const $context = $(context);
$context
.find('.js-form-submit')
.off('mousedown', Drupal.file.disableFields);
$context
.find('.js-form-managed-file .js-form-submit')
.off('mousedown', Drupal.file.progressBar);
}
},
};
@@ -107,10 +129,14 @@
*/
Drupal.behaviors.filePreviewLinks = {
attach(context) {
$(context).find('div.js-form-managed-file .file a').on('click', Drupal.file.openInNewWindow);
$(context)
.find('div.js-form-managed-file .file a')
.on('click', Drupal.file.openInNewWindow);
},
detach(context) {
$(context).find('div.js-form-managed-file .file a').off('click', Drupal.file.openInNewWindow);
$(context)
.find('div.js-form-managed-file .file a')
.off('click', Drupal.file.openInNewWindow);
},
};
@@ -120,7 +146,6 @@
* @namespace
*/
Drupal.file = Drupal.file || {
/**
* Client-side file input validation of file extensions.
*
@@ -139,18 +164,25 @@
if (extensionPattern.length > 1 && this.value.length > 0) {
const acceptableMatch = new RegExp(`\\.(${extensionPattern})$`, 'gi');
if (!acceptableMatch.test(this.value)) {
const error = Drupal.t('The selected file %filename cannot be uploaded. Only files with the following extensions are allowed: %extensions.', {
// According to the specifications of HTML5, a file upload control
// should not reveal the real local path to the file that a user
// has selected. Some web browsers implement this restriction by
// replacing the local path with "C:\fakepath\", which can cause
// confusion by leaving the user thinking perhaps Drupal could not
// find the file because it messed up the file path. To avoid this
// confusion, therefore, we strip out the bogus fakepath string.
'%filename': this.value.replace('C:\\fakepath\\', ''),
'%extensions': extensionPattern.replace(/\|/g, ', '),
});
$(this).closest('div.js-form-managed-file').prepend(`<div class="messages messages--error file-upload-js-error" aria-live="polite">${error}</div>`);
const error = Drupal.t(
'The selected file %filename cannot be uploaded. Only files with the following extensions are allowed: %extensions.',
{
// According to the specifications of HTML5, a file upload control
// should not reveal the real local path to the file that a user
// has selected. Some web browsers implement this restriction by
// replacing the local path with "C:\fakepath\", which can cause
// confusion by leaving the user thinking perhaps Drupal could not
// find the file because it messed up the file path. To avoid this
// confusion, therefore, we strip out the bogus fakepath string.
'%filename': this.value.replace('C:\\fakepath\\', ''),
'%extensions': extensionPattern.replace(/\|/g, ', '),
},
);
$(this)
.closest('div.js-form-managed-file')
.prepend(
`<div class="messages messages--error file-upload-js-error" aria-live="polite">${error}</div>`,
);
this.value = '';
// Cancel all other change event handlers.
event.stopImmediatePropagation();
@@ -167,7 +199,10 @@
* The event triggered. For example `change.autoFileUpload`.
*/
triggerUploadButton(event) {
$(event.target).closest('.js-form-managed-file').find('.js-form-submit').trigger('mousedown');
$(event.target)
.closest('.js-form-managed-file')
.find('.js-form-submit')
.trigger('mousedown');
},
/**
@@ -179,17 +214,14 @@
* The event triggered, most likely a `mousedown` event.
*/
disableFields(event) {
const $clickedButton = $(this).findOnce('ajax');
// Only disable upload fields for Ajax buttons.
if (!$clickedButton.length) {
return;
}
const $clickedButton = $(this);
// Check if we're working with an "Upload" button.
let $enabledFields = [];
if ($clickedButton.closest('div.js-form-managed-file').length > 0) {
$enabledFields = $clickedButton.closest('div.js-form-managed-file').find('input.js-form-file');
$enabledFields = $clickedButton
.closest('div.js-form-managed-file')
.find('input.js-form-file');
}
// Temporarily disable upload fields other than the one we're currently
@@ -201,7 +233,11 @@
// functions are called, so we don't have to worry about the fields being
// re-enabled too soon. @todo If the previous sentence is true, why not
// set the timeout to 0?
const $fieldsToTemporarilyDisable = $('div.js-form-managed-file input.js-form-file').not($enabledFields).not(':disabled');
const $fieldsToTemporarilyDisable = $(
'div.js-form-managed-file input.js-form-file',
)
.not($enabledFields)
.not(':disabled');
$fieldsToTemporarilyDisable.prop('disabled', true);
setTimeout(() => {
$fieldsToTemporarilyDisable.prop('disabled', false);
@@ -218,12 +254,17 @@
*/
progressBar(event) {
const $clickedButton = $(this);
const $progressId = $clickedButton.closest('div.js-form-managed-file').find('input.file-progress');
const $progressId = $clickedButton
.closest('div.js-form-managed-file')
.find('input.file-progress');
if ($progressId.length) {
const originalName = $progressId.attr('name');
// Replace the name with the required identifier.
$progressId.attr('name', originalName.match(/APC_UPLOAD_PROGRESS|UPLOAD_IDENTIFIER/)[0]);
$progressId.attr(
'name',
originalName.match(/APC_UPLOAD_PROGRESS|UPLOAD_IDENTIFIER/)[0],
);
// Restore the original name after the upload begins.
setTimeout(() => {
@@ -232,8 +273,12 @@
}
// Show the progress bar if the upload takes longer than half a second.
setTimeout(() => {
$clickedButton.closest('div.js-form-managed-file').find('div.ajax-progress-bar').slideDown();
$clickedButton
.closest('div.js-form-managed-file')
.find('div.ajax-progress-bar')
.slideDown();
}, 500);
$clickedButton.trigger('fileUpload');
},
/**
@@ -247,7 +292,11 @@
openInNewWindow(event) {
event.preventDefault();
$(this).attr('target', '_blank');
window.open(this.href, 'filePreview', 'toolbar=0,scrollbars=1,location=1,statusbar=1,menubar=0,resizable=1,width=500,height=550');
window.open(
this.href,
'filePreview',
'toolbar=0,scrollbars=1,location=1,statusbar=1,menubar=0,resizable=1,width=500,height=550',
);
},
};
}(jQuery, Drupal));
})(jQuery, Drupal);
+1 -1
View File
@@ -5,4 +5,4 @@ package: Field types
version: VERSION
core: 8.x
dependencies:
- field
- drupal:field
+9 -10
View File
@@ -39,7 +39,7 @@
attach: function attach(context) {
$(context).find('input[type="file"]').once('auto-file-upload').on('change.autoFileUpload', Drupal.file.triggerUploadButton);
},
detach: function detach(context, setting, trigger) {
detach: function detach(context, settings, trigger) {
if (trigger === 'unload') {
$(context).find('input[type="file"]').removeOnce('auto-file-upload').off('.autoFileUpload');
}
@@ -52,10 +52,12 @@
$context.find('.js-form-submit').on('mousedown', Drupal.file.disableFields);
$context.find('.js-form-managed-file .js-form-submit').on('mousedown', Drupal.file.progressBar);
},
detach: function detach(context) {
var $context = $(context);
$context.find('.js-form-submit').off('mousedown', Drupal.file.disableFields);
$context.find('.js-form-managed-file .js-form-submit').off('mousedown', Drupal.file.progressBar);
detach: function detach(context, settings, trigger) {
if (trigger === 'unload') {
var $context = $(context);
$context.find('.js-form-submit').off('mousedown', Drupal.file.disableFields);
$context.find('.js-form-managed-file .js-form-submit').off('mousedown', Drupal.file.progressBar);
}
}
};
@@ -93,11 +95,7 @@
$(event.target).closest('.js-form-managed-file').find('.js-form-submit').trigger('mousedown');
},
disableFields: function disableFields(event) {
var $clickedButton = $(this).findOnce('ajax');
if (!$clickedButton.length) {
return;
}
var $clickedButton = $(this);
var $enabledFields = [];
if ($clickedButton.closest('div.js-form-managed-file').length > 0) {
@@ -126,6 +124,7 @@
setTimeout(function () {
$clickedButton.closest('div.js-form-managed-file').find('div.ajax-progress-bar').slideDown();
}, 500);
$clickedButton.trigger('fileUpload');
},
openInNewWindow: function openInNewWindow(event) {
event.preventDefault();
+28 -25
View File
@@ -8,6 +8,7 @@
use Drupal\Core\Datetime\Entity\DateFormat;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
@@ -19,6 +20,11 @@ use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Template\Attribute;
/**
* The regex pattern used when checking for insecure file types.
*/
define('FILE_INSECURE_EXTENSION_REGEX', '/\.(php|pl|py|cgi|asp|js)(\.|$)/i');
// Load all Field module hooks for File.
require_once __DIR__ . '/file.field.inc';
@@ -156,7 +162,7 @@ function file_copy(FileInterface $source, $destination = NULL, $replace = FILE_E
else {
\Drupal::logger('file')->notice('File %file could not be copied because the destination %destination is invalid. This is often caused by improper use of file_copy() or a missing stream wrapper.', ['%file' => $source->getFileUri(), '%destination' => $destination]);
}
drupal_set_message(t('The specified file %file could not be copied because the destination is invalid. More information is available in the system log.', ['%file' => $source->getFileUri()]), 'error');
\Drupal::messenger()->addError(t('The specified file %file could not be copied because the destination is invalid. More information is available in the system log.', ['%file' => $source->getFileUri()]));
return FALSE;
}
@@ -231,7 +237,7 @@ function file_move(FileInterface $source, $destination = NULL, $replace = FILE_E
else {
\Drupal::logger('file')->notice('File %file could not be moved because the destination %destination is invalid. This may be caused by improper use of file_move() or a missing stream wrapper.', ['%file' => $source->getFileUri(), '%destination' => $destination]);
}
drupal_set_message(t('The specified file %file could not be moved because the destination is invalid. More information is available in the system log.', ['%file' => $source->getFileUri()]), 'error');
\Drupal::messenger()->addError(t('The specified file %file could not be moved because the destination is invalid. More information is available in the system log.', ['%file' => $source->getFileUri()]));
return FALSE;
}
@@ -479,7 +485,7 @@ function file_validate_image_resolution(FileInterface $file, $maximum_dimensions
'%new_height' => $image->getHeight(),
]);
}
drupal_set_message($message);
\Drupal::messenger()->addStatus($message);
}
else {
$errors[] = t('The image exceeds the maximum allowed dimensions and an attempt to resize it failed.');
@@ -547,7 +553,7 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM
}
if (!file_valid_uri($destination)) {
\Drupal::logger('file')->notice('The data could not be saved because the destination %destination is invalid. This may be caused by improper use of file_save_data() or a missing stream wrapper.', ['%destination' => $destination]);
drupal_set_message(t('The data could not be saved because the destination is invalid. More information is available in the system log.'), 'error');
\Drupal::messenger()->addError(t('The data could not be saved because the destination is invalid. More information is available in the system log.'));
return FALSE;
}
@@ -761,7 +767,7 @@ function file_cron() {
function _file_save_upload_from_form(array $element, FormStateInterface $form_state, $delta = NULL, $replace = FILE_EXISTS_RENAME) {
// Get all errors set before calling this method. This will also clear them
// from $_SESSION.
$errors_before = drupal_get_messages('error');
$errors_before = \Drupal::messenger()->deleteByType(MessengerInterface::TYPE_ERROR);
$upload_location = isset($element['#upload_location']) ? $element['#upload_location'] : FALSE;
$upload_name = implode('_', $element['#parents']);
@@ -771,9 +777,8 @@ function _file_save_upload_from_form(array $element, FormStateInterface $form_st
// Get new errors that are generated while trying to save the upload. This
// will also clear them from $_SESSION.
$errors_new = drupal_get_messages('error');
if (!empty($errors_new['error'])) {
$errors_new = $errors_new['error'];
$errors_new = \Drupal::messenger()->deleteByType(MessengerInterface::TYPE_ERROR);
if (!empty($errors_new)) {
if (count($errors_new) > 1) {
// Render multiple errors into a single message.
@@ -798,9 +803,9 @@ function _file_save_upload_from_form(array $element, FormStateInterface $form_st
// Ensure that errors set prior to calling this method are still shown to the
// user.
if (!empty($errors_before['error'])) {
foreach ($errors_before['error'] as $error) {
drupal_set_message($error, 'error');
if (!empty($errors_before)) {
foreach ($errors_before as $error) {
\Drupal::messenger()->addError($error);
}
}
@@ -888,13 +893,13 @@ function file_save_upload($form_field_name, $validators = [], $destination = FAL
switch ($file_info->getError()) {
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
drupal_set_message(t('The file %file could not be saved because it exceeds %maxsize, the maximum allowed size for uploads.', ['%file' => $file_info->getFilename(), '%maxsize' => format_size(file_upload_max_size())]), 'error');
\Drupal::messenger()->addError(t('The file %file could not be saved because it exceeds %maxsize, the maximum allowed size for uploads.', ['%file' => $file_info->getFilename(), '%maxsize' => format_size(file_upload_max_size())]));
$files[$i] = FALSE;
continue;
case UPLOAD_ERR_PARTIAL:
case UPLOAD_ERR_NO_FILE:
drupal_set_message(t('The file %file could not be saved because the upload did not complete.', ['%file' => $file_info->getFilename()]), 'error');
\Drupal::messenger()->addError(t('The file %file could not be saved because the upload did not complete.', ['%file' => $file_info->getFilename()]));
$files[$i] = FALSE;
continue;
@@ -907,7 +912,7 @@ function file_save_upload($form_field_name, $validators = [], $destination = FAL
// Unknown error
default:
drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', ['%file' => $file_info->getFilename()]), 'error');
\Drupal::messenger()->addError(t('The file %file could not be saved. An unknown error has occurred.', ['%file' => $file_info->getFilename()]));
$files[$i] = FALSE;
continue;
@@ -954,7 +959,7 @@ function file_save_upload($form_field_name, $validators = [], $destination = FAL
// rename filename.php.foo and filename.php to filename.php.foo.txt and
// filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads'
// evaluates to TRUE.
if (!\Drupal::config('system.file')->get('allow_insecure_uploads') && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $file->getFilename()) && (substr($file->getFilename(), -4) != '.txt')) {
if (!\Drupal::config('system.file')->get('allow_insecure_uploads') && preg_match(FILE_INSECURE_EXTENSION_REGEX, $file->getFilename()) && (substr($file->getFilename(), -4) != '.txt')) {
$file->setMimeType('text/plain');
// The destination filename will also later be used to create the URI.
$file->setFilename($file->getFilename() . '.txt');
@@ -962,7 +967,7 @@ function file_save_upload($form_field_name, $validators = [], $destination = FAL
// to add it here or else the file upload will fail.
if (!empty($extensions)) {
$validators['file_validate_extensions'][0] .= ' txt';
drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', ['%filename' => $file->getFilename()]));
\Drupal::messenger()->addStatus(t('For security reasons, your upload has been renamed to %filename.', ['%filename' => $file->getFilename()]));
}
}
@@ -974,7 +979,7 @@ function file_save_upload($form_field_name, $validators = [], $destination = FAL
// Assert that the destination contains a valid stream.
$destination_scheme = file_uri_scheme($destination);
if (!file_stream_wrapper_valid_scheme($destination_scheme)) {
drupal_set_message(t('The file could not be uploaded because the destination %destination is invalid.', ['%destination' => $destination]), 'error');
\Drupal::messenger()->addError(t('The file could not be uploaded because the destination %destination is invalid.', ['%destination' => $destination]));
$files[$i] = FALSE;
continue;
}
@@ -988,7 +993,7 @@ function file_save_upload($form_field_name, $validators = [], $destination = FAL
// If file_destination() returns FALSE then $replace === FILE_EXISTS_ERROR and
// there's an existing file so we need to bail.
if ($file->destination === FALSE) {
drupal_set_message(t('The file %source could not be uploaded because a file by that name already exists in the destination %directory.', ['%source' => $form_field_name, '%directory' => $destination]), 'error');
\Drupal::messenger()->addError(t('The file %source could not be uploaded because a file by that name already exists in the destination %directory.', ['%source' => $form_field_name, '%directory' => $destination]));
$files[$i] = FALSE;
continue;
}
@@ -1010,19 +1015,17 @@ function file_save_upload($form_field_name, $validators = [], $destination = FAL
'#items' => $errors,
],
];
// @todo Add support for render arrays in drupal_set_message()? See
// https://www.drupal.org/node/2505497.
drupal_set_message(\Drupal::service('renderer')->renderPlain($message), 'error');
// @todo Add support for render arrays in
// \Drupal\Core\Messenger\MessengerInterface::addMessage()?
// @see https://www.drupal.org/node/2505497.
\Drupal::messenger()->addError(\Drupal::service('renderer')->renderPlain($message));
$files[$i] = FALSE;
continue;
}
// Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary
// directory. This overcomes open_basedir restrictions for future file
// operations.
$file->setFileUri($file->destination);
if (!drupal_move_uploaded_file($file_info->getRealPath(), $file->getFileUri())) {
drupal_set_message(t('File upload error. Could not move uploaded file.'), 'error');
\Drupal::messenger()->addError(t('File upload error. Could not move uploaded file.'));
\Drupal::logger('file')->notice('Upload error. Could not move uploaded file %file to destination %destination.', ['%file' => $file->getFilename(), '%destination' => $file->getFileUri()]);
$files[$i] = FALSE;
continue;
@@ -299,6 +299,10 @@ class ManagedFile extends FormElement {
// Add the upload progress callback.
$element['upload_button']['#ajax']['progress']['url'] = Url::fromRoute('file.ajax_progress', ['key' => $upload_progress_key]);
// Set a custom submit event so we can modify the upload progress
// identifier element before the form gets submitted.
$element['upload_button']['#ajax']['event'] = 'fileUpload';
}
// The file upload field itself.
+7
View File
@@ -18,6 +18,13 @@ use Drupal\user\UserInterface;
* @ContentEntityType(
* id = "file",
* label = @Translation("File"),
* label_collection = @Translation("Files"),
* label_singular = @Translation("file"),
* label_plural = @Translation("files"),
* label_count = @PluralTranslation(
* singular = "@count file",
* plural = "@count files",
* ),
* handlers = {
* "storage" = "Drupal\file\FileStorage",
* "storage_schema" = "Drupal\file\FileStorageSchema",
@@ -52,11 +52,11 @@ class FileAccessControlHandler extends EntityAccessControlHandler {
// services can be more properly injected.
$allowed_fids = \Drupal::service('session')->get('anonymous_allowed_file_ids', []);
if (!empty($allowed_fids[$entity->id()])) {
return AccessResult::allowed();
return AccessResult::allowed()->addCacheContexts(['session', 'user']);
}
}
else {
return AccessResult::allowed();
return AccessResult::allowed()->addCacheContexts(['user']);
}
}
}
@@ -64,11 +64,11 @@ class FileAccessControlHandler extends EntityAccessControlHandler {
if ($operation == 'delete' || $operation == 'update') {
$account = $this->prepareUser($account);
$file_uid = $entity->get('uid')->getValue();
// Only the file owner can delete and update the file entity.
// Only the file owner can update or delete the file entity.
if ($account->id() == $file_uid[0]['target_id']) {
return AccessResult::allowed();
}
return AccessResult::forbidden();
return AccessResult::forbidden('Only the file owner can update or delete the file entity.');
}
// No opinion.
@@ -127,8 +127,6 @@ class FileAccessControlHandler extends EntityAccessControlHandler {
// create file entities that are referenced from another entity
// (e.g. an image for a article). A contributed module is free to alter
// this to allow file entities to be created directly.
// @todo Update comment to mention REST module when
// https://www.drupal.org/node/1927648 is fixed.
return AccessResult::neutral();
}
@@ -0,0 +1,23 @@
<?php
namespace Drupal\file;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceModifierInterface;
use Drupal\Core\StackMiddleware\NegotiationMiddleware;
/**
* Adds 'application/octet-stream' as a known (bin) format.
*/
class FileServiceProvider implements ServiceModifierInterface {
/**
* {@inheritdoc}
*/
public function alter(ContainerBuilder $container) {
if ($container->has('http_middleware.negotiation') && is_a($container->getDefinition('http_middleware.negotiation')->getClass(), NegotiationMiddleware::class, TRUE)) {
$container->getDefinition('http_middleware.negotiation')->addMethodCall('registerFormat', ['bin', ['application/octet-stream']]);
}
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ class FileStorageSchema extends SqlContentEntityStorageSchema {
$schema = parent::getSharedTableFieldSchema($storage_definition, $table_name, $column_mapping);
$field_name = $storage_definition->getName();
if ($table_name == 'file_managed') {
if ($table_name == $this->storage->getBaseTable()) {
switch ($field_name) {
case 'status':
case 'changed':
+1 -1
View File
@@ -65,7 +65,7 @@ class FileViewsData extends EntityViewsData {
$data['file_managed']['uid']['relationship']['title'] = $this->t('User who uploaded');
$data['file_managed']['uid']['relationship']['label'] = $this->t('User who uploaded');
$data['file_usage']['table']['group'] = $this->t('File Usage');
$data['file_usage']['table']['group'] = $this->t('File Usage');
// Provide field-type-things to several base tables; on the core files table
// ("file_managed") so that we can create relationships from files to
@@ -365,7 +365,7 @@ class FileWidget extends WidgetBase implements ContainerFactoryPluginInterface {
'%list' => implode(', ', $removed_names),
];
$message = t('Field %field can only hold @max values but there were @count uploaded. The following files have been omitted as a result: %list.', $args);
drupal_set_message($message, 'warning');
\Drupal::messenger()->addWarning($message);
$values['fids'] = array_slice($values['fids'], 0, $keep);
NestedArray::setValue($form_state->getValues(), $element['#parents'], $values);
}
@@ -15,7 +15,12 @@ class FileValidationConstraintValidator extends ConstraintValidator {
*/
public function validate($value, Constraint $constraint) {
// Get the file to execute validators.
$file = $value->get('entity')->getTarget()->getValue();
$target = $value->get('entity')->getTarget();
if (!$target) {
return;
}
$file = $target->getValue();
// Get the validators.
$validators = $value->getUploadValidators();
// Checks that a file meets the criteria specified by the validators.
@@ -2,7 +2,6 @@
namespace Drupal\file\Plugin\migrate\destination;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Field\Plugin\Field\FieldType\UriItem;
use Drupal\migrate\Row;
use Drupal\migrate\MigrateException;
@@ -58,7 +57,7 @@ class EntityFile extends EntityContentBase {
// Make it into a proper public file uri, stripping off the existing
// scheme if present.
$value = 'public://' . preg_replace('|^[a-z]+://|i', '', $value);
$value = Unicode::substr($value, 0, $field_definitions['uri']->getSetting('max_length'));
$value = mb_substr($value, 0, $field_definitions['uri']->getSetting('max_length'));
// Create a real file, so File::preSave() can do filesize() on it.
touch($value);
$row->setDestinationProperty('uri', $value);
@@ -42,7 +42,7 @@ class FileField extends FieldPluginBase {
/**
* {@inheritdoc}
*/
public function processFieldValues(MigrationInterface $migration, $field_name, $data) {
public function defineValueProcessPipeline(MigrationInterface $migration, $field_name, $data) {
$process = [
'plugin' => 'd6_field_file',
'source' => $field_name,
@@ -18,7 +18,7 @@ class FileField extends D6FileField {
/**
* {@inheritdoc}
*/
public function processFieldValues(MigrationInterface $migration, $field_name, $data) {
public function defineValueProcessPipeline(MigrationInterface $migration, $field_name, $data) {
$process = [
'plugin' => 'sub_process',
'source' => $field_name,
@@ -42,6 +42,7 @@ class File extends DrupalSqlBase {
public function query() {
return $this->select('files', 'f')
->fields('f')
->condition('filepath', '/tmp%', 'NOT LIKE')
->orderBy('timestamp')
// If two or more files have the same timestamp, they'll end up in a
// non-deterministic order. Ordering by fid (or any other unique field)
@@ -88,6 +89,7 @@ class File extends DrupalSqlBase {
'is_public' => $this->t('TRUE if the files directory is public otherwise FALSE.'),
];
}
/**
* {@inheritdoc}
*/
@@ -43,18 +43,21 @@ class File extends DrupalSqlBase {
public function query() {
$query = $this->select('file_managed', 'f')
->fields('f')
->condition('uri', 'temporary://%', 'NOT LIKE')
->orderBy('f.timestamp');
// Filter by scheme(s), if configured.
if (isset($this->configuration['scheme'])) {
$schemes = [];
// Remove 'temporary' scheme.
$valid_schemes = array_diff((array) $this->configuration['scheme'], ['temporary']);
// Accept either a single scheme, or a list.
foreach ((array) $this->configuration['scheme'] as $scheme) {
foreach ((array) $valid_schemes as $scheme) {
$schemes[] = rtrim($scheme) . '://';
}
$schemes = array_map([$this->getDatabase(), 'escapeLike'], $schemes);
// uri LIKE 'public://%' OR uri LIKE 'private://%'
// Add conditions, uri LIKE 'public://%' OR uri LIKE 'private://%'.
$conditions = new Condition('OR');
foreach ($schemes as $scheme) {
$conditions->condition('uri', $scheme . '%', 'LIKE');
@@ -0,0 +1,586 @@
<?php
namespace Drupal\file\Plugin\rest\resource;
use Drupal\Component\Utility\Bytes;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Config\Config;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Utility\Token;
use Drupal\file\FileInterface;
use Drupal\rest\ModifiedResourceResponse;
use Drupal\rest\Plugin\ResourceBase;
use Drupal\Component\Render\PlainTextOutput;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\file\Entity\File;
use Drupal\rest\Plugin\rest\resource\EntityResourceValidationTrait;
use Drupal\rest\RequestHandler;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
use Symfony\Component\Routing\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\HttpException;
/**
* File upload resource.
*
* This is implemented as a field-level resource for the following reasons:
* - Validation for uploaded files is tied to fields (allowed extensions, max
* size, etc..).
* - The actual files do not need to be stored in another temporary location,
* to be later moved when they are referenced from a file field.
* - Permission to upload a file can be determined by a users field level
* create access to the file field.
*
* @RestResource(
* id = "file:upload",
* label = @Translation("File Upload"),
* serialization_class = "Drupal\file\Entity\File",
* uri_paths = {
* "https://www.drupal.org/link-relations/create" = "/file/upload/{entity_type_id}/{bundle}/{field_name}"
* }
* )
*/
class FileUploadResource extends ResourceBase {
use EntityResourceValidationTrait {
validate as resourceValidate;
}
/**
* The regex used to extract the filename from the content disposition header.
*
* @var string
*/
const REQUEST_HEADER_FILENAME_REGEX = '@\bfilename(?<star>\*?)=\"(?<filename>.+)\"@';
/**
* The amount of bytes to read in each iteration when streaming file data.
*
* @var int
*/
const BYTES_TO_READ = 8192;
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The entity field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $entityFieldManager;
/**
* The currently authenticated user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* The MIME type guesser.
*
* @var \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface
*/
protected $mimeTypeGuesser;
/**
* The token replacement instance.
*
* @var \Drupal\Core\Utility\Token
*/
protected $token;
/**
* The lock service.
*
* @var \Drupal\Core\Lock\LockBackendInterface
*/
protected $lock;
/**
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $systemFileConfig;
/**
* Constructs a FileUploadResource instance.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param array $serializer_formats
* The available serialization formats.
* @param \Psr\Log\LoggerInterface $logger
* A logger instance.
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file system service.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
* The entity field manager.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The currently authenticated user.
* @param \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface $mime_type_guesser
* The MIME type guesser.
* @param \Drupal\Core\Utility\Token $token
* The token replacement instance.
* @param \Drupal\Core\Lock\LockBackendInterface $lock
* The lock service.
* @param \Drupal\Core\Config\Config $system_file_config
* The system file configuration.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, $serializer_formats, LoggerInterface $logger, FileSystemInterface $file_system, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, AccountInterface $current_user, MimeTypeGuesserInterface $mime_type_guesser, Token $token, LockBackendInterface $lock, Config $system_file_config) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);
$this->fileSystem = $file_system;
$this->entityTypeManager = $entity_type_manager;
$this->entityFieldManager = $entity_field_manager;
$this->currentUser = $current_user;
$this->mimeTypeGuesser = $mime_type_guesser;
$this->token = $token;
$this->lock = $lock;
$this->systemFileConfig = $system_file_config;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->getParameter('serializer.formats'),
$container->get('logger.factory')->get('rest'),
$container->get('file_system'),
$container->get('entity_type.manager'),
$container->get('entity_field.manager'),
$container->get('current_user'),
$container->get('file.mime_type.guesser'),
$container->get('token'),
$container->get('lock'),
$container->get('config.factory')->get('system.file')
);
}
/**
* {@inheritdoc}
*/
public function permissions() {
// Access to this resource depends on field-level access so no explicit
// permissions are required.
// @see \Drupal\file\Plugin\rest\resource\FileUploadResource::validateAndLoadFieldDefinition()
// @see \Drupal\rest\Plugin\rest\resource\EntityResource::permissions()
return [];
}
/**
* Creates a file from an endpoint.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
* @param string $entity_type_id
* The entity type ID.
* @param string $bundle
* The entity bundle. This will be the same as $entity_type_id for entity
* types that don't support bundles.
* @param string $field_name
* The field name.
*
* @return \Drupal\rest\ModifiedResourceResponse
* A 201 response, on success.
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
* Thrown when temporary files cannot be written, a lock cannot be acquired,
* or when temporary files cannot be moved to their new location.
*/
public function post(Request $request, $entity_type_id, $bundle, $field_name) {
$filename = $this->validateAndParseContentDispositionHeader($request);
$field_definition = $this->validateAndLoadFieldDefinition($entity_type_id, $bundle, $field_name);
$destination = $this->getUploadLocation($field_definition->getSettings());
// Check the destination file path is writable.
if (!file_prepare_directory($destination, FILE_CREATE_DIRECTORY)) {
throw new HttpException(500, 'Destination file path is not writable');
}
$validators = $this->getUploadValidators($field_definition);
$prepared_filename = $this->prepareFilename($filename, $validators);
// Create the file.
$file_uri = "{$destination}/{$prepared_filename}";
$temp_file_path = $this->streamUploadData();
// This will take care of altering $file_uri if a file already exists.
file_unmanaged_prepare($temp_file_path, $file_uri);
// Lock based on the prepared file URI.
$lock_id = $this->generateLockIdFromFileUri($file_uri);
if (!$this->lock->acquire($lock_id)) {
throw new HttpException(503, sprintf('File "%s" is already locked for writing'), NULL, ['Retry-After' => 1]);
}
// Begin building file entity.
$file = File::create([]);
$file->setOwnerId($this->currentUser->id());
$file->setFilename($prepared_filename);
$file->setMimeType($this->mimeTypeGuesser->guess($prepared_filename));
$file->setFileUri($file_uri);
// Set the size. This is done in File::preSave() but we validate the file
// before it is saved.
$file->setSize(@filesize($temp_file_path));
// Validate the file entity against entity-level validation and field-level
// validators.
$this->validate($file, $validators);
// Move the file to the correct location after validation. Use
// FILE_EXISTS_ERROR as the file location has already been determined above
// in file_unmanaged_prepare().
if (!file_unmanaged_move($temp_file_path, $file_uri, FILE_EXISTS_ERROR)) {
throw new HttpException(500, 'Temporary file could not be moved to file location');
}
$file->save();
$this->lock->release($lock_id);
// 201 Created responses return the newly created entity in the response
// body. These responses are not cacheable, so we add no cacheability
// metadata here.
return new ModifiedResourceResponse($file, 201);
}
/**
* Streams file upload data to temporary file and moves to file destination.
*
* @return string
* The temp file path.
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
* Thrown when input data cannot be read, the temporary file cannot be
* opened, or the temporary file cannot be written.
*/
protected function streamUploadData() {
// 'rb' is needed so reading works correctly on Windows environments too.
$file_data = fopen('php://input', 'rb');
$temp_file_path = $this->fileSystem->tempnam('temporary://', 'file');
$temp_file = fopen($temp_file_path, 'wb');
if ($temp_file) {
while (!feof($file_data)) {
$read = fread($file_data, static::BYTES_TO_READ);
if ($read === FALSE) {
// Close the file streams.
fclose($temp_file);
fclose($file_data);
$this->logger->error('Input data could not be read');
throw new HttpException(500, 'Input file data could not be read');
}
if (fwrite($temp_file, $read) === FALSE) {
// Close the file streams.
fclose($temp_file);
fclose($file_data);
$this->logger->error('Temporary file data for "%path" could not be written', ['%path' => $temp_file_path]);
throw new HttpException(500, 'Temporary file data could not be written');
}
}
// Close the temp file stream.
fclose($temp_file);
}
else {
// Close the file streams.
fclose($temp_file);
fclose($file_data);
$this->logger->error('Temporary file "%path" could not be opened for file upload', ['%path' => $temp_file_path]);
throw new HttpException(500, 'Temporary file could not be opened');
}
// Close the input stream.
fclose($file_data);
return $temp_file_path;
}
/**
* Validates and extracts the filename from the Content-Disposition header.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
*
* @return string
* The filename extracted from the header.
*
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
* Thrown when the 'Content-Disposition' request header is invalid.
*/
protected function validateAndParseContentDispositionHeader(Request $request) {
// Firstly, check the header exists.
if (!$request->headers->has('content-disposition')) {
throw new BadRequestHttpException('"Content-Disposition" header is required. A file name in the format "filename=FILENAME" must be provided');
}
$content_disposition = $request->headers->get('content-disposition');
// Parse the header value. This regex does not allow an empty filename.
// i.e. 'filename=""'. This also matches on a word boundary so other keys
// like 'not_a_filename' don't work.
if (!preg_match(static::REQUEST_HEADER_FILENAME_REGEX, $content_disposition, $matches)) {
throw new BadRequestHttpException('No filename found in "Content-Disposition" header. A file name in the format "filename=FILENAME" must be provided');
}
// Check for the "filename*" format. This is currently unsupported.
if (!empty($matches['star'])) {
throw new BadRequestHttpException('The extended "filename*" format is currently not supported in the "Content-Disposition" header');
}
// Don't validate the actual filename here, that will be done by the upload
// validators in validate().
// @see \Drupal\file\Plugin\rest\resource\FileUploadResource::validate()
$filename = $matches['filename'];
// Make sure only the filename component is returned. Path information is
// stripped as per https://tools.ietf.org/html/rfc6266#section-4.3.
return basename($filename);
}
/**
* Validates and loads a field definition instance.
*
* @param string $entity_type_id
* The entity type ID the field is attached to.
* @param string $bundle
* The bundle the field is attached to.
* @param string $field_name
* The field name.
*
* @return \Drupal\Core\Field\FieldDefinitionInterface
* The field definition.
*
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
* Thrown when the field does not exist.
* @throws \Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException
* Thrown when the target type of the field is not a file, or the current
* user does not have 'edit' access for the field.
*/
protected function validateAndLoadFieldDefinition($entity_type_id, $bundle, $field_name) {
$field_definitions = $this->entityFieldManager->getFieldDefinitions($entity_type_id, $bundle);
if (!isset($field_definitions[$field_name])) {
throw new NotFoundHttpException(sprintf('Field "%s" does not exist', $field_name));
}
/** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
$field_definition = $field_definitions[$field_name];
if ($field_definition->getSetting('target_type') !== 'file') {
throw new AccessDeniedHttpException(sprintf('"%s" is not a file field', $field_name));
}
$entity_access_control_handler = $this->entityTypeManager->getAccessControlHandler($entity_type_id);
$bundle = $this->entityTypeManager->getDefinition($entity_type_id)->hasKey('bundle') ? $bundle : NULL;
$access_result = $entity_access_control_handler->createAccess($bundle, NULL, [], TRUE)
->andIf($entity_access_control_handler->fieldAccess('edit', $field_definition, NULL, NULL, TRUE));
if (!$access_result->isAllowed()) {
throw new AccessDeniedHttpException($access_result->getReason());
}
return $field_definition;
}
/**
* Validates the file.
*
* @param \Drupal\file\FileInterface $file
* The file entity to validate.
* @param array $validators
* An array of upload validators to pass to file_validate().
*
* @throws \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException
* Thrown when there are file validation errors.
*/
protected function validate(FileInterface $file, array $validators) {
$this->resourceValidate($file);
// Validate the file based on the field definition configuration.
$errors = file_validate($file, $validators);
if (!empty($errors)) {
$message = "Unprocessable Entity: file validation failed.\n";
$message .= implode("\n", array_map(function ($error) {
return PlainTextOutput::renderFromHtml($error);
}, $errors));
throw new UnprocessableEntityHttpException($message);
}
}
/**
* Prepares the filename to strip out any malicious extensions.
*
* @param string $filename
* The file name.
* @param array $validators
* The array of upload validators.
*
* @return string
* The prepared/munged filename.
*/
protected function prepareFilename($filename, array &$validators) {
if (!empty($validators['file_validate_extensions'][0])) {
// If there is a file_validate_extensions validator and a list of
// valid extensions, munge the filename to protect against possible
// malicious extension hiding within an unknown file type. For example,
// "filename.html.foo".
$filename = file_munge_filename($filename, $validators['file_validate_extensions'][0]);
}
// Rename potentially executable files, to help prevent exploits (i.e. will
// rename filename.php.foo and filename.php to filename.php.foo.txt and
// filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads'
// evaluates to TRUE.
if (!$this->systemFileConfig->get('allow_insecure_uploads') && preg_match(FILE_INSECURE_EXTENSION_REGEX, $filename) && (substr($filename, -4) != '.txt')) {
// The destination filename will also later be used to create the URI.
$filename .= '.txt';
// The .txt extension may not be in the allowed list of extensions. We
// have to add it here or else the file upload will fail.
if (!empty($validators['file_validate_extensions'][0])) {
$validators['file_validate_extensions'][0] .= ' txt';
}
}
return $filename;
}
/**
* Determines the URI for a file field.
*
* @param array $settings
* The array of field settings.
*
* @return string
* An un-sanitized file directory URI with tokens replaced. The result of
* the token replacement is then converted to plain text and returned.
*/
protected function getUploadLocation(array $settings) {
$destination = trim($settings['file_directory'], '/');
// Replace tokens. As the tokens might contain HTML we convert it to plain
// text.
$destination = PlainTextOutput::renderFromHtml($this->token->replace($destination, []));
return $settings['uri_scheme'] . '://' . $destination;
}
/**
* Retrieves the upload validators for a field definition.
*
* This is copied from \Drupal\file\Plugin\Field\FieldType\FileItem as there
* is no entity instance available here that that a FileItem would exist for.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition for which to get validators.
*
* @return array
* An array suitable for passing to file_save_upload() or the file field
* element's '#upload_validators' property.
*/
protected function getUploadValidators(FieldDefinitionInterface $field_definition) {
$validators = [
// Add in our check of the file name length.
'file_validate_name_length' => [],
];
$settings = $field_definition->getSettings();
// Cap the upload size according to the PHP limit.
$max_filesize = Bytes::toInt(file_upload_max_size());
if (!empty($settings['max_filesize'])) {
$max_filesize = min($max_filesize, Bytes::toInt($settings['max_filesize']));
}
// There is always a file size limit due to the PHP server limit.
$validators['file_validate_size'] = [$max_filesize];
// Add the extension check if necessary.
if (!empty($settings['file_extensions'])) {
$validators['file_validate_extensions'] = [$settings['file_extensions']];
}
return $validators;
}
/**
* {@inheritdoc}
*/
protected function getBaseRoute($canonical_path, $method) {
return new Route($canonical_path, [
'_controller' => RequestHandler::class . '::handleRaw',
],
$this->getBaseRouteRequirements($method),
[],
'',
[],
// The HTTP method is a requirement for this route.
[$method]
);
}
/**
* {@inheritdoc}
*/
protected function getBaseRouteRequirements($method) {
$requirements = parent::getBaseRouteRequirements($method);
// Add the content type format access check. This will enforce that all
// incoming requests can only use the 'application/octet-stream'
// Content-Type header.
$requirements['_content_type_format'] = 'bin';
return $requirements;
}
/**
* Generates a lock ID based on the file URI.
*
* @param $file_uri
* The file URI.
*
* @return string
* The generated lock ID.
*/
protected static function generateLockIdFromFileUri($file_uri) {
return 'file:rest:' . Crypt::hashBase64($file_uri);
}
}
@@ -2,6 +2,8 @@
namespace Drupal\file\Tests;
@trigger_error('The ' . __NAMESPACE__ . '\FileFieldTestBase is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. Instead, use \Drupal\Tests\file\Functional\FileFieldTestBase. See https://www.drupal.org/node/2969361.', E_USER_DEPRECATED);
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Entity\FieldConfig;
use Drupal\file\FileInterface;
@@ -4,7 +4,6 @@ namespace Drupal\file\Tests;
use Drupal\comment\Entity\Comment;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Url;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
@@ -270,7 +269,7 @@ class FileFieldWidgetTest extends FileFieldTestBase {
// Try to upload exactly the allowed number of files on revision. Create an
// empty node first, to fill it in its first revision.
$node = $this->drupalCreateNode([
'type' => $type_name
'type' => $type_name,
]);
$this->uploadNodeFile($test_file, $field_name, $node->id(), 1);
$node_storage->resetCache([$nid]);
@@ -461,7 +460,7 @@ class FileFieldWidgetTest extends FileFieldTestBase {
* Tests file widget element.
*/
public function testWidgetElement() {
$field_name = Unicode::strtolower($this->randomMachineName());
$field_name = mb_strtolower($this->randomMachineName());
$html_name = str_replace('_', '-', $field_name);
$this->createFileField($field_name, 'node', 'article', ['cardinality' => FieldStorageConfig::CARDINALITY_UNLIMITED]);
$file = $this->getTestFile('text');
@@ -10,6 +10,7 @@ namespace Drupal\file\Tests;
* that aren't related to fields into it.
*/
class FileManagedFileElementTest extends FileFieldTestBase {
/**
* Tests the managed_file element type.
*/
@@ -2,6 +2,8 @@
namespace Drupal\file\Tests;
@trigger_error('The ' . __NAMESPACE__ . '\FileManagedTestBase is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. Instead, use \Drupal\Tests\file\Functional\FileManagedTestBase. See https://www.drupal.org/node/2969361.', E_USER_DEPRECATED);
use Drupal\file\Entity\File;
use Drupal\file\FileInterface;
use Drupal\simpletest\WebTestBase;
@@ -85,7 +85,7 @@ class FileModuleTestForm extends FormBase {
$fids[] = $fid;
}
drupal_set_message($this->t('The file ids are %fids.', ['%fids' => implode(',', $fids)]));
\Drupal::messenger()->addStatus($this->t('The file ids are %fids.', ['%fids' => implode(',', $fids)]));
}
}
@@ -108,13 +108,13 @@ class FileTestForm implements FormInterface {
$file = file_save_upload('file_test_upload', $validators, $destination, 0, $form_state->getValue('file_test_replace'));
if ($file) {
$form_state->setValue('file_test_upload', $file);
drupal_set_message(t('File @filepath was uploaded.', ['@filepath' => $file->getFileUri()]));
drupal_set_message(t('File name is @filename.', ['@filename' => $file->getFilename()]));
drupal_set_message(t('File MIME type is @mimetype.', ['@mimetype' => $file->getMimeType()]));
drupal_set_message(t('You WIN!'));
\Drupal::messenger()->addStatus(t('File @filepath was uploaded.', ['@filepath' => $file->getFileUri()]));
\Drupal::messenger()->addStatus(t('File name is @filename.', ['@filename' => $file->getFilename()]));
\Drupal::messenger()->addStatus(t('File MIME type is @mimetype.', ['@mimetype' => $file->getMimeType()]));
\Drupal::messenger()->addStatus(t('You WIN!'));
}
elseif ($file === FALSE) {
drupal_set_message(t('Epic upload FAIL!'), 'error');
\Drupal::messenger()->addError(t('Epic upload FAIL!'));
}
}
@@ -49,7 +49,7 @@ $connection->update('key_value')
->condition('collection', 'entity.definitions.installed')
->condition('name', 'node.field_storage_definitions')
->fields([
'value' => serialize($installed)
'value' => serialize($installed),
])
->execute();
@@ -5,5 +5,5 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- file
- views
- drupal:file
- drupal:views
@@ -1,6 +1,6 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
/**
* Tests for download/file transfer functions.
@@ -8,6 +8,7 @@ namespace Drupal\file\Tests;
* @group file
*/
class DownloadTest extends FileManagedTestBase {
protected function setUp() {
parent::setUp();
// Clear out any hook calls.
@@ -25,16 +26,17 @@ class DownloadTest extends FileManagedTestBase {
// encoded.
$filename = $GLOBALS['base_url'] . '/' . \Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath() . '/' . rawurlencode($file->getFilename());
$this->assertEqual($filename, $url, 'Correctly generated a URL for a created file.');
$this->drupalHead($url);
$this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the created file.');
$http_client = $this->getHttpClient();
$response = $http_client->head($url);
$this->assertEquals(200, $response->getStatusCode(), 'Confirmed that the generated URL is correct by downloading the created file.');
// Test generating a URL to a shipped file (i.e. a file that is part of
// Drupal core, a module or a theme, for example a JavaScript file).
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
$url = file_create_url($filepath);
$this->assertEqual($GLOBALS['base_url'] . '/' . $filepath, $url, 'Correctly generated a URL for a shipped file.');
$this->drupalHead($url);
$this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.');
$response = $http_client->head($url);
$this->assertEquals(200, $response->getStatusCode(), 'Confirmed that the generated URL is correct by downloading the shipped file.');
}
/**
@@ -60,7 +62,7 @@ class DownloadTest extends FileManagedTestBase {
$file->setPermanent();
$file->save();
$url = file_create_url($file->getFileUri());
$url = file_create_url($file->getFileUri());
// Set file_test access header to allow the download.
file_test_set_return('download', ['x-foo' => 'Bar']);
@@ -70,17 +72,18 @@ class DownloadTest extends FileManagedTestBase {
$this->assertResponse(200, 'Correctly allowed access to a file when file_test provides headers.');
// Test that the file transferred correctly.
$this->assertEqual($contents, $this->content, 'Contents of the file are correct.');
$this->assertSame($contents, $this->getSession()->getPage()->getContent(), 'Contents of the file are correct.');
$http_client = $this->getHttpClient();
// Deny access to all downloads via a -1 header.
file_test_set_return('download', -1);
$this->drupalHead($url);
$this->assertResponse(403, 'Correctly denied access to a file when file_test sets the header to -1.');
$response = $http_client->head($url, ['http_errors' => FALSE]);
$this->assertSame(403, $response->getStatusCode(), 'Correctly denied access to a file when file_test sets the header to -1.');
// Try non-existent file.
$url = file_create_url('private://' . $this->randomMachineName());
$this->drupalHead($url);
$this->assertResponse(404, 'Correctly returned 404 response for a non-existent file.');
$response = $http_client->head($url, ['http_errors' => FALSE]);
$this->assertSame(404, $response->getStatusCode(), 'Correctly returned 404 response for a non-existent file.');
}
/**
@@ -1,6 +1,6 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
use Drupal\node\Entity\Node;
use Drupal\user\RoleInterface;
@@ -1,6 +1,6 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\file\Entity\File;
@@ -53,8 +53,7 @@ class FileFieldDisplayTest extends FileFieldTestBase {
$this->assertNoText($field_name, format_string('Field label is hidden when no file attached for formatter %formatter', ['%formatter' => $formatter]));
}
$test_file = $this->getTestFile('text');
simpletest_generate_file('escaped-&-text', 64, 10, 'text');
$this->generateFile('escaped-&-text', 64, 10, 'text');
$test_file = File::create([
'uri' => 'public://escaped-&-text.txt',
'name' => 'escaped-&-text',
@@ -97,15 +96,17 @@ class FileFieldDisplayTest extends FileFieldTestBase {
// Test that fields appear as expected after during the preview.
// Add a second file.
$name = 'files[' . $field_name . '_1][]';
$edit[$name] = \Drupal::service('file_system')->realpath($test_file->getFileUri());
$edit_upload[$name] = \Drupal::service('file_system')->realpath($test_file->getFileUri());
$this->drupalPostForm("node/$nid/edit", $edit_upload, t('Upload'));
// Uncheck the display checkboxes and go to the preview.
$edit[$field_name . '[0][display]'] = FALSE;
$edit[$field_name . '[1][display]'] = FALSE;
$this->drupalPostForm("node/$nid/edit", $edit, t('Preview'));
$this->drupalPostForm(NULL, $edit, t('Preview'));
$this->clickLink(t('Back to content editing'));
$this->assertRaw($field_name . '[0][display]', 'First file appears as expected.');
$this->assertRaw($field_name . '[1][display]', 'Second file appears as expected.');
$this->assertSession()->responseContains($field_name . '[1][description]', 'Description of second file appears as expected.');
}
/**
@@ -1,6 +1,6 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
/**
* Tests file formatter access.
@@ -1,6 +1,6 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
use Drupal\file\Entity\File;
@@ -10,6 +10,7 @@ use Drupal\file\Entity\File;
* @group file
*/
class FileFieldPathTest extends FileFieldTestBase {
/**
* Tests the normal formatter display on node display.
*/
@@ -1,6 +1,6 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
use Drupal\file\Entity\File;
@@ -60,12 +60,12 @@ class FileFieldRSSContentTest extends FileFieldTestBase {
$this->drupalGet('rss.xml');
$uploaded_filename = str_replace('public://', '', $node_file->getFileUri());
$selector = sprintf(
'enclosure[url="%s"][length="%s"][type="%s"]',
'enclosure[@url="%s"][@length="%s"][@type="%s"]',
file_create_url("public://$uploaded_filename", ['absolute' => TRUE]),
$node_file->getSize(),
$node_file->getMimeType()
);
$this->assertTrue(!empty($this->cssSelect($selector)), 'File field RSS enclosure is displayed when viewing the RSS feed.');
$this->assertNotNull($this->getSession()->getDriver()->find('xpath', $selector), 'File field RSS enclosure is displayed when viewing the RSS feed.');
}
}
@@ -1,6 +1,6 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
use Drupal\file\Entity\File;
@@ -10,6 +10,7 @@ use Drupal\file\Entity\File;
* @group file
*/
class FileFieldRevisionTest extends FileFieldTestBase {
/**
* Tests creating multiple revisions of a node and managing attached files.
*
@@ -7,6 +7,7 @@ use Drupal\field\Entity\FieldConfig;
use Drupal\file\FileInterface;
use Drupal\Tests\BrowserTestBase;
use Drupal\file\Entity\File;
use Drupal\Tests\TestFileCreationTrait;
/**
* Provides methods specifically for testing File module's field handling.
@@ -14,6 +15,9 @@ use Drupal\file\Entity\File;
abstract class FileFieldTestBase extends BrowserTestBase {
use FileFieldCreationTrait;
use TestFileCreationTrait {
getTestFiles as drupalGetTestFiles;
}
/**
* Modules to enable.
@@ -156,7 +160,7 @@ abstract class FileFieldTestBase extends BrowserTestBase {
$edit[$name][] = $file_path;
}
}
$this->drupalPostForm("node/$nid/edit", $edit, t('Save and keep published'));
$this->drupalPostForm("node/$nid/edit", $edit, t('Save'));
return $nid;
}
@@ -172,7 +176,7 @@ abstract class FileFieldTestBase extends BrowserTestBase {
];
$this->drupalPostForm('node/' . $nid . '/edit', [], t('Remove'));
$this->drupalPostForm(NULL, $edit, t('Save and keep published'));
$this->drupalPostForm(NULL, $edit, t('Save'));
}
/**
@@ -185,7 +189,7 @@ abstract class FileFieldTestBase extends BrowserTestBase {
];
$this->drupalPostForm('node/' . $nid . '/edit', [], t('Remove'));
$this->drupalPostForm(NULL, $edit, t('Save and keep published'));
$this->drupalPostForm(NULL, $edit, t('Save'));
}
/**
@@ -1,6 +1,6 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldConfig;
@@ -187,25 +187,4 @@ class FileFieldValidateTest extends FileFieldTestBase {
$this->assertText('Article ' . $node->getTitle() . ' has been updated.');
}
/**
* Test the validation message is displayed only once for ajax uploads.
*/
public function testAJAXValidationMessage() {
$field_name = strtolower($this->randomMachineName());
$this->createFileField($field_name, 'node', 'article');
$this->drupalGet('node/add/article');
/** @var \Drupal\file\FileInterface $image_file */
$image_file = $this->getTestFile('image');
$edit = [
'files[' . $field_name . '_0]' => $this->container->get('file_system')->realpath($image_file->getFileUri()),
'title[0][value]' => $this->randomMachineName(),
];
$this->drupalPostAjaxForm(NULL, $edit, $field_name . '_0_upload_button');
$elements = $this->xpath('//div[contains(@class, :class)]', [
':class' => 'messages--error',
]);
$this->assertEqual(count($elements), 1, 'Ajax validation messages are displayed once.');
}
}
@@ -1,6 +1,6 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
use Drupal\node\Entity\Node;
use Drupal\file\Entity\File;
@@ -110,7 +110,7 @@ class FileListingTest extends FileFieldTestBase {
$this->assertLinkByHref(file_create_url($file->getFileUri()));
$this->assertLinkByHref('admin/content/files/usage/' . $file->id());
}
$this->assertFalse(preg_match('/views-field-status priority-low\">\s*' . t('Temporary') . '/', $this->getRawContent()), 'All files are stored as permanent.');
$this->assertFalse(preg_match('/views-field-status priority-low\">\s*' . t('Temporary') . '/', $this->getSession()->getPage()->getContent()), 'All files are stored as permanent.');
// Use one file two times and check usage information.
$orphaned_file = $nodes[1]->file->target_id;
@@ -127,7 +127,7 @@ class FileListingTest extends FileFieldTestBase {
$usage = $this->sumUsages($file_usage->listUsage($file));
$this->assertRaw('admin/content/files/usage/' . $file->id() . '">' . $usage);
$result = $this->xpath("//td[contains(@class, 'views-field-status') and contains(text(), :value)]", [':value' => t('Temporary')]);
$result = $this->xpath("//td[contains(@class, 'views-field-status') and contains(text(), :value)]", [':value' => 'Temporary']);
$this->assertEqual(1, count($result), 'Unused file marked as temporary.');
// Test file usage page.
@@ -1,6 +1,6 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
use Drupal\file\Entity\File;
@@ -1,9 +1,9 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
use Drupal\Core\Entity\Plugin\Validation\Constraint\ReferenceAccessConstraint;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\file\Entity\File;
use Drupal\node\Entity\NodeType;
use Drupal\user\RoleInterface;
@@ -81,23 +81,29 @@ class FilePrivateTest extends FileFieldTestBase {
$edit['title[0][value]'] = $this->randomMachineName();
$this->drupalPostForm('node/add/' . $type_name, $edit, t('Save'));
$new_node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$edit[$field_name . '[0][fids]'] = $node_file->id();
$this->drupalPostForm('node/' . $new_node->id() . '/edit', $edit, t('Save'));
// Can't use drupalPostForm() to set hidden fields.
$this->drupalGet('node/' . $new_node->id() . '/edit');
$this->getSession()->getPage()->find('css', 'input[name="' . $field_name . '[0][fids]"]')->setValue($node_file->id());
$this->getSession()->getPage()->pressButton(t('Save'));
// Make sure the form submit failed - we stayed on the edit form.
$this->assertUrl('node/' . $new_node->id() . '/edit');
// Check that we got the expected constraint form error.
$constraint = new ReferenceAccessConstraint();
$this->assertRaw(SafeMarkup::format($constraint->message, ['%type' => 'file', '%id' => $node_file->id()]));
$this->assertRaw(new FormattableMarkup($constraint->message, ['%type' => 'file', '%id' => $node_file->id()]));
// Attempt to reuse the existing file when creating a new node, and confirm
// that access is still denied.
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName();
$edit[$field_name . '[0][fids]'] = $node_file->id();
$this->drupalPostForm('node/add/' . $type_name, $edit, t('Save'));
// Can't use drupalPostForm() to set hidden fields.
$this->drupalGet('node/add/' . $type_name);
$this->getSession()->getPage()->find('css', 'input[name="title[0][value]"]')->setValue($edit['title[0][value]']);
$this->getSession()->getPage()->find('css', 'input[name="' . $field_name . '[0][fids]"]')->setValue($node_file->id());
$this->getSession()->getPage()->pressButton(t('Save'));
$new_node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$this->assertTrue(empty($new_node), 'Node was not created.');
$this->assertUrl('node/add/' . $type_name);
$this->assertRaw(SafeMarkup::format($constraint->message, ['%type' => 'file', '%id' => $node_file->id()]));
$this->assertRaw(new FormattableMarkup($constraint->message, ['%type' => 'file', '%id' => $node_file->id()]));
// Now make file_test_file_download() return everything.
\Drupal::state()->set('file_test.allow_all', TRUE);
@@ -144,9 +150,7 @@ class FilePrivateTest extends FileFieldTestBase {
$this->drupalGet($file_url);
$this->assertResponse(200, 'Confirmed that the anonymous uploader has access to the temporary file.');
// Close the prior connection and remove the session cookie.
$this->curlClose();
$this->curlCookies = [];
$this->cookies = [];
$this->getSession()->reset();
$this->drupalGet($file_url);
$this->assertResponse(403, 'Confirmed that another anonymous user cannot access the temporary file.');
@@ -174,9 +178,7 @@ class FilePrivateTest extends FileFieldTestBase {
$this->drupalGet($file_url);
$this->assertResponse(200, 'Confirmed that the anonymous uploader has access to the file whose references were removed.');
// Close the prior connection and remove the session cookie.
$this->curlClose();
$this->curlCookies = [];
$this->cookies = [];
$this->getSession()->reset();
$this->drupalGet($file_url);
$this->assertResponse(403, 'Confirmed that another anonymous user cannot access the file whose references were removed.');
@@ -197,9 +199,7 @@ class FilePrivateTest extends FileFieldTestBase {
$this->drupalGet($file_url);
$this->assertResponse(200, 'Confirmed that the anonymous uploader has access to the permanent file that is referenced by a published node.');
// Close the prior connection and remove the session cookie.
$this->curlClose();
$this->curlCookies = [];
$this->cookies = [];
$this->getSession()->reset();
$this->drupalGet($file_url);
$this->assertResponse(200, 'Confirmed that another anonymous user also has access to the permanent file that is referenced by a published node.');
@@ -214,7 +214,7 @@ class FilePrivateTest extends FileFieldTestBase {
$edit['files[' . $field_name . '_0]'] = $file_system->realpath($test_file->getFileUri());
$this->drupalPostForm(NULL, $edit, t('Save'));
$new_node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$new_node->setPublished(FALSE);
$new_node->setUnpublished();
$new_node->save();
$file = File::load($new_node->{$field_name}->target_id);
$this->assertTrue($file->isPermanent(), 'File is permanent.');
@@ -224,9 +224,7 @@ class FilePrivateTest extends FileFieldTestBase {
$this->drupalGet($file_url);
$this->assertResponse(403, 'Confirmed that the anonymous uploader cannot access the permanent file when it is referenced by an unpublished node.');
// Close the prior connection and remove the session cookie.
$this->curlClose();
$this->curlCookies = [];
$this->cookies = [];
$this->getSession()->reset();
$this->drupalGet($file_url);
$this->assertResponse(403, 'Confirmed that another anonymous user cannot access the permanent file when it is referenced by an unpublished node.');
}
@@ -1,6 +1,6 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
use Drupal\Component\Utility\Html;
use Drupal\Core\Render\BubbleableMetadata;
@@ -13,6 +13,7 @@ use Drupal\file\Entity\File;
* @group file
*/
class FileTokenReplaceTest extends FileFieldTestBase {
/**
* Creates a file, then tests the tokens generated from it.
*/
@@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\file\Functional;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\FileUploadResourceTestBase;
/**
* @group file
*/
class FileUploadJsonBasicAuthTest extends FileUploadResourceTestBase {
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,30 @@
<?php
namespace Drupal\Tests\file\Functional;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\FileUploadResourceTestBase;
/**
* @group file
*/
class FileUploadJsonCookieTest extends FileUploadResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -2,7 +2,6 @@
namespace Drupal\Tests\file\Functional\Formatter;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
@@ -47,7 +46,7 @@ abstract class FileMediaFormatterTestBase extends BrowserTestBase {
*/
protected function createMediaField($formatter, $file_extensions, array $formatter_settings = []) {
$entity_type = $bundle = 'entity_test';
$field_name = Unicode::strtolower($this->randomMachineName());
$field_name = mb_strtolower($this->randomMachineName());
FieldStorageConfig::create([
'entity_type' => $entity_type,
@@ -0,0 +1,139 @@
<?php
namespace Drupal\Tests\file\Functional\Hal;
use Drupal\Core\Cache\Cache;
use Drupal\Tests\file\Functional\Rest\FileResourceTestBase;
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group hal
*/
class FileHalJsonAnonTest extends FileResourceTestBase {
use HalEntityNormalizationTrait;
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$default_normalization = parent::getExpectedNormalizedEntity();
$normalization = $this->applyHalFieldNormalization($default_normalization);
$url = file_create_url($this->entity->getFileUri());
// @see \Drupal\Tests\hal\Functional\EntityResource\File\FileHalJsonAnonTest::testGetBcUriField()
if ($this->config('hal.settings')->get('bc_file_uri_as_url_normalizer')) {
$normalization['uri'][0]['value'] = $url;
}
$uid = $this->author->id();
return $normalization + [
'_embedded' => [
$this->baseUrl . '/rest/relation/file/file/uid' => [
[
'_links' => [
'self' => [
'href' => $this->baseUrl . "/user/$uid?_format=hal_json",
],
'type' => [
'href' => $this->baseUrl . '/rest/type/user/user',
],
],
'uuid' => [
[
'value' => $this->author->uuid(),
],
],
],
],
],
'_links' => [
'self' => [
'href' => $url,
],
'type' => [
'href' => $this->baseUrl . '/rest/type/file/file',
],
$this->baseUrl . '/rest/relation/file/file/uid' => [
[
'href' => $this->baseUrl . "/user/$uid?_format=hal_json",
],
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return parent::getNormalizedPostEntity() + [
'_links' => [
'type' => [
'href' => $this->baseUrl . '/rest/type/file/file',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheTags() {
return Cache::mergeTags(parent::getExpectedCacheTags(), ['config:hal.settings']);
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
return [
'url.site',
'user.permissions',
];
}
/**
* @see hal_update_8501()
*/
public function testGetBcUriField() {
$this->config('hal.settings')->set('bc_file_uri_as_url_normalizer', TRUE)->save(TRUE);
$this->initAuthentication();
$url = $this->getEntityResourceUrl();
$url->setOption('query', ['_format' => static::$format]);
$request_options = $this->getAuthenticationRequestOptions('GET');
$this->provisionEntityResource();
$this->setUpAuthorization('GET');
$response = $this->request('GET', $url, $request_options);
$expected = $this->getExpectedNormalizedEntity();
static::recursiveKSort($expected);
$actual = $this->serializer->decode((string) $response->getBody(), static::$format);
static::recursiveKSort($actual);
$this->assertSame($expected, $actual);
// Explicitly assert that $file->uri->value is an absolute file URL, unlike
// the default normalization.
$this->assertSame($this->baseUrl . '/' . $this->siteDirectory . '/files/drupal.txt', $actual['uri'][0]['value']);
}
}
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\file\Functional\Hal;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group hal
*/
class FileHalJsonBasicAuthTest extends FileHalJsonAnonTest {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,19 @@
<?php
namespace Drupal\Tests\file\Functional\Hal;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group hal
*/
class FileHalJsonCookieTest extends FileHalJsonAnonTest {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\file\Functional\Hal;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group hal
*/
class FileUploadHalJsonBasicAuthTest extends FileUploadHalJsonTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,19 @@
<?php
namespace Drupal\Tests\file\Functional\Hal;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group hal
*/
class FileUploadHalJsonCookieTest extends FileUploadHalJsonTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -0,0 +1,97 @@
<?php
namespace Drupal\Tests\file\Functional\Hal;
use Drupal\Tests\rest\Functional\FileUploadResourceTestBase;
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
/**
* Tests binary data file upload route for HAL JSON.
*/
abstract class FileUploadHalJsonTestBase extends FileUploadResourceTestBase {
use HalEntityNormalizationTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity($fid = 1, $expected_filename = 'example.txt', $expected_as_filename = FALSE) {
$normalization = parent::getExpectedNormalizedEntity($fid, $expected_filename, $expected_as_filename);
// Cannot use applyHalFieldNormalization() as it uses the $entity property
// from the test class, which in the case of file upload tests, is the
// parent entity test entity for the file that's created.
// The HAL normalization adds entity reference fields to '_links' and
// '_embedded'.
unset($normalization['uid']);
return $normalization + [
'_links' => [
'self' => [
// @todo This can use a proper link once
// https://www.drupal.org/project/drupal/issues/2907402 is complete.
// This link matches what is generated from from File::url(), a
// resource URL is currently not available.
'href' => file_create_url($normalization['uri'][0]['value']),
],
'type' => [
'href' => $this->baseUrl . '/rest/type/file/file',
],
$this->baseUrl . '/rest/relation/file/file/uid' => [
['href' => $this->baseUrl . '/user/' . $this->account->id() . '?_format=hal_json'],
],
],
'_embedded' => [
$this->baseUrl . '/rest/relation/file/file/uid' => [
[
'_links' => [
'self' => [
'href' => $this->baseUrl . '/user/' . $this->account->id() . '?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/user/user',
],
],
'uuid' => [
[
'value' => $this->account->uuid(),
],
],
],
],
],
];
}
/**
* {@inheritdoc}
*
* @see \Drupal\Tests\hal\Functional\EntityResource\EntityTest\EntityTestHalJsonAnonTest::getNormalizedPostEntity()
*/
protected function getNormalizedPostEntity() {
return parent::getNormalizedPostEntity() + [
'_links' => [
'type' => [
'href' => $this->baseUrl . '/rest/type/entity_test/entity_test',
],
],
];
}
}
@@ -1,6 +1,6 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
use Drupal\file\Entity\File;
use Drupal\node\Entity\Node;
@@ -1,6 +1,6 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
/**
* Tests the file uploading functions.
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\file\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class FileJsonAnonTest extends FileResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\file\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class FileJsonBasicAuthTest extends FileResourceTestBase {
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\file\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class FileJsonCookieTest extends FileResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -0,0 +1,233 @@
<?php
namespace Drupal\Tests\file\Functional\Rest;
use Drupal\file\Entity\File;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\user\Entity\User;
abstract class FileResourceTestBase extends EntityResourceTestBase {
use BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['file', 'user'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'file';
/**
* @var \Drupal\file\FileInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected static $patchProtectedFieldNames = [
'uri' => NULL,
'filemime' => NULL,
'filesize' => NULL,
'status' => NULL,
'changed' => NULL,
];
/**
* @var \Drupal\user\UserInterface
*/
protected $author;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
switch ($method) {
case 'GET':
$this->grantPermissionsToTestedRole(['access content']);
break;
case 'PATCH':
case 'DELETE':
// \Drupal\file\FileAccessControlHandler::checkAccess() grants 'update'
// and 'delete' access only to the user that owns the file. So there is
// no permission to grant: instead, the file owner must be changed from
// its default (user 1) to the current user.
$this->makeCurrentUserFileOwner();
break;
}
}
/**
* {@inheritdoc}
*/
protected function grantPermissionsToTestedRole(array $permissions) {
// testPatch() and testDelete() test the 'bc_entity_resource_permissions' BC
// layer; also call makeCurrentUserFileOwner() then.
if ($permissions === ['restful patch entity:file'] || $permissions === ['restful delete entity:file']) {
$this->makeCurrentUserFileOwner();
}
parent::grantPermissionsToTestedRole($permissions);
}
/**
* Makes the current user the file owner.
*/
protected function makeCurrentUserFileOwner() {
$account = static::$auth ? User::load(2) : User::load(0);
$this->entity->setOwnerId($account->id());
$this->entity->setOwner($account);
$this->entity->save();
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$this->author = User::load(1);
$file = File::create();
$file->setOwnerId($this->author->id());
$file->setFilename('drupal.txt');
$file->setMimeType('text/plain');
$file->setFileUri('public://drupal.txt');
$file->set('status', FILE_STATUS_PERMANENT);
$file->save();
file_put_contents($file->getFileUri(), 'Drupal');
return $file;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'changed' => [
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
],
'created' => [
$this->formatExpectedTimestampItemValues((int) $this->entity->getCreatedTime()),
],
'fid' => [
[
'value' => 1,
],
],
'filemime' => [
[
'value' => 'text/plain',
],
],
'filename' => [
[
'value' => 'drupal.txt',
],
],
'filesize' => [
[
'value' => (int) $this->entity->getSize(),
],
],
'langcode' => [
[
'value' => 'en',
],
],
'status' => [
[
'value' => TRUE,
],
],
'uid' => [
[
'target_id' => (int) $this->author->id(),
'target_type' => 'user',
'target_uuid' => $this->author->uuid(),
'url' => base_path() . 'user/' . $this->author->id(),
],
],
'uri' => [
[
'url' => base_path() . $this->siteDirectory . '/files/drupal.txt',
'value' => 'public://drupal.txt',
],
],
'uuid' => [
[
'value' => $this->entity->uuid(),
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return [
'uid' => [
[
'target_id' => $this->author->id(),
],
],
'filename' => [
[
'value' => 'drupal.txt',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPatchEntity() {
return array_diff_key($this->getNormalizedPostEntity(), ['uid' => TRUE]);
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
return [
'user.permissions',
];
}
/**
* {@inheritdoc}
*/
public function testPost() {
// Drupal does not allow creating file entities independently. It allows you
// to create file entities that are referenced from another entity (e.g. an
// image for a node's image field).
// For that purpose, there is the "file_upload" REST resource plugin.
// @see \Drupal\file\FileAccessControlHandler::checkCreateAccess()
// @see \Drupal\file\Plugin\rest\resource\FileUploadResource
$this->markTestSkipped();
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedAccessMessage($method) {
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
return parent::getExpectedUnauthorizedAccessMessage($method);
}
if ($method === 'GET') {
return "The 'access content' permission is required.";
}
if ($method === 'PATCH' || $method === 'DELETE') {
return 'Only the file owner can update or delete the file entity.';
}
return parent::getExpectedUnauthorizedAccessMessage($method);
}
}
@@ -0,0 +1,26 @@
<?php
namespace Drupal\Tests\file\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class FileXmlAnonTest extends FileResourceTestBase {
use AnonResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
}
@@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\file\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class FileXmlBasicAuthTest extends FileResourceTestBase {
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';
}
@@ -0,0 +1,31 @@
<?php
namespace Drupal\Tests\file\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class FileXmlCookieTest extends FileResourceTestBase {
use CookieResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -1,8 +1,9 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
use Drupal\file\Entity\File;
use Drupal\Tests\TestFileCreationTrait;
/**
* Tests the _file_save_upload_from_form() function.
@@ -13,6 +14,10 @@ use Drupal\file\Entity\File;
*/
class SaveUploadFormTest extends FileManagedTestBase {
use TestFileCreationTrait {
getTestFiles as drupalGetTestFiles;
}
/**
* Modules to enable.
*
@@ -298,7 +303,7 @@ class SaveUploadFormTest extends FileManagedTestBase {
$file_system = \Drupal::service('file_system');
$edit = [
'file_test_replace' => FILE_EXISTS_RENAME,
'files[file_test_upload][]' => $file_system->realpath($this->image->getFileUri())
'files[file_test_upload][]' => $file_system->realpath($this->image->getFileUri()),
];
$this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit'));
$this->assertResponse(200, 'Received a 200 response for posted test file.');
@@ -316,7 +321,7 @@ class SaveUploadFormTest extends FileManagedTestBase {
$file_system = \Drupal::service('file_system');
$edit = [
'file_test_replace' => FILE_EXISTS_REPLACE,
'files[file_test_upload][]' => $file_system->realpath($this->image->getFileUri())
'files[file_test_upload][]' => $file_system->realpath($this->image->getFileUri()),
];
$this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit'));
$this->assertResponse(200, 'Received a 200 response for posted test file.');
@@ -334,7 +339,7 @@ class SaveUploadFormTest extends FileManagedTestBase {
$file_system = \Drupal::service('file_system');
$edit = [
'file_test_replace' => FILE_EXISTS_ERROR,
'files[file_test_upload][]' => $file_system->realpath($this->image->getFileUri())
'files[file_test_upload][]' => $file_system->realpath($this->image->getFileUri()),
];
$this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit'));
$this->assertResponse(200, 'Received a 200 response for posted test file.');
@@ -365,7 +370,7 @@ class SaveUploadFormTest extends FileManagedTestBase {
$file_system = \Drupal::service('file_system');
$edit = [
'file_subdir' => $test_directory,
'files[file_test_upload][]' => $file_system->realpath($this->image->getFileUri())
'files[file_test_upload][]' => $file_system->realpath($this->image->getFileUri()),
];
\Drupal::state()->set('file_test.disable_error_collection', TRUE);
@@ -379,7 +384,7 @@ class SaveUploadFormTest extends FileManagedTestBase {
$this->assertResponse(200);
$this->assertRaw(t('Upload error. Could not move uploaded file @file to destination @destination.', [
'@file' => $this->image->getFilename(),
'@destination' => 'temporary://' . $test_directory . '/' . $this->image->getFilename()
'@destination' => 'temporary://' . $test_directory . '/' . $this->image->getFilename(),
]), 'Found upload error log entry.');
}
@@ -409,7 +414,7 @@ class SaveUploadFormTest extends FileManagedTestBase {
$edit = [
'files[file_test_upload][]' => $file_system->realpath($this->image->getFileUri()),
'error_message' => $error,
'extensions' => 'foo'
'extensions' => 'foo',
];
$this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit'));
$this->assertResponse(200, 'Received a 200 response for posted test file.');
@@ -419,7 +424,7 @@ class SaveUploadFormTest extends FileManagedTestBase {
// after calling _file_save_upload_from_form() are correct.
$this->assertText($error);
$this->assertRaw('Number of error messages before _file_save_upload_from_form(): 1');
$this->assertRaw('Number of error messages after _file_save_upload_from_form(): 2');
$this->assertRaw('Number of error messages after _file_save_upload_from_form(): 1');
// Test a successful upload with no messages.
$edit = [
@@ -445,17 +450,22 @@ class SaveUploadFormTest extends FileManagedTestBase {
/** @var \Drupal\Core\File\FileSystemInterface $file_system */
$file_system = \Drupal::service('file_system');
// Can't use drupalPostForm() for set nonexistent fields.
$this->drupalGet('file-test/save_upload_from_form_test');
$client = $this->getSession()->getDriver()->getClient();
$submit_xpath = $this->assertSession()->buttonExists('Submit')->getXpath();
$form = $client->getCrawler()->filterXPath($submit_xpath)->form();
$edit = [
'files[file_test_upload][]' => [
$file_system->realpath($this->phpfile->uri),
$file_system->realpath($textfile->uri),
],
'allow_all_extensions' => FALSE,
'is_image_file' => TRUE,
'extensions' => 'jpeg',
];
$edit += $form->getPhpValues();
$files['files']['file_test_upload'][0] = $file_system->realpath($this->phpfile->uri);
$files['files']['file_test_upload'][1] = $file_system->realpath($textfile->uri);
$client->request($form->getMethod(), $form->getUri(), $edit, $files);
$this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit'));
$this->assertResponse(200, 'Received a 200 response for posted test file.');
$this->assertRaw(t('Epic upload FAIL!'), 'Found the failure message.');
@@ -473,7 +483,7 @@ class SaveUploadFormTest extends FileManagedTestBase {
$file_system = \Drupal::service('file_system');
$edit = [
'files[file_test_upload][]' => $file_system->realpath($this->image->getFileUri()),
'extensions' => 'foo'
'extensions' => 'foo',
];
$this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit'));
$this->assertResponse(200, 'Received a 200 response for posted test file.');
@@ -1,8 +1,9 @@
<?php
namespace Drupal\file\Tests;
namespace Drupal\Tests\file\Functional;
use Drupal\file\Entity\File;
use Drupal\Tests\TestFileCreationTrait;
/**
* Tests the file_save_upload() function.
@@ -10,6 +11,11 @@ use Drupal\file\Entity\File;
* @group file
*/
class SaveUploadTest extends FileManagedTestBase {
use TestFileCreationTrait {
getTestFiles as drupalGetTestFiles;
}
/**
* Modules to enable.
*
@@ -280,7 +286,7 @@ class SaveUploadTest extends FileManagedTestBase {
public function testExistingRename() {
$edit = [
'file_test_replace' => FILE_EXISTS_RENAME,
'files[file_test_upload]' => \Drupal::service('file_system')->realpath($this->image->getFileUri())
'files[file_test_upload]' => \Drupal::service('file_system')->realpath($this->image->getFileUri()),
];
$this->drupalPostForm('file-test/upload', $edit, t('Submit'));
$this->assertResponse(200, 'Received a 200 response for posted test file.');
@@ -296,7 +302,7 @@ class SaveUploadTest extends FileManagedTestBase {
public function testExistingReplace() {
$edit = [
'file_test_replace' => FILE_EXISTS_REPLACE,
'files[file_test_upload]' => \Drupal::service('file_system')->realpath($this->image->getFileUri())
'files[file_test_upload]' => \Drupal::service('file_system')->realpath($this->image->getFileUri()),
];
$this->drupalPostForm('file-test/upload', $edit, t('Submit'));
$this->assertResponse(200, 'Received a 200 response for posted test file.');
@@ -312,7 +318,7 @@ class SaveUploadTest extends FileManagedTestBase {
public function testExistingError() {
$edit = [
'file_test_replace' => FILE_EXISTS_ERROR,
'files[file_test_upload]' => \Drupal::service('file_system')->realpath($this->image->getFileUri())
'files[file_test_upload]' => \Drupal::service('file_system')->realpath($this->image->getFileUri()),
];
$this->drupalPostForm('file-test/upload', $edit, t('Submit'));
$this->assertResponse(200, 'Received a 200 response for posted test file.');
@@ -341,7 +347,7 @@ class SaveUploadTest extends FileManagedTestBase {
$edit = [
'file_subdir' => $test_directory,
'files[file_test_upload]' => \Drupal::service('file_system')->realpath($this->image->getFileUri())
'files[file_test_upload]' => \Drupal::service('file_system')->realpath($this->image->getFileUri()),
];
\Drupal::state()->set('file_test.disable_error_collection', TRUE);
@@ -355,7 +361,7 @@ class SaveUploadTest extends FileManagedTestBase {
$this->assertResponse(200);
$this->assertRaw(t('Upload error. Could not move uploaded file @file to destination @destination.', [
'@file' => $this->image->getFilename(),
'@destination' => 'temporary://' . $test_directory . '/' . $this->image->getFilename()
'@destination' => 'temporary://' . $test_directory . '/' . $this->image->getFilename(),
]), 'Found upload error log entry.');
}
@@ -1,13 +1,13 @@
<?php
namespace Drupal\file\Tests\Update;
namespace Drupal\Tests\file\Functional\Update;
use Drupal\system\Tests\Update\UpdatePathTestBase;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
* Tests File update path.
*
* @group file
* @group legacy
*/
class FileUpdateTest extends UpdatePathTestBase {
@@ -21,8 +21,8 @@ class FileUpdateTest extends UpdatePathTestBase {
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../tests/fixtures/update/drupal-8.file_formatters_update_2677990.php',
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../../tests/fixtures/update/drupal-8.file_formatters_update_2677990.php',
];
}
@@ -10,6 +10,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* @see https://www.drupal.org/node/2801777
*
* @group Update
* @group legacy
*/
class FileUsageTemporaryDeletionConfigurationUpdateTest extends UpdatePathTestBase {
@@ -1,10 +1,10 @@
<?php
namespace Drupal\file\Tests\Views;
namespace Drupal\Tests\file\Functional\Views;
use Drupal\field\Entity\FieldConfig;
use Drupal\file\Entity\File;
use Drupal\views\Tests\ViewTestBase;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Views;
use Drupal\views\Tests\ViewTestData;
use Drupal\field\Entity\FieldStorageConfig;
@@ -0,0 +1,49 @@
<?php
namespace Drupal\Tests\file\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\Tests\file\Functional\FileFieldCreationTrait;
use Drupal\Tests\TestFileCreationTrait;
/**
* Tests validation functions such as file type, max file size, max size per
* node, and required.
*
* @group file
*/
class FileFieldValidateTest extends WebDriverTestBase {
use FileFieldCreationTrait;
use TestFileCreationTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'file'];
/**
* Test the validation message is displayed only once for ajax uploads.
*/
public function testAjaxValidationMessage() {
$field_name = strtolower($this->randomMachineName());
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
$this->createFileField($field_name, 'node', 'article', [], ['file_extensions' => 'txt']);
$this->drupalLogin($this->drupalCreateUser([
'access content',
'create article content',
]));
$page = $this->getSession()->getPage();
$this->drupalGet('node/add/article');
$image_file = current($this->getTestFiles('image'));
$image_path = $this->container->get('file_system')->realpath($image_file->uri);
$page->attachFileToField('files[' . $field_name . '_0]', $image_path);
$elements = $page->waitFor(10, function () use ($page) {
return $page->findAll('css', '.messages--error');
});
$this->assertCount(1, $elements, 'Ajax validation messages are displayed once.');
}
}
@@ -3,7 +3,7 @@
namespace Drupal\Tests\file\FunctionalJavascript;
use Drupal\Component\Utility\Bytes;
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\Tests\TestFileCreationTrait;
use Drupal\Tests\file\Functional\FileFieldCreationTrait;
@@ -12,7 +12,7 @@ use Drupal\Tests\file\Functional\FileFieldCreationTrait;
*
* @group file
*/
class MaximumFileSizeExceededUploadTest extends JavascriptTestBase {
class MaximumFileSizeExceededUploadTest extends WebDriverTestBase {
use FileFieldCreationTrait;
use TestFileCreationTrait;
@@ -93,7 +93,7 @@ class AccessTest extends KernelTestBase {
\Drupal::currentUser()->setAccount($this->user1);
/** @var \Drupal\file\FileInterface $file */
$file = File::create([
'uri' => 'public://test.png'
'uri' => 'public://test.png',
]);
// While creating a file entity access will be allowed for create-only
// fields.
@@ -123,4 +123,28 @@ class AccessTest extends KernelTestBase {
$this->assertFalse($this->file->access('create'));
}
/**
* Tests cacheability metadata.
*/
public function testFileCacheability() {
$file = File::create([
'filename' => 'green-scarf',
'uri' => 'private://green-scarf',
'filemime' => 'text/plain',
'status' => FILE_STATUS_PERMANENT,
]);
$file->save();
\Drupal::service('session')->set('anonymous_allowed_file_ids', [$file->id() => $file->id()]);
$account = User::getAnonymousUser();
$file->setOwnerId($account->id())->save();
$this->assertSame(['session', 'user'], $file->access('view', $account, TRUE)->getCacheContexts());
$this->assertSame(['session', 'user'], $file->access('download', $account, TRUE)->getCacheContexts());
$account = $this->user1;
$file->setOwnerId($account->id())->save();
$this->assertSame(['user'], $file->access('view', $account, TRUE)->getCacheContexts());
$this->assertSame(['user'], $file->access('download', $account, TRUE)->getCacheContexts());
}
}
@@ -10,6 +10,7 @@ use Drupal\file\Entity\File;
* @group file
*/
class CopyTest extends FileManagedUnitTestBase {
/**
* Test file copying in the normal, base case.
*/
@@ -10,6 +10,7 @@ use Drupal\file\Entity\File;
* @group file
*/
class DeleteTest extends FileManagedUnitTestBase {
/**
* Tries deleting a normal file (as opposed to a directory, symlink, etc).
*/
@@ -78,9 +78,9 @@ class FileItemValidationTest extends KernelTestBase {
'default' => [
'files' => [
'test.txt' => str_repeat('a', 3000),
]
]
]
],
],
],
]);
// Test for max filesize.
@@ -95,7 +95,7 @@ class FileItemValidationTest extends KernelTestBase {
'uid' => $this->user->id(),
'field_test_file' => [
'target_id' => $file->id(),
]
],
]);
$result = $entity_test->validate();
$this->assertCount(2, $result);
@@ -104,6 +104,18 @@ class FileItemValidationTest extends KernelTestBase {
$this->assertEquals('The file is <em class="placeholder">2.93 KB</em> exceeding the maximum file size of <em class="placeholder">2 KB</em>.', (string) $result->get(0)->getMessage());
$this->assertEquals('field_test_file.0', $result->get(1)->getPropertyPath());
$this->assertEquals('Only files with the following extensions are allowed: <em class="placeholder">jpg|png</em>.', (string) $result->get(1)->getMessage());
// Refer to a file that does not exist.
$entity_test = EntityTest::create([
'uid' => $this->user->id(),
'field_test_file' => [
'target_id' => 2,
],
]);
$result = $entity_test->validate();
$this->assertCount(1, $result);
$this->assertEquals('field_test_file.0.target_id', $result->get(0)->getPropertyPath());
$this->assertEquals('The referenced entity (<em class="placeholder">file</em>: <em class="placeholder">2</em>) does not exist.', (string) $result->get(0)->getMessage());
}
/**
@@ -10,6 +10,7 @@ use Drupal\file\Entity\File;
* @group file
*/
class LoadTest extends FileManagedUnitTestBase {
/**
* Try to load a non-existent file by fid.
*/
@@ -30,7 +30,7 @@ trait FileMigrationTestTrait {
// Make sure we have a single trailing slash.
$source = $migration->getSourceConfiguration();
$source['site_path'] = 'core/modules/simpletest';
$source['constants']['source_base_path'] = \Drupal::root() . '/';
$source['constants']['source_base_path'] = $this->root . '/';
$migration->set('source', $source);
}
}
@@ -2,7 +2,6 @@
namespace Drupal\Tests\file\Kernel\Migrate\d6;
use Drupal\Component\Utility\Random;
use Drupal\file\Entity\File;
use Drupal\file\FileInterface;
use Drupal\KernelTests\KernelTestBase;
@@ -11,7 +10,7 @@ use Drupal\Tests\migrate\Kernel\MigrateDumpAlterInterface;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* file migration.
* Test file migration.
*
* @group migrate_drupal_6
*/
@@ -68,8 +67,10 @@ class MigrateFileTest extends MigrateDrupal6TestBase implements MigrateDumpAlter
public function testFiles() {
$this->assertEntity(1, 'Image1.png', '39325', 'public://image-1.png', 'image/png', '1');
$this->assertEntity(2, 'Image2.jpg', '1831', 'public://image-2.jpg', 'image/jpeg', '1');
$this->assertEntity(3, 'Image-test.gif', '183', 'public://image-test.gif', 'image/jpeg', '1');
$this->assertEntity(3, 'image-3.jpg', '1831', 'public://image-3.jpg', 'image/jpeg', '1');
$this->assertEntity(4, 'html-1.txt', '24', 'public://html-1.txt', 'text/plain', '1');
// Ensure temporary file was not migrated.
$this->assertNull(File::load(6));
$map_table = $this->getMigration('d6_file')->getIdMap()->mapTableName();
$map = \Drupal::database()
@@ -81,10 +82,9 @@ class MigrateFileTest extends MigrateDrupal6TestBase implements MigrateDumpAlter
// The 4 files from the fixture.
1 => '1',
2 => '2',
// The file updated in migrateDumpAlter().
3 => '3',
5 => '4',
// The file updated in migrateDumpAlter().
6 => NULL,
// The file created in migrateDumpAlter().
7 => '4',
];
@@ -124,10 +124,9 @@ class MigrateFileTest extends MigrateDrupal6TestBase implements MigrateDumpAlter
// The 4 files from the fixture.
1 => '5',
2 => '6',
// The file updated in migrateDumpAlter().
3 => '7',
5 => '8',
// The file updated in migrateDumpAlter().
6 => NULL,
// The files created in migrateDumpAlter().
7 => '8',
8 => '8',
@@ -142,33 +141,17 @@ class MigrateFileTest extends MigrateDrupal6TestBase implements MigrateDumpAlter
$this->assertEquals(8, count(File::loadMultiple()));
}
/**
* @return string
* A filename based upon the test.
*/
public static function getUniqueFilename() {
return static::$tempFilename;
}
/**
* {@inheritdoc}
*/
public static function migrateDumpAlter(KernelTestBase $test) {
// Creates a random filename and updates the source database.
$random = new Random();
$temp_directory = file_directory_temp();
file_prepare_directory($temp_directory, FILE_CREATE_DIRECTORY);
static::$tempFilename = $test->getDatabasePrefix() . $random->name() . '.jpg';
$file_path = $temp_directory . '/' . static::$tempFilename;
file_put_contents($file_path, '');
$db = Database::getConnection('default', 'migrate');
$db->update('files')
->condition('fid', 6)
->condition('fid', 3)
->fields([
'filename' => static::$tempFilename,
'filepath' => $file_path,
'filename' => 'image-3.jpg',
'filepath' => 'core/modules/simpletest/files/image-3.jpg',
])
->execute();
@@ -44,7 +44,7 @@ class MigrateUploadEntityDisplayTest extends MigrateDrupal6TestBase {
$component = $display->getComponent('upload');
$this->assertTrue(is_null($component));
$this->assertIdentical(['node', 'page', 'default', 'upload'], $this->getMigration('d6_upload_entity_display')->getIdMap()->lookupDestinationID(['page']));
$this->assertIdentical(['node', 'page', 'default', 'upload'], $this->getMigration('d6_upload_entity_display')->getIdMap()->lookupDestinationId(['page']));
}
/**
@@ -44,7 +44,7 @@ class MigrateUploadEntityFormDisplayTest extends MigrateDrupal6TestBase {
$component = $display->getComponent('upload');
$this->assertTrue(is_null($component));
$this->assertIdentical(['node', 'page', 'default', 'upload'], $this->getMigration('d6_upload_entity_form_display')->getIdMap()->lookupDestinationID(['page']));
$this->assertIdentical(['node', 'page', 'default', 'upload'], $this->getMigration('d6_upload_entity_form_display')->getIdMap()->lookupDestinationId(['page']));
}
/**
@@ -31,7 +31,7 @@ class MigrateUploadFieldTest extends MigrateDrupal6TestBase {
public function testUpload() {
$field_storage = FieldStorageConfig::load('node.upload');
$this->assertIdentical('node.upload', $field_storage->id());
$this->assertIdentical(['node', 'upload'], $this->getMigration('d6_upload_field')->getIdMap()->lookupDestinationID(['']));
$this->assertIdentical(['node', 'upload'], $this->getMigration('d6_upload_field')->getIdMap()->lookupDestinationId(['']));
}
}
@@ -43,7 +43,7 @@ class MigrateUploadInstanceTest extends MigrateDrupal6TestBase {
$field = FieldConfig::load('node.article.upload');
$this->assertTrue(is_null($field));
$this->assertIdentical(['node', 'page', 'upload'], $this->getMigration('d6_upload_field_instance')->getIdMap()->lookupDestinationID(['page']));
$this->assertIdentical(['node', 'page', 'upload'], $this->getMigration('d6_upload_field_instance')->getIdMap()->lookupDestinationId(['page']));
}
}
@@ -2,36 +2,80 @@
namespace Drupal\Tests\file\Kernel\Migrate\d7;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
use Drupal\file\Entity\File;
use Drupal\file\FileInterface;
/**
* A trait to setup the file migration.
*/
trait FileMigrationSetupTrait {
/**
* Returns information about the file to be migrated.
*
* @return array
* Array with keys 'path', 'size', 'base_path', and 'plugin_id'.
*/
abstract protected function getFileMigrationInfo();
/**
* Prepare the file migration for running.
*/
protected function fileMigrationSetup() {
$this->installSchema('file', ['file_usage']);
$this->installEntitySchema('file');
$this->container->get('stream_wrapper_manager')->registerWrapper('public', PublicStream::class, StreamWrapperInterface::NORMAL);
$this->installSchema('file', ['file_usage']);
$fs = \Drupal::service('file_system');
// The public file directory active during the test will serve as the
// root of the fictional Drupal 7 site we're migrating.
$fs->mkdir('public://sites/default/files', NULL, TRUE);
file_put_contents('public://sites/default/files/cube.jpeg', str_repeat('*', 3620));
$info = $this->getFileMigrationInfo();
$fs = $this->container->get('file_system');
// Ensure that the files directory exists.
$fs->mkdir(dirname($info['path']), NULL, TRUE);
// Put test file in the source directory.
file_put_contents($info['path'], str_repeat('*', $info['size']));
/** @var \Drupal\migrate\Plugin\Migration $migration */
$migration = $this->getMigration('d7_file');
$migration = $this->getMigration($info['plugin_id']);
// Set the source plugin's source_base_path configuration value, which
// would normally be set by the user running the migration.
$source = $migration->getSourceConfiguration();
$source['constants']['source_base_path'] = $fs->realpath('public://');
$source['constants']['source_base_path'] = $fs->realpath($info['base_path']);
$migration->set('source', $source);
$this->executeMigration($migration);
}
/**
* Tests a single file entity.
*
* @param int $id
* The file ID.
* @param string $name
* The expected file name.
* @param string $uri
* The expected URI.
* @param string $mime
* The expected MIME type.
* @param string $size
* The expected file size.
* @param string $created
* The expected creation time.
* @param string $changed
* The expected modification time.
* @param string $uid
* The expected owner ID.
*/
protected function assertEntity($id, $name, $uri, $mime, $size, $created, $changed, $uid) {
/** @var \Drupal\file\FileInterface $file */
$file = File::load($id);
$this->assertInstanceOf(FileInterface::class, $file);
$this->assertSame($name, $file->getFilename());
$this->assertSame($uri, $file->getFileUri());
$this->assertFileExists($uri);
$this->assertSame($mime, $file->getMimeType());
$this->assertSame($size, $file->getSize());
// isPermanent(), isTemporary(), etc. are determined by the status column.
$this->assertTrue($file->isPermanent());
$this->assertSame($created, $file->getCreatedTime());
$this->assertSame($changed, $file->getChangedTime());
$this->assertSame($uid, $file->getOwnerId());
}
}
@@ -3,7 +3,6 @@
namespace Drupal\Tests\file\Kernel\Migrate\d7;
use Drupal\file\Entity\File;
use Drupal\file\FileInterface;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
@@ -15,6 +14,9 @@ class MigrateFileTest extends MigrateDrupal7TestBase {
use FileMigrationSetupTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['file'];
/**
@@ -22,44 +24,19 @@ class MigrateFileTest extends MigrateDrupal7TestBase {
*/
protected function setUp() {
parent::setUp();
$this->fileMigrationSetup();
}
/**
* Tests a single file entity.
*
* @param int $id
* The file ID.
* @param string $name
* The expected file name.
* @param string $uri
* The expected URI.
* @param string $mime
* The expected MIME type.
* @param int $size
* The expected file size.
* @param int $created
* The expected creation time.
* @param int $changed
* The expected modification time.
* @param int $uid
* The expected owner ID.
* {@inheritdoc}
*/
protected function assertEntity($id, $name, $uri, $mime, $size, $created, $changed, $uid) {
/** @var \Drupal\file\FileInterface $file */
$file = File::load($id);
$this->assertTrue($file instanceof FileInterface);
$this->assertIdentical($name, $file->getFilename());
$this->assertIdentical($uri, $file->getFileUri());
$this->assertTrue(file_exists($uri));
$this->assertIdentical($mime, $file->getMimeType());
$this->assertIdentical($size, $file->getSize());
// isPermanent(), isTemporary(), etc. are determined by the status column.
$this->assertTrue($file->isPermanent());
$this->assertIdentical($created, $file->getCreatedTime());
$this->assertIdentical($changed, $file->getChangedTime());
$this->assertIdentical($uid, $file->getOwnerId());
protected function getFileMigrationInfo() {
return [
'path' => 'public://sites/default/files/cube.jpeg',
'size' => '3620',
'base_path' => 'public://',
'plugin_id' => 'd7_file',
];
}
/**
@@ -67,6 +44,8 @@ class MigrateFileTest extends MigrateDrupal7TestBase {
*/
public function testFileMigration() {
$this->assertEntity(1, 'cube.jpeg', 'public://cube.jpeg', 'image/jpeg', '3620', '1421727515', '1421727515', '1');
// Ensure temporary file was not migrated.
$this->assertNull(File::load(4));
}
}
@@ -3,8 +3,6 @@
namespace Drupal\Tests\file\Kernel\Migrate\d7;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\file\Entity\File;
use Drupal\file\FileInterface;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
@@ -14,6 +12,8 @@ use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
*/
class MigratePrivateFileTest extends MigrateDrupal7TestBase {
use FileMigrationSetupTrait;
/**
* {@inheritdoc}
*/
@@ -25,22 +25,19 @@ class MigratePrivateFileTest extends MigrateDrupal7TestBase {
protected function setUp() {
parent::setUp();
$this->setSetting('file_private_path', $this->container->get('site.path') . '/private');
$this->installEntitySchema('file');
$fs = $this->container->get('file_system');
$this->fileMigrationSetup();
}
// Ensure that the private files directory exists.
$fs->mkdir('private://sites/default/private/', NULL, TRUE);
// Put test file in the source directory.
file_put_contents('private://sites/default/private/Babylon5.txt', str_repeat('*', 3));
/** @var \Drupal\migrate\Plugin\Migration $migration */
$migration = $this->getMigration('d7_file_private');
// Set the source plugin's source_file_private_path configuration value,
// which would normally be set by the user running the migration.
$source = $migration->getSourceConfiguration();
$source['constants']['source_base_path'] = $fs->realpath('private://');
$migration->set('source', $source);
$this->executeMigration($migration);
/**
* {@inheritdoc}
*/
protected function getFileMigrationInfo() {
return [
'path' => 'private://sites/default/private/Babylon5.txt',
'size' => '3',
'base_path' => 'private://',
'plugin_id' => 'd7_file_private',
];
}
/**
@@ -52,42 +49,6 @@ class MigratePrivateFileTest extends MigrateDrupal7TestBase {
->addTag('stream_wrapper', ['scheme' => 'private']);
}
/**
* Tests a single file entity.
*
* @param int $id
* The file ID.
* @param string $name
* The expected file name.
* @param string $uri
* The expected URI.
* @param string $mime
* The expected MIME type.
* @param int $size
* The expected file size.
* @param int $created
* The expected creation time.
* @param int $changed
* The expected modification time.
* @param int $uid
* The expected owner ID.
*/
protected function assertEntity($id, $name, $uri, $mime, $size, $created, $changed, $uid) {
/** @var \Drupal\file\FileInterface $file */
$file = File::load($id);
$this->assertInstanceOf(FileInterface::class, $file);
$this->assertSame($name, $file->getFilename());
$this->assertSame($uri, $file->getFileUri());
$this->assertFileExists($uri);
$this->assertSame($mime, $file->getMimeType());
$this->assertSame($size, $file->getSize());
// isPermanent(), isTemporary(), etc. are determined by the status column.
$this->assertTrue($file->isPermanent());
$this->assertSame($created, $file->getCreatedTime());
$this->assertSame($changed, $file->getChangedTime());
$this->assertSame($uid, $file->getOwnerId());
}
/**
* Tests that all expected files are migrated.
*/
@@ -10,6 +10,7 @@ use Drupal\file\Entity\File;
* @group file
*/
class MoveTest extends FileManagedUnitTestBase {
/**
* Move a normal file.
*/
@@ -46,11 +46,42 @@ class FileTest extends MigrateSqlSourceTestBase {
'status' => 1,
'timestamp' => 1382255662,
],
[
'fid' => 3,
'uid' => 1,
'filename' => 'migrate-test-file-3.pdf',
'filepath' => '/tmp/migrate-test-file-3.pdf',
'filemime' => 'application/pdf',
'filesize' => 304124,
'status' => 1,
'timestamp' => 1382277662,
],
];
// The expected results are identical to the source data.
$tests[0]['expected_data'] = $tests[0]['source_data']['files'];
// The expected results are the same as the source data but excluding
// the temporary file.
$tests[0]['expected_data'] = [
[
'fid' => 1,
'uid' => 1,
'filename' => 'migrate-test-file-1.pdf',
'filepath' => 'sites/default/files/migrate-test-file-1.pdf',
'filemime' => 'application/pdf',
'filesize' => 890404,
'status' => 1,
'timestamp' => 1382255613,
],
[
'fid' => 2,
'uid' => 1,
'filename' => 'migrate-test-file-2.pdf',
'filepath' => 'sites/default/files/migrate-test-file-2.pdf',
'filemime' => 'application/pdf',
'filesize' => 204124,
'status' => 1,
'timestamp' => 1382255662,
],
];
return $tests;
}
@@ -84,15 +84,14 @@ class FileTest extends MigrateSqlSourceTestBase {
],
];
// The expected results will include only the first three files, since we
// are configuring the plugin to filter out the file with the null URI
// scheme.
$tests[0]['expected_data'] = array_slice($tests[0]['source_data']['file_managed'], 0, 3);
// The expected results will include only the first two files, since the
// plugin will filter out files with either the null URI scheme or the
// temporary scheme.
$tests[0]['expected_data'] = array_slice($tests[0]['source_data']['file_managed'], 0, 2);
// The filepath property will vary by URI scheme.
$tests[0]['expected_data'][0]['filepath'] = 'sites/default/files/cube.jpeg';
$tests[0]['expected_data'][1]['filepath'] = '/path/to/private/files/cube.jpeg';
$tests[0]['expected_data'][2]['filepath'] = '/tmp/cube.jpeg';
// Do an automatic count.
$tests[0]['expected_count'] = NULL;
@@ -102,10 +101,61 @@ class FileTest extends MigrateSqlSourceTestBase {
'constants' => [
'source_base_path' => '/path/to/files',
],
// Only return files which use one of these URI schemes.
'scheme' => ['public', 'private', 'temporary'],
];
// Test getting only public files.
$tests[1]['source_data'] = $tests[0]['source_data'];
$tests[1]['expected_data'] = [
[
'fid' => '1',
'uid' => '1',
'filename' => 'cube.jpeg',
'uri' => 'public://cube.jpeg',
'filemime' => 'image/jpeg',
'filesize' => '3620',
'status' => '1',
'timestamp' => '1421727515',
],
];
// Do an automatic count.
$tests[1]['expected_count'] = NULL;
// Set up plugin configuration.
$tests[1]['configuration'] = [
'constants' => [
'source_base_path' => '/path/to/files',
],
'scheme' => ['public'],
];
// Test getting only public files when configuration scheme is not an array.
$tests[2]['source_data'] = $tests[0]['source_data'];
$tests[2]['expected_data'] = [
[
'fid' => '1',
'uid' => '1',
'filename' => 'cube.jpeg',
'uri' => 'public://cube.jpeg',
'filemime' => 'image/jpeg',
'filesize' => '3620',
'status' => '1',
'timestamp' => '1421727515',
],
];
// Do an automatic count.
$tests[2]['expected_count'] = NULL;
// Set up plugin configuration.
$tests[2]['configuration'] = [
'constants' => [
'source_base_path' => '/path/to/files',
],
'scheme' => 'public',
];
return $tests;
}
@@ -10,6 +10,7 @@ use Drupal\file\Entity\File;
* @group file
*/
class SaveDataTest extends FileManagedUnitTestBase {
/**
* Test the file_save_data() function when no filename is provided.
*/
@@ -10,6 +10,7 @@ use Drupal\file\Entity\File;
* @group file
*/
class SaveTest extends FileManagedUnitTestBase {
public function testFileSave() {
// Create a new file entity.
$file = File::create([
@@ -10,6 +10,7 @@ use Drupal\file\Entity\File;
* @group file
*/
class SpaceUsedTest extends FileManagedUnitTestBase {
protected function setUp() {
parent::setUp();
@@ -15,6 +15,7 @@ use Drupal\node\Entity\NodeType;
* @group file
*/
class UsageTest extends FileManagedUnitTestBase {
/**
* Tests \Drupal\file\FileUsage\DatabaseFileUsageBackend::listUsage().
*/
@@ -26,7 +27,7 @@ class UsageTest extends FileManagedUnitTestBase {
'module' => 'testing',
'type' => 'foo',
'id' => 1,
'count' => 1
'count' => 1,
])
->execute();
db_insert('file_usage')
@@ -35,7 +36,7 @@ class UsageTest extends FileManagedUnitTestBase {
'module' => 'testing',
'type' => 'bar',
'id' => 2,
'count' => 2
'count' => 2,
])
->execute();
@@ -8,6 +8,7 @@ namespace Drupal\Tests\file\Kernel;
* @group file
*/
class ValidateTest extends FileManagedUnitTestBase {
/**
* Test that the validators passed into are checked.
*/
@@ -137,7 +137,6 @@ class ValidatorTest extends FileManagedUnitTestBase {
$this->assertEqual(count($errors), 1, 'An error reported for 0 length filename.', 'File');
}
/**
* Test file_validate_size().
*/
@@ -48,7 +48,7 @@ class FileViewsFieldAccessTest extends FieldFieldAccessTestBase {
'uri' => 'public://test.txt',
'status' => TRUE,
'langcode' => 'fr',
'uid' => $user->id()
'uid' => $user->id(),
]);
$file->save();
@@ -33,7 +33,7 @@ class FileCckTest extends UnitTestCase {
$migration = $this->prophesize(MigrationInterface::class);
// The plugin's processFieldValues() method will call
// The plugin's defineValueProcessPipeline() 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().
@@ -64,7 +64,7 @@ class FileCckTest extends UnitTestCase {
return [
['image', 'imagefield_widget'],
['file', 'filefield_widget'],
['file', 'x_widget']
['file', 'x_widget'],
];
}
@@ -33,7 +33,7 @@ class FileCckTest extends UnitTestCase {
$migration = $this->prophesize(MigrationInterface::class);
// The plugin's processFieldValues() method will call
// The plugin's defineValueProcessPipeline() 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().
@@ -69,7 +69,7 @@ class FileCckTest extends UnitTestCase {
return [
['image', 'imagefield_widget'],
['file', 'filefield_widget'],
['file', 'x_widget']
['file', 'x_widget'],
];
}
@@ -32,7 +32,7 @@ class ImageCckTest extends UnitTestCase {
$migration = $this->prophesize(MigrationInterface::class);
// The plugin's processFieldValues() method will call
// The plugin's defineValueProcessPipeline() 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().
@@ -0,0 +1,19 @@
<?php
namespace Drupal\Tests\file\Unit\Plugin\migrate\field\d6;
/**
* @coversDefaultClass \Drupal\file\Plugin\migrate\field\d6\FileField
* @group legacy
* @group file
*/
class FileFieldLegacyTest extends FileFieldTest {
/**
* @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 testDefineValueProcessPipeline($method = 'processFieldValues') {
parent::testDefineValueProcessPipeline($method);
}
}
@@ -32,7 +32,7 @@ class FileFieldTest extends UnitTestCase {
$migration = $this->prophesize(MigrationInterface::class);
// The plugin's processFieldValues() method will call
// The plugin's defineValueProcessPipeline() 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().
@@ -44,10 +44,10 @@ class FileFieldTest extends UnitTestCase {
}
/**
* @covers ::processFieldValues
* @covers ::defineValueProcessPipeline
*/
public function testProcessFieldValues() {
$this->plugin->processFieldValues($this->migration, 'somefieldname', []);
public function testDefineValueProcessPipeline($method = 'defineValueProcessPipeline') {
$this->plugin->$method($this->migration, 'somefieldname', []);
$expected = [
'plugin' => 'd6_field_file',
@@ -63,7 +63,7 @@ class FileFieldTest extends UnitTestCase {
return [
['image', 'imagefield_widget'],
['file', 'filefield_widget'],
['file', 'x_widget']
['file', 'x_widget'],
];
}
@@ -0,0 +1,19 @@
<?php
namespace Drupal\Tests\file\Unit\Plugin\migrate\field\d7;
/**
* @coversDefaultClass \Drupal\file\Plugin\migrate\field\d7\FileField
* @group legacy
* @group file
*/
class FileFieldLegacyTest extends FileFieldTest {
/**
* @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 testDefineValueProcessPipeline($method = 'processFieldValues') {
parent::testDefineValueProcessPipeline($method);
}
}
@@ -32,7 +32,7 @@ class FileFieldTest extends UnitTestCase {
$migration = $this->prophesize(MigrationInterface::class);
// The plugin's processFieldValues() method will call
// The plugin's defineValueProcessPipeline() 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().
@@ -44,10 +44,10 @@ class FileFieldTest extends UnitTestCase {
}
/**
* @covers ::processFieldValues
* @covers ::defineValueProcessPipeline
*/
public function testProcessFieldValues() {
$this->plugin->processFieldValues($this->migration, 'somefieldname', []);
public function testDefineValueProcessPipeline($method = 'defineValueProcessPipeline') {
$this->plugin->$method($this->migration, 'somefieldname', []);
$expected = [
'plugin' => 'sub_process',
@@ -68,7 +68,7 @@ class FileFieldTest extends UnitTestCase {
return [
['image', 'imagefield_widget'],
['file', 'filefield_widget'],
['file', 'x_widget']
['file', 'x_widget'],
];
}
@@ -0,0 +1,18 @@
<?php
namespace Drupal\Tests\file\Unit\Plugin\migrate\field\d7;
/**
* @group legacy
* @group file
*/
class ImageFieldLegacyTest extends ImageFieldTest {
/**
* @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 testDefineValueProcessPipeline($method = 'processFieldValues') {
parent::testDefineValueProcessPipeline($method);
}
}
@@ -32,7 +32,7 @@ class ImageFieldTest extends UnitTestCase {
$migration = $this->prophesize(MigrationInterface::class);
// The plugin's processFieldValues() method will call
// The plugin's defineValueProcessPipeline() 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().
@@ -44,11 +44,12 @@ class ImageFieldTest extends UnitTestCase {
}
/**
* @covers ::processFieldValues
* @runInSeparateProcess
* @covers ::defineValueProcessPipeline
* @expectedDeprecation 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. See https://www.drupal.org/node/2936061.
*/
public function testProcessFieldValues() {
$this->plugin->processFieldValues($this->migration, 'somefieldname', []);
public function testDefineValueProcessPipeline($method = 'defineValueProcessPipeline') {
$this->plugin->$method($this->migration, 'somefieldname', []);
$expected = [
'plugin' => 'sub_process',