Files
Umbraco-CMS/src/Umbraco.Web.BackOffice/Controllers/MediaPickerThreeController.cs
Mads Rasmussen d089825537 Feature: Media Picker drag and drop upload directly on property editor (#13393)
* prototype drag and drop upload

* Add upload image endpoint

* Add MediaPickerThreeController.cs

* Revert "Add upload image endpoint"

This reverts commit 4bb5865480737a00b723968e3bbba317b252acf7.

* Update IIOHelper dependency

* show preview when uploading a new media item

* open uploaded media in media entry editor

* map data from uploaded media entry to support cropper

* add crop data to uploaded media item

* remove media library buttons for media entries not created in the media library

* Implement temp images save & add to media picker 3

* Implement ITemporaryImageService

* Remove save logic from MediaPicker3PropertyEditor

* Dont use a TempImageDto

* Add GetByAlias endpoint

* Add additonal xml doc

* Refactor to take array of aliases

* Add FromQuery attribute

* Formatting

* add resource to get media types by alias

* validate file size and file type based on server variables

* Update OpenApi.json
Add media picker three to BackOfficeServerVariables

* rename endpoint to upload media

* Use baseurl Method

* Dont upload in rte folder

* pass params correctly to end point

* queue files before uploading

* handle invalid files

* progress bar design adjustments

* only create data url for images

* disable edit and name buttons when uploading

* fix missing error messages for invalid files

* add temp location to media entry

* Add startNode to TemporaryImageService.cs

* Refactor get by alias

* Rename to GetAllFiltered

* use getAllFiltered method

* remove autoselect option

* fix missing alias when selecting media type

* fix file filter

* don't overwrite invalid entries from dropping new files

* add disallowed files to filter

* remove console.log

* move media uploader logic to reusable function

* fix missing tmp location

* attach media type alias to the mediaEntry

* support readonly mode

* show discard changes when files has been dropped

* add disabled prop to button group

* emit events when upload queue starts and ends

* pass node to media picker property editor

* add service to keep track of all uploads in progress

* add upload in progress to uploadTracker when the queue starts and ends

* disabled buttons when any upload is in progress

* return a subscription to align with eventsService

* Fix up cases where StartNodeId was null

* scope css

* Show filename in dialog for selecting media type

* reuse translation from media library dropzone

* Don't check for only images

* Remove composer

* Add mediaTypeAlias to TemporaryImageService

* Rename ITemporaryImageService to ITemporaryMediaService

* prefix client side only props with $ so we don't send unnecessary data to the server

* use prefixed dataURL in media entry editor

* render icon for non images

* fix auto select media type

Co-authored-by: Zeegaan <nge@umbraco.dk>
Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
2022-11-18 11:47:42 +01:00

114 lines
4.2 KiB
C#

using System.Net;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Dictionary;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Media;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
using Umbraco.Cms.Web.Common.ActionsResults;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;
namespace Umbraco.Cms.Web.BackOffice.Controllers;
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[Authorize(Policy = AuthorizationPolicies.SectionAccessMedia)]
public class MediaPickerThreeController : ContentControllerBase
{
private readonly IHostingEnvironment _hostingEnvironment;
private readonly ContentSettings _contentSettings;
private readonly IImageUrlGenerator _imageUrlGenerator;
private readonly IIOHelper _ioHelper;
public MediaPickerThreeController(
ICultureDictionary cultureDictionary,
ILoggerFactory loggerFactory,
IShortStringHelper shortStringHelper,
IEventMessagesFactory eventMessages,
ILocalizedTextService localizedTextService,
IJsonSerializer serializer,
IHostingEnvironment hostingEnvironment,
IOptionsSnapshot<ContentSettings> contentSettings,
IImageUrlGenerator imageUrlGenerator,
IIOHelper ioHelper)
: base(cultureDictionary, loggerFactory, shortStringHelper, eventMessages, localizedTextService, serializer)
{
_hostingEnvironment = hostingEnvironment;
_contentSettings = contentSettings.Value;
_imageUrlGenerator = imageUrlGenerator;
_ioHelper = ioHelper;
}
[HttpPost]
public async Task<IActionResult> UploadMedia(List<IFormFile> file)
{
// Create an unique folder path to help with concurrent users to avoid filename clash
var imageTempPath =
_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempFileUploads + "/" + Guid.NewGuid());
// Ensure image temp path exists
if (Directory.Exists(imageTempPath) == false)
{
Directory.CreateDirectory(imageTempPath);
}
// Must have a file
if (file.Count == 0)
{
return NotFound();
}
// Should only have one file
if (file.Count > 1)
{
return new UmbracoProblemResult("Only one file can be uploaded at a time", HttpStatusCode.BadRequest);
}
// Really we should only have one file per request to this endpoint
IFormFile formFile = file.First();
var fileName = formFile.FileName.Trim(new[] { '\"' }).TrimEnd();
var safeFileName = fileName.ToSafeFileName(ShortStringHelper);
var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLowerInvariant();
if (_contentSettings.IsFileAllowedForUpload(ext) == false)
{
// Throw some error - to say can't upload this IMG type
return new UmbracoProblemResult("This is not an image filetype extension that is approved", HttpStatusCode.BadRequest);
}
var newFilePath = imageTempPath + Path.DirectorySeparatorChar + safeFileName;
var relativeNewFilePath = GetRelativePath(newFilePath);
await using (FileStream stream = System.IO.File.Create(newFilePath))
{
await formFile.CopyToAsync(stream);
}
return Ok(new { tmpLocation = relativeNewFilePath });
}
// Use private method istead of _ioHelper.GetRelativePath as that is relative for the webroot and not the content root.
private string GetRelativePath(string path)
{
if (path.IsFullPath())
{
var rootDirectory = _hostingEnvironment.MapPathContentRoot("~");
var relativePath = _ioHelper.PathStartsWith(path, rootDirectory) ? path[rootDirectory.Length..] : path;
path = relativePath;
}
return PathUtility.EnsurePathIsApplicationRootPrefixed(path);
}
}