diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
index 2b62c6ba2156..2940caaf1db6 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
@@ -329,6 +329,7 @@
or click here to choose files
You can drag files here to upload
Cannot upload this file, it does not have an approved file type
+ Cannot upload this file, the media type with alias '%0%' is not allowed here
Cannot upload this file, it does not have a valid file name
Max file size is
Media root
@@ -1448,6 +1449,8 @@ To manage your website, simply open the Umbraco backoffice and start adding cont
Insufficient user permissions, could not complete the operation
Cancelled
Operation was cancelled by a 3rd party add-in
+ This file is being uploaded as part of a folder, but creating a new folder is not allowed here
+ Creating a new folder is not allowed here
Publishing was cancelled by a 3rd party add-in
Property type already exists
Property type created
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
index 79121d015d13..8d3e7d171788 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
@@ -331,8 +331,9 @@
Click to upload
or click here to choose files
- You can drag files here to upload.
+ You can drag files here to upload.
Cannot upload this file, it does not have an approved file type
+ Cannot upload this file, the media type with alias '%0%' is not allowed here
Cannot upload this file, it does not have a valid file name
Max file size is
Media root
@@ -1460,6 +1461,9 @@ To manage your website, simply open the Umbraco backoffice and start adding cont
Insufficient user permissions, could not complete the operation
Cancelled
Operation was cancelled by a 3rd party add-in
+ This file is being uploaded as part of a folder, but creating a new folder is not allowed here
+ Creating a new folder is not allowed here
+ Publishing was cancelled by a 3rd party add-in
Property type already exists
Property type created
DataType: %1%]]>
diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs
index 18cbac332821..45d7aebae1b5 100644
--- a/src/Umbraco.Web/Editors/MediaController.cs
+++ b/src/Umbraco.Web/Editors/MediaController.cs
@@ -598,6 +598,13 @@ public MediaItemDisplay PostAddFolder(PostedFolder folder)
{
var intParentId = GetParentIdAsInt(folder.ParentId, validatePermissions:true);
+ var isFolderAllowed = IsFolderCreationAllowedHere(intParentId);
+ if (isFolderAllowed == false)
+ {
+ throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse(
+ Services.TextService.Localize("speechBubbles", "folderCreationNotAllowed")));
+ }
+
var mediaService = Services.MediaService;
var f = mediaService.CreateMedia(folder.Name, intParentId, Constants.Conventions.MediaTypes.Folder);
@@ -640,10 +647,16 @@ public async Task PostAddFile()
var tempFiles = new PostedFiles();
var mediaService = Services.MediaService;
+ var localizedTextService = Services.TextService;
//in case we pass a path with a folder in it, we will create it and upload media to it.
if (result.FormData.ContainsKey("path"))
{
+ if (!IsFolderCreationAllowedHere(parentId))
+ {
+ AddCancelMessage(tempFiles, message: "speechBubbles/folderUploadNotAllowed");
+ return Request.CreateResponse(HttpStatusCode.OK, tempFiles);
+ }
var folders = result.FormData["path"].Split(Constants.CharArrays.ForwardSlash);
@@ -653,7 +666,7 @@ public async Task PostAddFile()
IMedia folderMediaItem;
//if uploading directly to media root and not a subfolder
- if (parentId == -1)
+ if (parentId == Constants.System.Root)
{
//look for matching folder
folderMediaItem =
@@ -691,6 +704,44 @@ public async Task PostAddFile()
}
}
+ var mediaTypeAlias = string.Empty;
+ var allMediaTypes = Services.MediaTypeService.GetAll().ToList();
+ var allowedContentTypes = new HashSet();
+
+ if (parentId != Constants.System.Root)
+ {
+ var mediaFolderItem = mediaService.GetById(parentId);
+ var mediaFolderType = allMediaTypes.FirstOrDefault(x => x.Alias == mediaFolderItem.ContentType.Alias);
+
+ if (mediaFolderType != null)
+ {
+ IMediaType mediaTypeItem = null;
+
+ foreach (ContentTypeSort allowedContentType in mediaFolderType.AllowedContentTypes)
+ {
+ IMediaType checkMediaTypeItem = allMediaTypes.FirstOrDefault(x => x.Id == allowedContentType.Id.Value);
+ allowedContentTypes.Add(checkMediaTypeItem);
+
+ var fileProperty = checkMediaTypeItem?.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == Constants.Conventions.Media.File);
+ if (fileProperty != null)
+ {
+ mediaTypeItem = checkMediaTypeItem;
+ }
+ }
+
+ //Only set the permission-based mediaType if we only allow 1 specific file under this parent.
+ if (allowedContentTypes.Count == 1 && mediaTypeItem != null)
+ {
+ mediaTypeAlias = mediaTypeItem.Alias;
+ }
+ }
+ }
+ else
+ {
+ var typesAllowedAtRoot = allMediaTypes.Where(x => x.AllowedAsRoot).ToList();
+ allowedContentTypes.UnionWith(typesAllowedAtRoot);
+ }
+
//get the files
foreach (var file in result.FileData)
{
@@ -698,81 +749,94 @@ public async Task PostAddFile()
var safeFileName = fileName.ToSafeFileName();
var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower();
- if (Current.Configs.Settings().Content.IsFileAllowedForUpload(ext))
+ if (!Current.Configs.Settings().Content.IsFileAllowedForUpload(ext))
+ {
+ tempFiles.Notifications.Add(new Notification(
+ localizedTextService.Localize("speechBubbles", "operationFailedHeader"),
+ localizedTextService.Localize("media", "disallowedFileType"),
+ NotificationStyle.Warning));
+ continue;
+ }
+
+ if (string.IsNullOrEmpty(mediaTypeAlias))
{
- var mediaType = Constants.Conventions.MediaTypes.File;
+ mediaTypeAlias = Constants.Conventions.MediaTypes.File;
if (result.FormData["contentTypeAlias"] == Constants.Conventions.MediaTypes.AutoSelect)
{
- var mediaTypes = Services.MediaTypeService.GetAll();
// Look up MediaTypes
- foreach (var mediaTypeItem in mediaTypes)
+ foreach (var mediaTypeItem in allMediaTypes)
{
- var fileProperty = mediaTypeItem.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == "umbracoFile");
- if (fileProperty != null) {
- var dataTypeKey = fileProperty.DataTypeKey;
- var dataType = Services.DataTypeService.GetDataType(dataTypeKey);
-
- if (dataType != null && dataType.Configuration is IFileExtensionsConfig fileExtensionsConfig) {
- var fileExtensions = fileExtensionsConfig.FileExtensions;
- if (fileExtensions != null)
- {
- if (fileExtensions.Where(x => x.Value == ext).Count() != 0)
- {
- mediaType = mediaTypeItem.Alias;
- break;
- }
- }
- }
+ var fileProperty = mediaTypeItem.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == Constants.Conventions.Media.File);
+ if (fileProperty == null)
+ {
+ continue;
+ }
+
+ var dataTypeKey = fileProperty.DataTypeKey;
+ var dataType = Services.DataTypeService.GetDataType(dataTypeKey);
+
+ if (dataType == null || dataType.Configuration is not IFileExtensionsConfig fileExtensionsConfig)
+ {
+ continue;
+ }
+
+ var fileExtensions = fileExtensionsConfig.FileExtensions;
+ if (fileExtensions == null || fileExtensions.All(x => x.Value != ext))
+ {
+ continue;
}
+ mediaTypeAlias = mediaTypeItem.Alias;
+ break;
}
// If media type is still File then let's check if it's an image.
- if (mediaType == Constants.Conventions.MediaTypes.File && Current.Configs.Settings().Content.ImageFileTypes.Contains(ext))
+ if (mediaTypeAlias == Constants.Conventions.MediaTypes.File && Current.Configs.Settings().Content.ImageFileTypes.Contains(ext))
{
- mediaType = Constants.Conventions.MediaTypes.Image;
+ mediaTypeAlias = Constants.Conventions.MediaTypes.Image;
}
}
else
{
- mediaType = result.FormData["contentTypeAlias"];
+ mediaTypeAlias = result.FormData["contentTypeAlias"];
}
+ }
- var mediaItemName = fileName.ToFriendlyName();
+ if (allowedContentTypes.Any(x => x.Alias == mediaTypeAlias) == false)
+ {
+ tempFiles.Notifications.Add(new Notification(
+ localizedTextService.Localize("speechBubbles", "operationFailedHeader"),
+ localizedTextService.Localize("media", "disallowedMediaType", new[] { mediaTypeAlias }),
+ NotificationStyle.Warning));
+ continue;
+ }
- var f = mediaService.CreateMedia(mediaItemName, parentId, mediaType, Security.CurrentUser.Id);
+ var mediaItemName = fileName.ToFriendlyName();
- var fileInfo = new FileInfo(file.LocalFileName);
- var fs = fileInfo.OpenReadWithRetry();
- if (fs == null) throw new InvalidOperationException("Could not acquire file stream");
- using (fs)
- {
- f.SetValue(Services.ContentTypeBaseServices, Constants.Conventions.Media.File,fileName, fs);
- }
+ var createdMediaItem = mediaService.CreateMedia(mediaItemName, parentId, mediaTypeAlias, Security.CurrentUser.Id);
- var saveResult = mediaService.Save(f, Security.CurrentUser.Id);
- if (saveResult == false)
- {
- AddCancelMessage(tempFiles,
- message: Services.TextService.Localize("speechBubbles", "operationCancelledText") + " -- " + mediaItemName);
- }
- else
- {
- tempFiles.UploadedFiles.Add(new ContentPropertyFile
- {
- FileName = fileName,
- PropertyAlias = Constants.Conventions.Media.File,
- TempFilePath = file.LocalFileName
- });
- }
+ var fileInfo = new FileInfo(file.LocalFileName);
+ var fs = fileInfo.OpenReadWithRetry();
+ if (fs == null) throw new InvalidOperationException("Could not acquire file stream");
+ using (fs)
+ {
+ createdMediaItem.SetValue(Services.ContentTypeBaseServices, Constants.Conventions.Media.File, fileName, fs);
+ }
+
+ var saveResult = mediaService.Save(createdMediaItem, Security.CurrentUser.Id);
+ if (saveResult == false)
+ {
+ AddCancelMessage(tempFiles, message: "speechBubbles/operationCancelledText" + " -- " + mediaItemName);
}
else
{
- tempFiles.Notifications.Add(new Notification(
- Services.TextService.Localize("speechBubbles", "operationFailedHeader"),
- Services.TextService.Localize("media", "disallowedFileType"),
- NotificationStyle.Warning));
+ tempFiles.UploadedFiles.Add(new ContentPropertyFile
+ {
+ FileName = fileName,
+ PropertyAlias = Constants.Conventions.Media.File,
+ TempFilePath = file.LocalFileName
+ });
}
}
@@ -792,6 +856,29 @@ public async Task PostAddFile()
return Request.CreateResponse(HttpStatusCode.OK, tempFiles);
}
+ private bool IsFolderCreationAllowedHere(int parentId)
+ {
+ var allMediaTypes = Services.MediaTypeService.GetAll().ToList();
+ var isFolderAllowed = false;
+ if (parentId == Constants.System.Root)
+ {
+ var typesAllowedAtRoot = allMediaTypes.Where(ct => ct.AllowedAsRoot).ToList();
+ isFolderAllowed = typesAllowedAtRoot.Any(x => x.Alias == Constants.Conventions.MediaTypes.Folder);
+ }
+ else
+ {
+ var parentMediaType = Services.MediaService.GetById(parentId);
+ var mediaFolderType = allMediaTypes.FirstOrDefault(x => x.Alias == parentMediaType.ContentType.Alias);
+ if (mediaFolderType != null)
+ {
+ isFolderAllowed =
+ mediaFolderType.AllowedContentTypes.Any(x => x.Alias == Constants.Conventions.MediaTypes.Folder);
+ }
+ }
+
+ return isFolderAllowed;
+ }
+
private IMedia FindInChildren(int mediaId, string nameToFind, string contentTypeAlias)
{
const int pageSize = 500;