V12: Dropzone should handle internal and external errors when uploading (#14579)

* fix: for safety measure check that a file is truthy before trying to upload it

* fix: push an error when file.$error is encountered to make sure it does not get uploaded

* fix: mark file as done if it errors

* format error messages

* fix: check for maxFileSize before uploading pasted images in tinymce

* remove the image from the DOM if any error is encountered

* feat: add property to fileManager to get and format the maxFileSize

* fix: make tinymce use fileManager to get maxFileSize

* fix(image cropper): check for maxFileSize before setting file to upload

* multiply by 1000 to get bytes
This commit is contained in:
Jacob Overgaard
2023-07-24 08:17:58 +00:00
committed by GitHub
parent 04a10be2a9
commit 41c7e34059
5 changed files with 71 additions and 11 deletions

View File

@@ -148,6 +148,17 @@ angular.module("umbraco.directives")
*/
function _upload(file) {
if (!file) {
return;
}
if (file.$error) {
file.done = true;
scope.processed.push(file);
file.messages.push({type: "Error", header: "Error"});
return;
}
scope.propertyAlias = scope.propertyAlias ? scope.propertyAlias : "umbracoFile";
scope.contentTypeAlias = scope.contentTypeAlias ? scope.contentTypeAlias : "umbracoAutoSelect";

View File

@@ -7,8 +7,10 @@
* @param {any} fileManager
* @param {any} mediaHelper
* @param {any} angularHelper
* @param {any} $attrs
* @param {any} notificationsService
*/
function umbPropertyFileUploadController($scope, $q, fileManager, mediaHelper, angularHelper, $attrs) {
function umbPropertyFileUploadController($scope, $q, fileManager, mediaHelper, angularHelper, $attrs, notificationsService) {
//NOTE: this component supports multiple files, though currently the uploader does not but perhaps sometime in the future
// we'd want it to, so i'll leave the multiple file support in place
@@ -271,15 +273,25 @@
if (args.files && args.files.length > 0) {
const filesAllowed = [];
for (let i = 0; i < args.files.length; i++) {
if (fileManager.maxFileSize && args.files[i].size > fileManager.maxFileSize) {
notificationsService.error(`File upload "${args.files[i].name}"`, `File size of ${args.files[i].size / 1000} KB exceeds the maximum allowed size of ${fileManager.maxFileSize / 1000} KB`);
} else {
filesAllowed.push(args.files[i]);
}
}
//set the files collection
fileManager.setFiles({
propertyAlias: vm.propertyAlias,
files: args.files,
files: filesAllowed,
culture: vm.culture,
segment: vm.segment
});
updateModelFromSelectedFiles(args.files).then(function(newVal) {
updateModelFromSelectedFiles(filesAllowed).then(function(newVal) {
angularHelper.safeApply($scope,
function() {
//pass in the file names and the model files

View File

@@ -14,6 +14,17 @@ function fileManager($rootScope) {
var mgr = {
/**
* @ngdoc property
* @name umbraco.services.fileManager#maxFileSize
* @propertyOf umbraco.services.fileManager
* @type {Number}
* @default 0
* @description
* The max file size allowed to be uploaded to the server in bytes
*/
maxFileSize: parseInt(Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize ?? '0', 10) * 1000,
/**
* @ngdoc function
* @name umbraco.services.fileManager#setFiles

View File

@@ -9,7 +9,7 @@
* @doc https://www.tiny.cloud/docs/tinymce/6/
*/
function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, stylesheetResource, macroResource, macroService,
$routeParams, umbRequestHelper, angularHelper, userService, editorService, entityResource, eventsService, localStorageService, mediaHelper) {
$routeParams, umbRequestHelper, angularHelper, userService, editorService, entityResource, eventsService, localStorageService, mediaHelper, fileManager) {
//These are absolutely required in order for the macros to render inline
//we put these as extended elements because they get merged on top of the normal allowed elements by tiny mce
@@ -202,6 +202,17 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
function uploadImageHandler(blobInfo, progress) {
return new Promise(function (resolve, reject) {
const blob = blobInfo.blob();
// if the file size is greater than the max file size, reject it
if (fileManager.maxFileSize > 0 && blob.size > fileManager.maxFileSize) {
reject({
message: `The file size (${blob.size / 1000} KB) exceeded the maximum allowed size of ${fileManager.maxFileSize / 1000} KB.`,
remove: true
});
return;
}
const xhr = new XMLHttpRequest();
xhr.open('POST', Umbraco.Sys.ServerVariables.umbracoUrls.tinyMceApiBaseUrl + 'UploadImage');
@@ -222,12 +233,18 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
};
xhr.onerror = function () {
reject('Image upload failed due to a XHR Transport error. Code: ' + xhr.status);
reject({
message: 'Image upload failed due to a XHR Transport error. Code: ' + xhr.status,
remove: true
});
};
xhr.onload = function () {
if (xhr.status < 200 || xhr.status >= 300) {
reject('HTTP Error: ' + xhr.status);
reject({
message: 'HTTP Error: ' + xhr.status,
remove: true
});
return;
}
@@ -237,7 +254,10 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
data = data.split("\n");
if (!data.length > 1) {
reject('Unrecognized text string: ' + data);
reject({
message: 'Unrecognized text string: ' + data,
remove: true
});
return;
}
@@ -246,12 +266,18 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
try {
json = JSON.parse(data[1]);
} catch (e) {
reject('Invalid JSON: ' + data + ' - ' + e.message);
reject({
message: 'Invalid JSON: ' + data + ' - ' + e.message,
remove: true
});
return;
}
if (!json || typeof json.tmpLocation !== 'string') {
reject('Invalid JSON: ' + data);
reject({
message: 'Invalid JSON: ' + data,
remove: true
});
return;
}
@@ -265,7 +291,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
};
const formData = new FormData();
formData.append('file', blobInfo.blob(), blobInfo.blob().name);
formData.append('file', blob, blob.name);
xhr.send(formData);
});

View File

@@ -56,7 +56,7 @@
<div>
<span>{{ file.name }}</span>
<span ng-if="file.messages.length > 0 || file.$error" class="file-messages">
<span class="errorMessage color-red" ng-repeat="message in file.messages">{{message.header}}: {{message.message}}</span>
<span class="errorMessage color-red" ng-repeat="message in ::file.messages" ng-if="message.message">{{::message.header}}: {{::message.message}}</span>
<span ng-if="file.$error === 'pattern'" class="errorMessage color-red"><localize key="media_disallowedFileType"></localize></span>
<span ng-if="file.$error === 'maxSize'" class="errorMessage color-red"><localize key="media_maxFileSize"></localize> "{{maxFileSize}}"</span>
</span>