Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ported over the fixes in #11858 "Check media Parent for permissions when setting correct MediaType" to target v8 #12233

Merged
merged 4 commits into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@
<key alias="orClickHereToUpload">or click here to choose files</key>
<key alias="dragFilesHereToUpload">You can drag files here to upload</key>
<key alias="disallowedFileType">Cannot upload this file, it does not have an approved file type</key>
<key alias="disallowedMediaType">Cannot upload this file, the media type with alias '%0%' is not allowed here</key>
<key alias="invalidFileName">Cannot upload this file, it does not have a valid file name</key>
<key alias="maxFileSize">Max file size is</key>
<key alias="mediaRoot">Media root</key>
Expand Down Expand Up @@ -1448,6 +1449,8 @@ To manage your website, simply open the Umbraco backoffice and start adding cont
<key alias="invalidUserPermissionsText">Insufficient user permissions, could not complete the operation</key>
<key alias="operationCancelledHeader">Cancelled</key>
<key alias="operationCancelledText">Operation was cancelled by a 3rd party add-in</key>
<key alias="folderUploadNotAllowed">This file is being uploaded as part of a folder, but creating a new folder is not allowed here</key>
<key alias="folderCreationNotAllowed">Creating a new folder is not allowed here</key>
<key alias="contentPublishedFailedByEvent">Publishing was cancelled by a 3rd party add-in</key>
<key alias="contentTypeDublicatePropertyType">Property type already exists</key>
<key alias="contentTypePropertyTypeCreated">Property type created</key>
Expand Down
6 changes: 5 additions & 1 deletion src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
Original file line number Diff line number Diff line change
Expand Up @@ -331,8 +331,9 @@
<area alias="media">
<key alias="clickToUpload">Click to upload</key>
<key alias="orClickHereToUpload">or click here to choose files</key>
<key alias="dragFilesHereToUpload">You can drag files here to upload.</key>
<key alias="dragFilesHereToUpload">You can drag files here to upload.</key>
<key alias="disallowedFileType">Cannot upload this file, it does not have an approved file type</key>
<key alias="disallowedMediaType">Cannot upload this file, the media type with alias '%0%' is not allowed here</key>
<key alias="invalidFileName">Cannot upload this file, it does not have a valid file name</key>
<key alias="maxFileSize">Max file size is</key>
<key alias="mediaRoot">Media root</key>
Expand Down Expand Up @@ -1460,6 +1461,9 @@ To manage your website, simply open the Umbraco backoffice and start adding cont
<key alias="invalidUserPermissionsText">Insufficient user permissions, could not complete the operation</key>
<key alias="operationCancelledHeader">Cancelled</key>
<key alias="operationCancelledText">Operation was cancelled by a 3rd party add-in</key>
<key alias="folderUploadNotAllowed">This file is being uploaded as part of a folder, but creating a new folder is not allowed here</key>
<key alias="folderCreationNotAllowed">Creating a new folder is not allowed here</key>
<key alias="contentPublishedFailedByEvent">Publishing was cancelled by a 3rd party add-in</key>
<key alias="contentTypeDublicatePropertyType">Property type already exists</key>
<key alias="contentTypePropertyTypeCreated">Property type created</key>
<key alias="contentTypePropertyTypeCreatedText"><![CDATA[Name: %0% <br /> DataType: %1%]]></key>
Expand Down
191 changes: 139 additions & 52 deletions src/Umbraco.Web/Editors/MediaController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -640,10 +647,16 @@ public async Task<HttpResponseMessage> 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);

Expand All @@ -653,7 +666,7 @@ public async Task<HttpResponseMessage> 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 =
Expand Down Expand Up @@ -691,88 +704,139 @@ public async Task<HttpResponseMessage> PostAddFile()
}
}

var mediaTypeAlias = string.Empty;
var allMediaTypes = Services.MediaTypeService.GetAll().ToList();
var allowedContentTypes = new HashSet<IMediaType>();

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)
{
var fileName = file.Headers.ContentDisposition.FileName.Trim(Constants.CharArrays.DoubleQuote).TrimEnd();
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
});
}
}

Expand All @@ -792,6 +856,29 @@ public async Task<HttpResponseMessage> 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;
Expand Down