diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs
similarity index 78%
rename from src/Umbraco.Web/Editors/MediaController.cs
rename to src/Umbraco.Web.BackOffice/Controllers/MediaController.cs
index 6791e480d7..0a44d360d9 100644
--- a/src/Umbraco.Web/Editors/MediaController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs
@@ -4,12 +4,9 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
-using System.Net.Http.Formatting;
using System.Text;
using System.Threading.Tasks;
-using System.Web.Http;
-using System.Web.Http.Controllers;
-using System.Web.Http.ModelBinding;
+using Microsoft.AspNetCore.Mvc;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
@@ -17,12 +14,9 @@ using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
-using Umbraco.Web.Mvc;
-using Umbraco.Web.WebApi;
-using Umbraco.Core.Cache;
-using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Dictionary;
+using Umbraco.Core.Events;
using Umbraco.Core.Models.ContentEditing;
using Umbraco.Core.Models.Editors;
using Umbraco.Core.Models.Entities;
@@ -30,13 +24,18 @@ using Umbraco.Core.Models.Validation;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.PropertyEditors;
-using Umbraco.Core.Strings;
using Umbraco.Web.ContentApps;
-using Umbraco.Web.Editors.Filters;
using Umbraco.Web.WebApi.Filters;
using Constants = Umbraco.Core.Constants;
using Umbraco.Core.Mapping;
-using Umbraco.Web.Routing;
+using Umbraco.Core.Strings;
+using Umbraco.Extensions;
+using Umbraco.Web.BackOffice.Controllers;
+using Umbraco.Web.BackOffice.Filters;
+using Umbraco.Web.Common.Attributes;
+using Umbraco.Web.Common.Exceptions;
+using Umbraco.Web.Editors.Binders;
+using Umbraco.Web.Security;
namespace Umbraco.Web.Editors
{
@@ -46,47 +45,55 @@ namespace Umbraco.Web.Editors
///
[PluginController("UmbracoApi")]
[UmbracoApplicationAuthorize(Constants.Applications.Media)]
- [MediaControllerControllerConfiguration]
public class MediaController : ContentControllerBase
{
private readonly IContentSettings _contentSettings;
private readonly IIOHelper _ioHelper;
-
+ private readonly IMediaTypeService _mediaTypeService;
+ private readonly IMediaService _mediaService;
+ private readonly IEntityService _entityService;
+ private readonly IWebSecurity _webSecurity;
+ private readonly UmbracoMapper _umbracoMapper;
+ private readonly IDataTypeService _dataTypeService;
+ private readonly ILocalizedTextService _localizedTextService;
+ private readonly ISqlContext _sqlContext;
+ private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider;
+ private readonly IRelationService _relationService;
public MediaController(
ICultureDictionary cultureDictionary,
- PropertyEditorCollection propertyEditors,
- IGlobalSettings globalSettings,
- IUmbracoContextAccessor umbracoContextAccessor,
- ISqlContext sqlContext,
- ServiceContext services,
- AppCaches appCaches,
- IProfilingLogger logger,
- IRuntimeState runtimeState,
- IMediaFileSystem mediaFileSystem,
+ ILogger logger,
IShortStringHelper shortStringHelper,
- UmbracoMapper umbracoMapper,
+ IEventMessagesFactory eventMessages,
+ ILocalizedTextService localizedTextService,
IContentSettings contentSettings,
IIOHelper ioHelper,
- IPublishedUrlProvider publishedUrlProvider)
- : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider)
+ IMediaTypeService mediaTypeService,
+ IMediaService mediaService,
+ IEntityService entityService,
+ IWebSecurity webSecurity,
+ UmbracoMapper umbracoMapper,
+ IDataTypeService dataTypeService,
+ ISqlContext sqlContext,
+ IContentTypeBaseServiceProvider contentTypeBaseServiceProvider,
+ IRelationService relationService,
+ PropertyEditorCollection propertyEditors,
+ IMediaFileSystem mediaFileSystem)
+ : base(cultureDictionary, logger, shortStringHelper, eventMessages, localizedTextService)
{
- _propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors));
+ _contentSettings = contentSettings;
+ _ioHelper = ioHelper;
+ _mediaTypeService = mediaTypeService;
+ _mediaService = mediaService;
+ _entityService = entityService;
+ _webSecurity = webSecurity;
+ _umbracoMapper = umbracoMapper;
+ _dataTypeService = dataTypeService;
+ _localizedTextService = localizedTextService;
+ _sqlContext = sqlContext;
+ _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider;
+ _relationService = relationService;
+ _propertyEditors = propertyEditors;
_mediaFileSystem = mediaFileSystem;
- _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings));
- _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper));
- }
-
- ///
- /// Configures this controller with a custom action selector
- ///
- private class MediaControllerControllerConfigurationAttribute : Attribute, IControllerConfiguration
- {
- public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
- {
- controllerSettings.Services.Replace(typeof(IHttpActionSelector), new ParameterSwapControllerActionSelector(
- new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetById", "id", typeof(int), typeof(Guid), typeof(Udi)),
- new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetChildren", "id", typeof(int), typeof(Guid), typeof(Udi))));
- }
}
///
@@ -98,14 +105,14 @@ namespace Umbraco.Web.Editors
// [OutgoingEditorModelEvent] // TODO introduce when moved to .NET Core
public MediaItemDisplay GetEmpty(string contentTypeAlias, int parentId)
{
- var contentType = Services.MediaTypeService.Get(contentTypeAlias);
+ var contentType = _mediaTypeService.Get(contentTypeAlias);
if (contentType == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
- var emptyContent = Services.MediaService.CreateMedia("", parentId, contentType.Alias, Security.GetUserId().ResultOr(Constants.Security.SuperUserId));
- var mapped = Mapper.Map(emptyContent);
+ var emptyContent = _mediaService.CreateMedia("", parentId, contentType.Alias, _webSecurity.GetUserId().ResultOr(Constants.Security.SuperUserId));
+ var mapped = _umbracoMapper.Map(emptyContent);
//remove the listview app if it exists
mapped.ContentApps = mapped.ContentApps.Where(x => x.Alias != "umbListView").ToList();
@@ -120,14 +127,14 @@ namespace Umbraco.Web.Editors
public MediaItemDisplay GetRecycleBin()
{
var apps = new List();
- apps.Add(ListViewContentAppFactory.CreateContentApp(Services.DataTypeService, _propertyEditors, "recycleBin", "media", Core.Constants.DataTypes.DefaultMediaListView));
+ apps.Add(ListViewContentAppFactory.CreateContentApp(_dataTypeService, _propertyEditors, "recycleBin", "media", Core.Constants.DataTypes.DefaultMediaListView));
apps[0].Active = true;
var display = new MediaItemDisplay
{
Id = Constants.System.RecycleBinMedia,
Alias = "recycleBin",
ParentId = -1,
- Name = Services.TextService.Localize("general/recycleBin"),
+ Name = _localizedTextService.Localize("general/recycleBin"),
ContentTypeAlias = "recycleBin",
CreateDate = DateTime.Now,
IsContainer = true,
@@ -145,9 +152,10 @@ namespace Umbraco.Web.Editors
///
// [OutgoingEditorModelEvent] // TODO introduce when moved to .NET Core
[EnsureUserPermissionForMedia("id")]
+ [DetermineAmbiguousActionByPassingParameters]
public MediaItemDisplay GetById(int id)
{
- var foundContent = GetObjectFromRequest(() => Services.MediaService.GetById(id));
+ var foundContent = GetObjectFromRequest(() => _mediaService.GetById(id));
if (foundContent == null)
{
@@ -155,7 +163,7 @@ namespace Umbraco.Web.Editors
//HandleContentNotFound will throw an exception
return null;
}
- return Mapper.Map(foundContent);
+ return _umbracoMapper.Map(foundContent);
}
///
@@ -165,9 +173,10 @@ namespace Umbraco.Web.Editors
///
// [OutgoingEditorModelEvent] // TODO introduce when moved to .NET Core
[EnsureUserPermissionForMedia("id")]
+ [DetermineAmbiguousActionByPassingParameters]
public MediaItemDisplay GetById(Guid id)
{
- var foundContent = GetObjectFromRequest(() => Services.MediaService.GetById(id));
+ var foundContent = GetObjectFromRequest(() => _mediaService.GetById(id));
if (foundContent == null)
{
@@ -175,7 +184,7 @@ namespace Umbraco.Web.Editors
//HandleContentNotFound will throw an exception
return null;
}
- return Mapper.Map(foundContent);
+ return _umbracoMapper.Map(foundContent);
}
///
@@ -185,6 +194,7 @@ namespace Umbraco.Web.Editors
///
// [OutgoingEditorModelEvent] // TODO introduce when moved to .NET Core
[EnsureUserPermissionForMedia("id")]
+ [DetermineAmbiguousActionByPassingParameters]
public MediaItemDisplay GetById(Udi id)
{
var guidUdi = id as GuidUdi;
@@ -201,10 +211,10 @@ namespace Umbraco.Web.Editors
///
///
//[FilterAllowedOutgoingMedia(typeof(IEnumerable))] // TODO introduce when moved to .NET Core
- public IEnumerable GetByIds([FromUri]int[] ids)
+ public IEnumerable GetByIds([FromQuery]int[] ids)
{
- var foundMedia = Services.MediaService.GetByIds(ids);
- return foundMedia.Select(media => Mapper.Map(media));
+ var foundMedia = _mediaService.GetByIds(ids);
+ return foundMedia.Select(media => _umbracoMapper.Map(media));
}
///
@@ -218,7 +228,7 @@ namespace Umbraco.Web.Editors
{
//Suggested convention for folder mediatypes - we can make this more or less complicated as long as we document it...
//if you create a media type, which has an alias that ends with ...Folder then its a folder: ex: "secureFolder", "bannerFolder", "Folder"
- var folderTypes = Services.MediaTypeService
+ var folderTypes = _mediaTypeService
.GetAll()
.Where(x => x.Alias.EndsWith("Folder"))
.Select(x => x.Id)
@@ -230,14 +240,14 @@ namespace Umbraco.Web.Editors
}
long total;
- var children = Services.MediaService.GetPagedChildren(id, pageNumber - 1, pageSize, out total,
+ var children = _mediaService.GetPagedChildren(id, pageNumber - 1, pageSize, out total,
//lookup these content types
- SqlContext.Query().Where(x => folderTypes.Contains(x.ContentTypeId)),
+ _sqlContext.Query().Where(x => folderTypes.Contains(x.ContentTypeId)),
Ordering.By("Name", Direction.Ascending));
return new PagedResult>(total, pageNumber, pageSize)
{
- Items = children.Select(Mapper.Map>)
+ Items = children.Select(_umbracoMapper.Map>)
};
}
@@ -249,8 +259,8 @@ namespace Umbraco.Web.Editors
{
// TODO: Add permissions check!
- return Services.MediaService.GetRootMedia()
- .Select(Mapper.Map>);
+ return _mediaService.GetRootMedia()
+ .Select(_umbracoMapper.Map>);
}
#region GetChildren
@@ -262,13 +272,14 @@ namespace Umbraco.Web.Editors
protected int[] UserStartNodes
{
- get { return _userStartNodes ?? (_userStartNodes = Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService)); }
+ get { return _userStartNodes ?? (_userStartNodes = _webSecurity.CurrentUser.CalculateMediaStartNodeIds(_entityService)); }
}
///
/// Returns the child media objects - using the entity INT id
///
- //[FilterAllowedOutgoingMedia(typeof(IEnumerable>), "Items")] // TODO introduce when moved to .NET Core
+ [FilterAllowedOutgoingMedia(typeof(IEnumerable>), "Items")] // TODO introduce when moved to .NET Core//[FilterAllowedOutgoingMedia(typeof(IEnumerable>), "Items")]
+ [DetermineAmbiguousActionByPassingParameters]
public PagedResult> GetChildren(int id,
int pageNumber = 0,
int pageSize = 0,
@@ -283,13 +294,13 @@ namespace Umbraco.Web.Editors
{
if (pageNumber > 0)
return new PagedResult>(0, 0, 0);
- var nodes = Services.MediaService.GetByIds(UserStartNodes).ToArray();
+ var nodes = _mediaService.GetByIds(UserStartNodes).ToArray();
if (nodes.Length == 0)
return new PagedResult>(0, 0, 0);
if (pageSize < nodes.Length) pageSize = nodes.Length; // bah
var pr = new PagedResult>(nodes.Length, pageNumber, pageSize)
{
- Items = nodes.Select(Mapper.Map>)
+ Items = nodes.Select(_umbracoMapper.Map>)
};
return pr;
}
@@ -304,11 +315,11 @@ namespace Umbraco.Web.Editors
if (filter.IsNullOrWhiteSpace() == false)
{
//add the default text filter
- queryFilter = SqlContext.Query()
+ queryFilter = _sqlContext.Query()
.Where(x => x.Name.Contains(filter));
}
- children = Services.MediaService
+ children = _mediaService
.GetPagedChildren(
id, (pageNumber - 1), pageSize,
out totalChildren,
@@ -318,7 +329,7 @@ namespace Umbraco.Web.Editors
else
{
//better to not use this without paging where possible, currently only the sort dialog does
- children = Services.MediaService.GetPagedChildren(id,0, int.MaxValue, out var total).ToList();
+ children = _mediaService.GetPagedChildren(id,0, int.MaxValue, out var total).ToList();
totalChildren = children.Count;
}
@@ -329,7 +340,7 @@ namespace Umbraco.Web.Editors
var pagedResult = new PagedResult>(totalChildren, pageNumber, pageSize);
pagedResult.Items = children
- .Select(Mapper.Map>);
+ .Select(_umbracoMapper.Map>);
return pagedResult;
}
@@ -345,7 +356,8 @@ namespace Umbraco.Web.Editors
///
///
///
- //[FilterAllowedOutgoingMedia(typeof(IEnumerable>), "Items")] // TODO introduce when moved to .NET Core
+ [FilterAllowedOutgoingMedia(typeof(IEnumerable>), "Items")]
+ [DetermineAmbiguousActionByPassingParameters]
public PagedResult> GetChildren(Guid id,
int pageNumber = 0,
int pageSize = 0,
@@ -354,7 +366,7 @@ namespace Umbraco.Web.Editors
bool orderBySystemField = true,
string filter = "")
{
- var entity = Services.EntityService.Get(id);
+ var entity = _entityService.Get(id);
if (entity != null)
{
return GetChildren(entity.Id, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter);
@@ -373,7 +385,8 @@ namespace Umbraco.Web.Editors
///
///
///
- //[FilterAllowedOutgoingMedia(typeof(IEnumerable>), "Items")] // TODO introduce when moved to .NET Core
+ [FilterAllowedOutgoingMedia(typeof(IEnumerable>), "Items")]
+ [DetermineAmbiguousActionByPassingParameters]
public PagedResult> GetChildren(Udi id,
int pageNumber = 0,
int pageSize = 0,
@@ -385,7 +398,7 @@ namespace Umbraco.Web.Editors
var guidUdi = id as GuidUdi;
if (guidUdi != null)
{
- var entity = Services.EntityService.Get(guidUdi.Guid);
+ var entity = _entityService.Get(guidUdi.Guid);
if (entity != null)
{
return GetChildren(entity.Id, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter);
@@ -406,7 +419,7 @@ namespace Umbraco.Web.Editors
[HttpPost]
public HttpResponseMessage DeleteById(int id)
{
- var foundMedia = GetObjectFromRequest(() => Services.MediaService.GetById(id));
+ var foundMedia = GetObjectFromRequest(() => _mediaService.GetById(id));
if (foundMedia == null)
{
@@ -416,7 +429,7 @@ namespace Umbraco.Web.Editors
//if the current item is in the recycle bin
if (foundMedia.Trashed == false)
{
- var moveResult = Services.MediaService.MoveToRecycleBin(foundMedia, Security.GetUserId().ResultOr(Constants.Security.SuperUserId));
+ var moveResult = _mediaService.MoveToRecycleBin(foundMedia, _webSecurity.GetUserId().ResultOr(Constants.Security.SuperUserId));
if (moveResult == false)
{
//returning an object of INotificationModel will ensure that any pending
@@ -426,7 +439,7 @@ namespace Umbraco.Web.Editors
}
else
{
- var deleteResult = Services.MediaService.Delete(foundMedia, Security.GetUserId().ResultOr(Constants.Security.SuperUserId));
+ var deleteResult = _mediaService.Delete(foundMedia, _webSecurity.GetUserId().ResultOr(Constants.Security.SuperUserId));
if (deleteResult == false)
{
//returning an object of INotificationModel will ensure that any pending
@@ -450,11 +463,11 @@ namespace Umbraco.Web.Editors
var destinationParentID = move.ParentId;
var sourceParentID = toMove.ParentId;
- var moveResult = Services.MediaService.Move(toMove, move.ParentId, Security.GetUserId().ResultOr(Constants.Security.SuperUserId));
+ var moveResult = _mediaService.Move(toMove, move.ParentId, _webSecurity.GetUserId().ResultOr(Constants.Security.SuperUserId));
if (sourceParentID == destinationParentID)
{
- return Request.CreateValidationErrorResponse(new SimpleNotificationModel(new BackOfficeNotification("",Services.TextService.Localize("media/moveToSameFolderFailed"),NotificationStyle.Error)));
+ return Request.CreateValidationErrorResponse(new SimpleNotificationModel(new BackOfficeNotification("",_localizedTextService.Localize("media/moveToSameFolderFailed"),NotificationStyle.Error)));
}
if (moveResult == false)
{
@@ -519,17 +532,17 @@ namespace Umbraco.Web.Editors
{
//ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue!
// add the model state to the outgoing object and throw validation response
- var forDisplay = Mapper.Map(contentItem.PersistedContent);
+ var forDisplay = _umbracoMapper.Map(contentItem.PersistedContent);
forDisplay.Errors = ModelState.ToErrorDictionary();
throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
}
}
//save the item
- var saveStatus = Services.MediaService.Save(contentItem.PersistedContent, Security.GetUserId().ResultOr(Constants.Security.SuperUserId));
+ var saveStatus = _mediaService.Save(contentItem.PersistedContent, _webSecurity.GetUserId().ResultOr(Constants.Security.SuperUserId));
//return the updated model
- var display = Mapper.Map(contentItem.PersistedContent);
+ var display = _umbracoMapper.Map(contentItem.PersistedContent);
//lastly, if it is not valid, add the model state to the outgoing object and throw a 403
HandleInvalidModelState(display);
@@ -542,8 +555,8 @@ namespace Umbraco.Web.Editors
if (saveStatus.Success)
{
display.AddSuccessNotification(
- Services.TextService.Localize("speechBubbles/editMediaSaved"),
- Services.TextService.Localize("speechBubbles/editMediaSavedText"));
+ _localizedTextService.Localize("speechBubbles/editMediaSaved"),
+ _localizedTextService.Localize("speechBubbles/editMediaSavedText"));
}
else
{
@@ -572,9 +585,9 @@ namespace Umbraco.Web.Editors
[HttpPost]
public HttpResponseMessage EmptyRecycleBin()
{
- Services.MediaService.EmptyRecycleBin(Security.GetUserId().ResultOr(Constants.Security.SuperUserId));
+ _mediaService.EmptyRecycleBin(_webSecurity.GetUserId().ResultOr(Constants.Security.SuperUserId));
- return Request.CreateNotificationSuccessResponse(Services.TextService.Localize("defaultdialogs/recycleBinIsEmpty"));
+ return Request.CreateNotificationSuccessResponse(_localizedTextService.Localize("defaultdialogs/recycleBinIsEmpty"));
}
///
@@ -596,7 +609,7 @@ namespace Umbraco.Web.Editors
return Request.CreateResponse(HttpStatusCode.OK);
}
- var mediaService = Services.MediaService;
+ var mediaService = _mediaService;
var sortedMedia = new List();
try
{
@@ -621,12 +634,12 @@ namespace Umbraco.Web.Editors
{
var intParentId = GetParentIdAsInt(folder.ParentId, validatePermissions:true);
- var mediaService = Services.MediaService;
+ var mediaService = _mediaService;
var f = mediaService.CreateMedia(folder.Name, intParentId, Constants.Conventions.MediaTypes.Folder);
- mediaService.Save(f, Security.CurrentUser.Id);
+ mediaService.Save(f, _webSecurity.CurrentUser.Id);
- return Mapper.Map(f);
+ return _umbracoMapper.Map(f);
}
///
@@ -662,7 +675,7 @@ namespace Umbraco.Web.Editors
int parentId = GetParentIdAsInt(currentFolderId, validatePermissions: true);
var tempFiles = new PostedFiles();
- var mediaService = Services.MediaService;
+ var mediaService = _mediaService;
//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"))
@@ -739,21 +752,21 @@ namespace Umbraco.Web.Editors
var mediaItemName = fileName.ToFriendlyName();
- var f = mediaService.CreateMedia(mediaItemName, parentId, mediaType, Security.CurrentUser.Id);
+ var f = mediaService.CreateMedia(mediaItemName, parentId, mediaType, _webSecurity.CurrentUser.Id);
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(_mediaFileSystem, ShortStringHelper, Services.ContentTypeBaseServices, Constants.Conventions.Media.File,fileName, fs);
+ f.SetValue(_mediaFileSystem, ShortStringHelper, _contentTypeBaseServiceProvider, Constants.Conventions.Media.File,fileName, fs);
}
- var saveResult = mediaService.Save(f, Security.CurrentUser.Id);
+ var saveResult = mediaService.Save(f, _webSecurity.CurrentUser.Id);
if (saveResult == false)
{
AddCancelMessage(tempFiles,
- message: Services.TextService.Localize("speechBubbles/operationCancelledText") + " -- " + mediaItemName);
+ message: _localizedTextService.Localize("speechBubbles/operationCancelledText") + " -- " + mediaItemName);
}
else
{
@@ -768,8 +781,8 @@ namespace Umbraco.Web.Editors
else
{
tempFiles.Notifications.Add(new BackOfficeNotification(
- Services.TextService.Localize("speechBubbles/operationFailedHeader"),
- Services.TextService.Localize("media/disallowedFileType"),
+ _localizedTextService.Localize("speechBubbles/operationFailedHeader"),
+ _localizedTextService.Localize("media/disallowedFileType"),
NotificationStyle.Warning));
}
}
@@ -797,8 +810,8 @@ namespace Umbraco.Web.Editors
var total = long.MaxValue;
while (page * pageSize < total)
{
- var children = Services.MediaService.GetPagedChildren(mediaId, page, pageSize, out total,
- SqlContext.Query().Where(x => x.Name == nameToFind));
+ var children = _mediaService.GetPagedChildren(mediaId, page, pageSize, out total,
+ _sqlContext.Query().Where(x => x.Name == nameToFind));
foreach (var c in children)
return c; //return first one if any are found
}
@@ -814,7 +827,7 @@ namespace Umbraco.Web.Editors
/// and if that check fails an unauthorized exception will occur
///
///
- private int GetParentIdAsInt(string parentId, bool validatePermissions)
+ private ActionResult GetParentIdAsInt(string parentId, bool validatePermissions)
{
int intParentId;
@@ -831,37 +844,36 @@ namespace Umbraco.Web.Editors
Guid idGuid;
if (Guid.TryParse(parentId, out idGuid))
{
- var entity = Services.EntityService.Get(idGuid);
+ var entity = _entityService.Get(idGuid);
if (entity != null)
{
intParentId = entity.Id;
}
else
{
- throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, "The passed id doesn't exist"));
+ return NotFound("The passed id doesn't exist");
}
}
else
{
- throw new HttpResponseException(
- Request.CreateValidationErrorResponse("The request was not formatted correctly, the parentId is not an integer, Guid or UDI"));
+ throw HttpResponseException.CreateValidationErrorResponse("The request was not formatted correctly, the parentId is not an integer, Guid or UDI");
}
}
//ensure the user has access to this folder by parent id!
if (validatePermissions && CheckPermissions(
- new Dictionary(),
- Security.CurrentUser,
- Services.MediaService,
- Services.EntityService,
+ new Dictionary
internal class ContentModelBinderHelper
{
- // public TModelSave BindModelFromMultipartRequest(ActionContext actionContext,
- // ModelBindingContext bindingContext)
- // where TModelSave : IHaveUploadedFiles
- // {
- // var result = actionContext.ReadAsMultipart(Constants.SystemDirectories.TempFileUploads);
- //
- // var model = actionContext.GetModelFromMultipartRequest(result, "contentItem");
- //
- // //get the files
- // foreach (var file in result.FileData)
- // {
- // //The name that has been assigned in JS has 2 or more parts. The second part indicates the property id
- // // for which the file belongs, the remaining parts are just metadata that can be used by the property editor.
- // var parts = file.Headers.ContentDisposition.Name.Trim('\"').Split('_');
- // if (parts.Length < 2)
- // {
- // bindingContext.HttpContext.SetReasonPhrase(
- // "The request was not formatted correctly the file name's must be underscore delimited");
- // throw new HttpResponseException(HttpStatusCode.BadRequest);
- // }
- //
- // var propAlias = parts[1];
- //
- // //if there are 3 parts part 3 is always culture
- // string culture = null;
- // if (parts.Length > 2)
- // {
- // culture = parts[2];
- // //normalize to null if empty
- // if (culture.IsNullOrWhiteSpace())
- // {
- // culture = null;
- // }
- // }
- //
- // //if there are 4 parts part 4 is always segment
- // string segment = null;
- // if (parts.Length > 3)
- // {
- // segment = parts[3];
- // //normalize to null if empty
- // if (segment.IsNullOrWhiteSpace())
- // {
- // segment = null;
- // }
- // }
- //
- // // TODO: anything after 4 parts we can put in metadata
- //
- // var fileName = file.Headers.ContentDisposition.FileName.Trim('\"');
- //
- // model.UploadedFiles.Add(new ContentPropertyFile
- // {
- // TempFilePath = file.LocalFileName,
- // PropertyAlias = propAlias,
- // Culture = culture,
- // Segment = segment,
- // FileName = fileName
- // });
- // }
- //
- // bindingContext.Model = model;
- //
- // return model;
- // }
+ public async Task BindModelFromMultipartRequestAsync(
+ IJsonSerializer jsonSerializer,
+ IHostingEnvironment hostingEnvironment,
+ ModelBindingContext bindingContext)
+ where T: class, IHaveUploadedFiles
+ {
+ var modelName = bindingContext.ModelName;
- ///
+ var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
+
+ if (valueProviderResult == ValueProviderResult.None)
+ {
+ return null;
+ }
+ bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);
+
+ var value = valueProviderResult.FirstValue;
+
+ // Check if the argument value is null or empty
+ if (string.IsNullOrEmpty(value))
+ {
+ return null;
+ }
+ var model = jsonSerializer.Deserialize(value);
+ if (model is null)
+ {
+ // Non-integer arguments result in model state errors
+ bindingContext.ModelState.TryAddModelError(
+ modelName, $"Cannot deserialize {modelName} as {nameof(T)}.");
+
+ return null;
+ }
+
+ //Handle file uploads
+ foreach (var formFile in bindingContext.HttpContext.Request.Form.Files)
+ {
+ //The name that has been assigned in JS has 2 or more parts. The second part indicates the property id
+ // for which the file belongs, the remaining parts are just metadata that can be used by the property editor.
+ var parts = formFile.Name.Trim('\"').Split('_');
+ if (parts.Length < 2)
+ {
+ bindingContext.HttpContext.SetReasonPhrase( "The request was not formatted correctly the file name's must be underscore delimited");
+ throw new HttpResponseException(HttpStatusCode.BadRequest);
+ }
+ var propAlias = parts[1];
+
+ //if there are 3 parts part 3 is always culture
+ string culture = null;
+ if (parts.Length > 2)
+ {
+ culture = parts[2];
+ //normalize to null if empty
+ if (culture.IsNullOrWhiteSpace())
+ {
+ culture = null;
+ }
+ }
+
+ //if there are 4 parts part 4 is always segment
+ string segment = null;
+ if (parts.Length > 3)
+ {
+ segment = parts[3];
+ //normalize to null if empty
+ if (segment.IsNullOrWhiteSpace())
+ {
+ segment = null;
+ }
+ }
+
+ // TODO: anything after 4 parts we can put in metadata
+
+ var fileName = formFile.FileName.Trim('\"');
+
+ var tempFileUploadFolder = hostingEnvironment.MapPathContentRoot(Core.Constants.SystemDirectories.TempFileUploads);
+ Directory.CreateDirectory(tempFileUploadFolder);
+ var tempFilePath = Path.Combine(tempFileUploadFolder, Guid.NewGuid().ToString());
+
+ using (var stream = System.IO.File.Create(tempFilePath))
+ {
+ await formFile.CopyToAsync(stream);
+ }
+
+ model.UploadedFiles.Add(new ContentPropertyFile
+ {
+ TempFilePath = tempFilePath,
+ PropertyAlias = propAlias,
+ Culture = culture,
+ Segment = segment,
+ FileName = fileName
+ });
+ }
+
+ return model;
+ }
+
+ ///
/// we will now assign all of the values in the 'save' model to the DTO object
///
///
diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/MediaItemBinder.cs b/src/Umbraco.Web.BackOffice/ModelBinders/MediaItemBinder.cs
index a617d03625..7f7831eab2 100644
--- a/src/Umbraco.Web.BackOffice/ModelBinders/MediaItemBinder.cs
+++ b/src/Umbraco.Web.BackOffice/ModelBinders/MediaItemBinder.cs
@@ -1,78 +1,82 @@
-// using System;
-// using System.Threading.Tasks;
-// using Microsoft.AspNetCore.Mvc.ModelBinding;
-// using Umbraco.Core.Mapping;
-// using Umbraco.Core.Models;
-// using Umbraco.Core.Services;
-// using Umbraco.Web.Models.ContentEditing;
-//
-// namespace Umbraco.Web.Editors.Binders
-// {
-// ///
-// /// The model binder for
-// ///
-// internal class MediaItemBinder : IModelBinder
-// {
-// private readonly IMediaService _mediaService;
-// private readonly UmbracoMapper _umbracoMapper;
-// private readonly IMediaTypeService _mediaTypeService;
-// private readonly ContentModelBinderHelper _modelBinderHelper;
-//
-//
-// public MediaItemBinder(IMediaService mediaService, UmbracoMapper umbracoMapper, IMediaTypeService mediaTypeService)
-// {
-// _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService));
-// _umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper));
-// _mediaTypeService = mediaTypeService ?? throw new ArgumentNullException(nameof(mediaTypeService));
-//
-// _modelBinderHelper = new ContentModelBinderHelper();
-// }
-//
-// ///
-// /// Creates the model from the request and binds it to the context
-// ///
-// ///
-// ///
-// public Task BindModelAsync(ModelBindingContext bindingContext)
-// {
-// var actionContext = bindingContext.ActionContext;
-// var model = _modelBinderHelper.BindModelFromMultipartRequest(actionContext, bindingContext);
-// if (model == null)
-// {
-// bindingContext.Result = ModelBindingResult.Failed();
-// return Task.CompletedTask;
-// }
-//
-// model.PersistedContent = ContentControllerBase.IsCreatingAction(model.Action) ? CreateNew(model) : GetExisting(model);
-//
-// //create the dto from the persisted model
-// if (model.PersistedContent != null)
-// {
-// model.PropertyCollectionDto = _umbracoMapper.Map(model.PersistedContent);
-// //now map all of the saved values to the dto
-// _modelBinderHelper.MapPropertyValuesFromSaved(model, model.PropertyCollectionDto);
-// }
-//
-// model.Name = model.Name.Trim();
-//
-// bindingContext.Result = ModelBindingResult.Success(model);
-// return Task.CompletedTask;
-// }
-//
-// private IMedia GetExisting(MediaItemSave model)
-// {
-// return _mediaService.GetById(Convert.ToInt32(model.Id));
-// }
-//
-// private IMedia CreateNew(MediaItemSave model)
-// {
-// var mediaType = _mediaTypeService.Get(model.ContentTypeAlias);
-// if (mediaType == null)
-// {
-// throw new InvalidOperationException("No media type found with alias " + model.ContentTypeAlias);
-// }
-// return new Core.Models.Media(model.Name, model.ParentId, mediaType);
-// }
-//
-// }
-// }
+using System;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Umbraco.Core.Hosting;
+using Umbraco.Core.Mapping;
+using Umbraco.Core.Models;
+using Umbraco.Core.Serialization;
+using Umbraco.Core.Services;
+using Umbraco.Web.Models.ContentEditing;
+
+namespace Umbraco.Web.Editors.Binders
+{
+ ///
+ /// The model binder for
+ ///
+ internal class MediaItemBinder : IModelBinder
+ {
+ private readonly IJsonSerializer _jsonSerializer;
+ private readonly IHostingEnvironment _hostingEnvironment;
+ private readonly IMediaService _mediaService;
+ private readonly UmbracoMapper _umbracoMapper;
+ private readonly IMediaTypeService _mediaTypeService;
+ private readonly ContentModelBinderHelper _modelBinderHelper;
+
+
+ public MediaItemBinder(IJsonSerializer jsonSerializer, IHostingEnvironment hostingEnvironment, IMediaService mediaService, UmbracoMapper umbracoMapper, IMediaTypeService mediaTypeService)
+ {
+ _jsonSerializer = jsonSerializer ?? throw new ArgumentNullException(nameof(jsonSerializer));
+ _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
+ _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService));
+ _umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper));
+ _mediaTypeService = mediaTypeService ?? throw new ArgumentNullException(nameof(mediaTypeService));
+
+ _modelBinderHelper = new ContentModelBinderHelper();
+ }
+
+ ///
+ /// Creates the model from the request and binds it to the context
+ ///
+ ///
+ ///
+ public async Task BindModelAsync(ModelBindingContext bindingContext)
+ {
+
+ var model = await _modelBinderHelper.BindModelFromMultipartRequestAsync(_jsonSerializer, _hostingEnvironment, bindingContext);
+ if (model == null)
+ {
+ return;
+ }
+
+ model.PersistedContent = ContentControllerBase.IsCreatingAction(model.Action) ? CreateNew(model) : GetExisting(model);
+
+ //create the dto from the persisted model
+ if (model.PersistedContent != null)
+ {
+ model.PropertyCollectionDto = _umbracoMapper.Map(model.PersistedContent);
+ //now map all of the saved values to the dto
+ _modelBinderHelper.MapPropertyValuesFromSaved(model, model.PropertyCollectionDto);
+ }
+
+ model.Name = model.Name.Trim();
+
+ bindingContext.Result = ModelBindingResult.Success(model);
+ }
+
+ private IMedia GetExisting(MediaItemSave model)
+ {
+ return _mediaService.GetById(Convert.ToInt32(model.Id));
+ }
+
+ private IMedia CreateNew(MediaItemSave model)
+ {
+ var mediaType = _mediaTypeService.Get(model.ContentTypeAlias);
+ if (mediaType == null)
+ {
+ throw new InvalidOperationException("No media type found with alias " + model.ContentTypeAlias);
+ }
+ return new Core.Models.Media(model.Name, model.ParentId, mediaType);
+ }
+
+ }
+}
diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/MemberBinder.cs b/src/Umbraco.Web.BackOffice/ModelBinders/MemberBinder.cs
index 8e857a687f..23a4d3849f 100644
--- a/src/Umbraco.Web.BackOffice/ModelBinders/MemberBinder.cs
+++ b/src/Umbraco.Web.BackOffice/ModelBinders/MemberBinder.cs
@@ -1,144 +1,148 @@
-// using System;
-// using System.Collections.Generic;
-// using Umbraco.Core;
-// using Umbraco.Core.Models;
-// using Umbraco.Core.Services;
-// using Umbraco.Core.Strings;
-// using Umbraco.Web.Models.ContentEditing;
-// using System.Linq;
-// using System.Threading.Tasks;
-// using Microsoft.AspNetCore.Mvc.ModelBinding;
-// using Umbraco.Core.Mapping;
-//
-// namespace Umbraco.Web.Editors.Binders
-// {
-// ///
-// /// The model binder for
-// ///
-// internal class MemberBinder : IModelBinder
-// {
-// private readonly ContentModelBinderHelper _modelBinderHelper;
-// private readonly IShortStringHelper _shortStringHelper;
-// private readonly UmbracoMapper _umbracoMapper;
-// private readonly IMemberService _memberService;
-// private readonly IMemberTypeService _memberTypeService;
-//
-// public MemberBinder(
-// IShortStringHelper shortStringHelper,
-// UmbracoMapper umbracoMapper,
-// IMemberService memberService,
-// IMemberTypeService memberTypeService)
-// {
-//
-// _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper));
-// _umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper));
-// _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService));
-// _memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService));
-// _modelBinderHelper = new ContentModelBinderHelper();
-// }
-//
-// ///
-// /// Creates the model from the request and binds it to the context
-// ///
-// ///
-// ///
-// ///
-// public Task BindModelAsync(ModelBindingContext bindingContext)
-// {
-// var actionContext = bindingContext.ActionContext;
-// var model = _modelBinderHelper.BindModelFromMultipartRequest(actionContext, bindingContext);
-// if (model == null)
-// {
-// bindingContext.Result = ModelBindingResult.Failed();
-// return Task.CompletedTask;
-// }
-//
-// model.PersistedContent = ContentControllerBase.IsCreatingAction(model.Action) ? CreateNew(model) : GetExisting(model);
-//
-// //create the dto from the persisted model
-// if (model.PersistedContent != null)
-// {
-// model.PropertyCollectionDto = _umbracoMapper.Map(model.PersistedContent);
-// //now map all of the saved values to the dto
-// _modelBinderHelper.MapPropertyValuesFromSaved(model, model.PropertyCollectionDto);
-// }
-//
-// model.Name = model.Name.Trim();
-//
-// bindingContext.Result = ModelBindingResult.Success(model);
-// return Task.CompletedTask;
-// }
-//
-// ///
-// /// Returns an IMember instance used to bind values to and save (depending on the membership scenario)
-// ///
-// ///
-// ///
-// private IMember GetExisting(MemberSave model)
-// {
-// return GetExisting(model.Key);
-// }
-//
-// private IMember GetExisting(Guid key)
-// {
-// var member = _memberService.GetByKey(key);
-// if (member == null)
-// {
-// throw new InvalidOperationException("Could not find member with key " + key);
-// }
-//
-// return member;
-// }
-//
-// ///
-// /// Gets an instance of IMember used when creating a member
-// ///
-// ///
-// ///
-// ///
-// /// Depending on whether a custom membership provider is configured this will return different results.
-// ///
-// private IMember CreateNew(MemberSave model)
-// {
-// var contentType = _memberTypeService.Get(model.ContentTypeAlias);
-// if (contentType == null)
-// {
-// throw new InvalidOperationException("No member type found with alias " + model.ContentTypeAlias);
-// }
-//
-// //remove all membership properties, these values are set with the membership provider.
-// FilterMembershipProviderProperties(contentType);
-//
-// //return the new member with the details filled in
-// return new Member(model.Name, model.Email, model.Username, model.Password.NewPassword, contentType);
-// }
-//
-// ///
-// /// This will remove all of the special membership provider properties which were required to display the property editors
-// /// for editing - but the values have been mapped back to the MemberSave object directly - we don't want to keep these properties
-// /// on the IMember because they will attempt to be persisted which we don't want since they might not even exist.
-// ///
-// ///
-// private void FilterMembershipProviderProperties(IContentTypeBase contentType)
-// {
-// var defaultProps = ConventionsHelper.GetStandardPropertyTypeStubs(_shortStringHelper);
-// //remove all membership properties, these values are set with the membership provider.
-// var exclude = defaultProps.Select(x => x.Value.Alias).ToArray();
-// FilterContentTypeProperties(contentType, exclude);
-// }
-//
-// private void FilterContentTypeProperties(IContentTypeBase contentType, IEnumerable exclude)
-// {
-// //remove all properties based on the exclusion list
-// foreach (var remove in exclude)
-// {
-// if (contentType.PropertyTypeExists(remove))
-// {
-// contentType.RemovePropertyType(remove);
-// }
-// }
-// }
-//
-//
-// }
-// }
+using System;
+using System.Collections.Generic;
+using Umbraco.Core;
+using Umbraco.Core.Models;
+using Umbraco.Core.Services;
+using Umbraco.Core.Strings;
+using Umbraco.Web.Models.ContentEditing;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Umbraco.Core.Hosting;
+using Umbraco.Core.Mapping;
+using Umbraco.Core.Serialization;
+
+namespace Umbraco.Web.Editors.Binders
+{
+ ///
+ /// The model binder for
+ ///
+ internal class MemberBinder : IModelBinder
+ {
+ private readonly ContentModelBinderHelper _modelBinderHelper;
+ private readonly IJsonSerializer _jsonSerializer;
+ private readonly IHostingEnvironment _hostingEnvironment;
+ private readonly IShortStringHelper _shortStringHelper;
+ private readonly UmbracoMapper _umbracoMapper;
+ private readonly IMemberService _memberService;
+ private readonly IMemberTypeService _memberTypeService;
+
+ public MemberBinder(
+ IJsonSerializer jsonSerializer,
+ IHostingEnvironment hostingEnvironment,
+ IShortStringHelper shortStringHelper,
+ UmbracoMapper umbracoMapper,
+ IMemberService memberService,
+ IMemberTypeService memberTypeService)
+ {
+ _jsonSerializer = jsonSerializer ?? throw new ArgumentNullException(nameof(jsonSerializer));
+ _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
+ _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper));
+ _umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper));
+ _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService));
+ _memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService));
+ _modelBinderHelper = new ContentModelBinderHelper();
+ }
+
+ ///
+ /// Creates the model from the request and binds it to the context
+ ///
+ ///
+ ///
+ ///
+ public async Task BindModelAsync(ModelBindingContext bindingContext)
+ {
+ var model = await _modelBinderHelper.BindModelFromMultipartRequestAsync(_jsonSerializer, _hostingEnvironment, bindingContext);
+ if (model == null)
+ {
+ return;
+ }
+
+ model.PersistedContent = ContentControllerBase.IsCreatingAction(model.Action) ? CreateNew(model) : GetExisting(model);
+
+ //create the dto from the persisted model
+ if (model.PersistedContent != null)
+ {
+ model.PropertyCollectionDto = _umbracoMapper.Map(model.PersistedContent);
+ //now map all of the saved values to the dto
+ _modelBinderHelper.MapPropertyValuesFromSaved(model, model.PropertyCollectionDto);
+ }
+
+ model.Name = model.Name.Trim();
+
+ bindingContext.Result = ModelBindingResult.Success(model);
+ }
+
+ ///
+ /// Returns an IMember instance used to bind values to and save (depending on the membership scenario)
+ ///
+ ///
+ ///
+ private IMember GetExisting(MemberSave model)
+ {
+ return GetExisting(model.Key);
+ }
+
+ private IMember GetExisting(Guid key)
+ {
+ var member = _memberService.GetByKey(key);
+ if (member == null)
+ {
+ throw new InvalidOperationException("Could not find member with key " + key);
+ }
+
+ return member;
+ }
+
+ ///
+ /// Gets an instance of IMember used when creating a member
+ ///
+ ///
+ ///
+ ///
+ /// Depending on whether a custom membership provider is configured this will return different results.
+ ///
+ private IMember CreateNew(MemberSave model)
+ {
+ var contentType = _memberTypeService.Get(model.ContentTypeAlias);
+ if (contentType == null)
+ {
+ throw new InvalidOperationException("No member type found with alias " + model.ContentTypeAlias);
+ }
+
+ //remove all membership properties, these values are set with the membership provider.
+ FilterMembershipProviderProperties(contentType);
+
+ //return the new member with the details filled in
+ return new Member(model.Name, model.Email, model.Username, model.Password.NewPassword, contentType);
+ }
+
+ ///
+ /// This will remove all of the special membership provider properties which were required to display the property editors
+ /// for editing - but the values have been mapped back to the MemberSave object directly - we don't want to keep these properties
+ /// on the IMember because they will attempt to be persisted which we don't want since they might not even exist.
+ ///
+ ///
+ private void FilterMembershipProviderProperties(IContentTypeBase contentType)
+ {
+ var defaultProps = ConventionsHelper.GetStandardPropertyTypeStubs(_shortStringHelper);
+ //remove all membership properties, these values are set with the membership provider.
+ var exclude = defaultProps.Select(x => x.Value.Alias).ToArray();
+ FilterContentTypeProperties(contentType, exclude);
+ }
+
+ private void FilterContentTypeProperties(IContentTypeBase contentType, IEnumerable exclude)
+ {
+ //remove all properties based on the exclusion list
+ foreach (var remove in exclude)
+ {
+ if (contentType.PropertyTypeExists(remove))
+ {
+ contentType.RemovePropertyType(remove);
+ }
+ }
+ }
+
+
+ }
+}
diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs
deleted file mode 100644
index cf96b0ade6..0000000000
--- a/src/Umbraco.Web/Editors/MemberController.cs
+++ /dev/null
@@ -1,470 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Linq;
-using System.Net;
-using System.Net.Http;
-using System.Net.Http.Formatting;
-using System.Net.Http.Headers;
-using System.Threading.Tasks;
-using System.Web.Http;
-using System.Web.Http.ModelBinding;
-using Umbraco.Core;
-using Umbraco.Core.Cache;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Dictionary;
-using Umbraco.Core.Logging;
-using Umbraco.Core.Models;
-using Umbraco.Core.Models.ContentEditing;
-using Umbraco.Core.Models.Membership;
-using Umbraco.Core.Persistence;
-using Umbraco.Core.PropertyEditors;
-using Umbraco.Core.Security;
-using Umbraco.Core.Services;
-using Umbraco.Core.Services.Implement;
-using Umbraco.Core.Strings;
-using Umbraco.Web.ContentApps;
-using Umbraco.Web.Editors.Binders;
-using Umbraco.Web.Editors.Filters;
-using Umbraco.Web.Models.ContentEditing;
-using Umbraco.Web.Mvc;
-using Umbraco.Web.WebApi;
-using Umbraco.Web.WebApi.Filters;
-using Constants = Umbraco.Core.Constants;
-using Umbraco.Core.Mapping;
-using Umbraco.Web.Routing;
-
-namespace Umbraco.Web.Editors
-{
- ///
- /// This controller is decorated with the UmbracoApplicationAuthorizeAttribute which means that any user requesting
- /// access to ALL of the methods on this controller will need access to the member application.
- ///
- [PluginController("UmbracoApi")]
- [UmbracoApplicationAuthorize(Constants.Applications.Members)]
- [OutgoingNoHyphenGuidFormat]
- public class MemberController : ContentControllerBase
- {
- public MemberController(
- IMemberPasswordConfiguration passwordConfig,
- ICultureDictionary cultureDictionary,
- PropertyEditorCollection propertyEditors,
- IGlobalSettings globalSettings,
- IUmbracoContextAccessor umbracoContextAccessor,
- ISqlContext sqlContext,
- ServiceContext services,
- AppCaches appCaches,
- IProfilingLogger logger,
- IRuntimeState runtimeState,
- IShortStringHelper shortStringHelper,
- UmbracoMapper umbracoMapper,
- IPublishedUrlProvider publishedUrlProvider)
- : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider)
- {
- _passwordConfig = passwordConfig ?? throw new ArgumentNullException(nameof(passwordConfig));
- _propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors));
- _passwordSecurity = new LegacyPasswordSecurity(_passwordConfig);
- _passwordValidator = new ConfiguredPasswordValidator();
- }
-
- private readonly IMemberPasswordConfiguration _passwordConfig;
- private readonly PropertyEditorCollection _propertyEditors;
- private readonly LegacyPasswordSecurity _passwordSecurity;
- private readonly IPasswordValidator _passwordValidator;
-
- public PagedResult GetPagedResults(
- int pageNumber = 1,
- int pageSize = 100,
- string orderBy = "username",
- Direction orderDirection = Direction.Ascending,
- bool orderBySystemField = true,
- string filter = "",
- string memberTypeAlias = null)
- {
-
- if (pageNumber <= 0 || pageSize <= 0)
- {
- throw new NotSupportedException("Both pageNumber and pageSize must be greater than zero");
- }
-
- var members = Services.MemberService
- .GetAll((pageNumber - 1), pageSize, out var totalRecords, orderBy, orderDirection, orderBySystemField, memberTypeAlias, filter).ToArray();
- if (totalRecords == 0)
- {
- return new PagedResult(0, 0, 0);
- }
-
- var pagedResult = new PagedResult(totalRecords, pageNumber, pageSize)
- {
- Items = members
- .Select(x => Mapper.Map(x))
- };
- return pagedResult;
- }
-
- ///
- /// Returns a display node with a list view to render members
- ///
- ///
- ///
- public MemberListDisplay GetListNodeDisplay(string listName)
- {
- var foundType = Services.MemberTypeService.Get(listName);
- var name = foundType != null ? foundType.Name : listName;
-
- var apps = new List();
- apps.Add(ListViewContentAppFactory.CreateContentApp(Services.DataTypeService, _propertyEditors, listName, "member", Core.Constants.DataTypes.DefaultMembersListView));
- apps[0].Active = true;
-
- var display = new MemberListDisplay
- {
- ContentTypeAlias = listName,
- ContentTypeName = name,
- Id = listName,
- IsContainer = true,
- Name = listName == Constants.Conventions.MemberTypes.AllMembersListId ? "All Members" : name,
- Path = "-1," + listName,
- ParentId = -1,
- ContentApps = apps
- };
-
- return display;
- }
-
- ///
- /// Gets the content json for the member
- ///
- ///
- ///
- // [OutgoingEditorModelEvent] // TODO introduce when moved to .NET Core
- public MemberDisplay GetByKey(Guid key)
- {
- var foundMember = Services.MemberService.GetByKey(key);
- if (foundMember == null)
- {
- HandleContentNotFound(key);
- }
- return Mapper.Map(foundMember);
- }
-
- ///
- /// Gets an empty content item for the
- ///
- ///
- ///
- // [OutgoingEditorModelEvent] // TODO introduce when moved to .NET Core
- public MemberDisplay GetEmpty(string contentTypeAlias = null)
- {
- IMember emptyContent;
- if (contentTypeAlias == null)
- {
- throw new HttpResponseException(HttpStatusCode.NotFound);
- }
-
- var contentType = Services.MemberTypeService.Get(contentTypeAlias);
- if (contentType == null)
- {
- throw new HttpResponseException(HttpStatusCode.NotFound);
- }
-
- var passwordGenerator = new PasswordGenerator(_passwordConfig);
-
- emptyContent = new Member(contentType);
- emptyContent.AdditionalData["NewPassword"] = passwordGenerator.GeneratePassword();
- return Mapper.Map(emptyContent);
- }
-
- ///
- /// Saves member
- ///
- ///
- [FileUploadCleanupFilter]
- // [OutgoingEditorModelEvent] // TODO introduce when moved to .NET Core
- [MemberSaveValidation]
- public async Task PostSave(
- [ModelBinder(typeof(MemberBinder))]
- MemberSave contentItem)
- {
-
- //If we've reached here it means:
- // * Our model has been bound
- // * and validated
- // * any file attachments have been saved to their temporary location for us to use
- // * we have a reference to the DTO object and the persisted object
- // * Permissions are valid
-
- //map the properties to the persisted entity
- MapPropertyValues(contentItem);
-
- await ValidateMemberDataAsync(contentItem);
-
- //Unlike content/media - if there are errors for a member, we do NOT proceed to save them, we cannot so return the errors
- if (ModelState.IsValid == false)
- {
- var forDisplay = Mapper.Map(contentItem.PersistedContent);
- forDisplay.Errors = ModelState.ToErrorDictionary();
- throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
- }
-
- //We're gonna look up the current roles now because the below code can cause
- // events to be raised and developers could be manually adding roles to members in
- // their handlers. If we don't look this up now there's a chance we'll just end up
- // removing the roles they've assigned.
- var currRoles = Services.MemberService.GetAllRoles(contentItem.PersistedContent.Username);
- //find the ones to remove and remove them
- var rolesToRemove = currRoles.Except(contentItem.Groups).ToArray();
-
- //Depending on the action we need to first do a create or update using the membership provider
- // this ensures that passwords are formatted correctly and also performs the validation on the provider itself.
- switch (contentItem.Action)
- {
- case ContentSaveAction.Save:
- UpdateMemberData(contentItem);
- break;
- case ContentSaveAction.SaveNew:
- contentItem.PersistedContent = CreateMemberData(contentItem);
- break;
- default:
- //we don't support anything else for members
- throw new HttpResponseException(HttpStatusCode.NotFound);
- }
-
- //TODO: There's 3 things saved here and we should do this all in one transaction, which we can do here by wrapping in a scope
- // but it would be nicer to have this taken care of within the Save method itself
-
- //create/save the IMember
- Services.MemberService.Save(contentItem.PersistedContent);
-
- //Now let's do the role provider stuff - now that we've saved the content item (that is important since
- // if we are changing the username, it must be persisted before looking up the member roles).
- if (rolesToRemove.Any())
- {
- Services.MemberService.DissociateRoles(new[] { contentItem.PersistedContent.Username }, rolesToRemove);
- }
- //find the ones to add and add them
- var toAdd = contentItem.Groups.Except(currRoles).ToArray();
- if (toAdd.Any())
- {
- //add the ones submitted
- Services.MemberService.AssignRoles(new[] { contentItem.PersistedContent.Username }, toAdd);
- }
-
- //return the updated model
- var display = Mapper.Map(contentItem.PersistedContent);
-
- //lastly, if it is not valid, add the model state to the outgoing object and throw a 403
- HandleInvalidModelState(display);
-
- var localizedTextService = Services.TextService;
- //put the correct messages in
- switch (contentItem.Action)
- {
- case ContentSaveAction.Save:
- case ContentSaveAction.SaveNew:
- display.AddSuccessNotification(localizedTextService.Localize("speechBubbles/editMemberSaved"), localizedTextService.Localize("speechBubbles/editMemberSaved"));
- break;
- }
-
- return display;
- }
-
- ///
- /// Maps the property values to the persisted entity
- ///
- ///
- private void MapPropertyValues(MemberSave contentItem)
- {
- UpdateName(contentItem);
-
- //map the custom properties - this will already be set for new entities in our member binder
- contentItem.PersistedContent.Email = contentItem.Email;
- contentItem.PersistedContent.Username = contentItem.Username;
-
- //use the base method to map the rest of the properties
- base.MapPropertyValuesForPersistence(
- contentItem,
- contentItem.PropertyCollectionDto,
- (save, property) => property.GetValue(), //get prop val
- (save, property, v) => property.SetValue(v), //set prop val
- null); // member are all invariant
- }
-
- private IMember CreateMemberData(MemberSave contentItem)
- {
- var memberType = Services.MemberTypeService.Get(contentItem.ContentTypeAlias);
- if (memberType == null)
- throw new InvalidOperationException($"No member type found with alias {contentItem.ContentTypeAlias}");
- var member = new Member(contentItem.Name, contentItem.Email, contentItem.Username, memberType, true)
- {
- CreatorId = Security.CurrentUser.Id,
- RawPasswordValue = _passwordSecurity.HashPasswordForStorage(contentItem.Password.NewPassword),
- Comments = contentItem.Comments,
- IsApproved = contentItem.IsApproved
- };
-
- return member;
- }
-
- ///
- /// Update the member security data
- ///
- ///
- ///
- /// If the password has been reset then this method will return the reset/generated password, otherwise will return null.
- ///
- private void UpdateMemberData(MemberSave contentItem)
- {
- contentItem.PersistedContent.WriterId = Security.CurrentUser.Id;
-
- // If the user doesn't have access to sensitive values, then we need to check if any of the built in member property types
- // have been marked as sensitive. If that is the case we cannot change these persisted values no matter what value has been posted.
- // There's only 3 special ones we need to deal with that are part of the MemberSave instance: Comments, IsApproved, IsLockedOut
- // but we will take care of this in a generic way below so that it works for all props.
- if (!Security.CurrentUser.HasAccessToSensitiveData())
- {
- var memberType = Services.MemberTypeService.Get(contentItem.PersistedContent.ContentTypeId);
- var sensitiveProperties = memberType
- .PropertyTypes.Where(x => memberType.IsSensitiveProperty(x.Alias))
- .ToList();
-
- foreach (var sensitiveProperty in sensitiveProperties)
- {
- var destProp = contentItem.Properties.FirstOrDefault(x => x.Alias == sensitiveProperty.Alias);
- if (destProp != null)
- {
- //if found, change the value of the contentItem model to the persisted value so it remains unchanged
- var origValue = contentItem.PersistedContent.GetValue(sensitiveProperty.Alias);
- destProp.Value = origValue;
- }
- }
- }
-
- var isLockedOut = contentItem.IsLockedOut;
-
- //if they were locked but now they are trying to be unlocked
- if (contentItem.PersistedContent.IsLockedOut && isLockedOut == false)
- {
- contentItem.PersistedContent.IsLockedOut = false;
- contentItem.PersistedContent.FailedPasswordAttempts = 0;
- }
- else if (!contentItem.PersistedContent.IsLockedOut && isLockedOut)
- {
- //NOTE: This should not ever happen unless someone is mucking around with the request data.
- //An admin cannot simply lock a user, they get locked out by password attempts, but an admin can un-approve them
- ModelState.AddModelError("custom", "An admin cannot lock a user");
- }
-
- //no password changes then exit ?
- if (contentItem.Password == null)
- return;
-
- // set the password
- contentItem.PersistedContent.RawPasswordValue = _passwordSecurity.HashPasswordForStorage(contentItem.Password.NewPassword);
- }
-
- private static void UpdateName(MemberSave memberSave)
- {
- //Don't update the name if it is empty
- if (memberSave.Name.IsNullOrWhiteSpace() == false)
- {
- memberSave.PersistedContent.Name = memberSave.Name;
- }
- }
-
- // TODO: This logic should be pulled into the service layer
- private async Task ValidateMemberDataAsync(MemberSave contentItem)
- {
- if (contentItem.Name.IsNullOrWhiteSpace())
- {
- ModelState.AddPropertyError(
- new ValidationResult("Invalid user name", new[] { "value" }),
- string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
- return false;
- }
-
- if (contentItem.Password != null && !contentItem.Password.NewPassword.IsNullOrWhiteSpace())
- {
- var validPassword = await _passwordValidator.ValidateAsync(_passwordConfig, contentItem.Password.NewPassword);
- if (!validPassword)
- {
- ModelState.AddPropertyError(
- new ValidationResult("Invalid password: " + string.Join(", ", validPassword.Result), new[] { "value" }),
- string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
- return false;
- }
- }
-
- var byUsername = Services.MemberService.GetByUsername(contentItem.Username);
- if (byUsername != null && byUsername.Key != contentItem.Key)
- {
- ModelState.AddPropertyError(
- new ValidationResult("Username is already in use", new[] { "value" }),
- string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
- return false;
- }
-
- var byEmail = Services.MemberService.GetByEmail(contentItem.Email);
- if (byEmail != null && byEmail.Key != contentItem.Key)
- {
- ModelState.AddPropertyError(
- new ValidationResult("Email address is already in use", new[] { "value" }),
- string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
- return false;
- }
-
- return true;
- }
-
- ///
- /// Permanently deletes a member
- ///
- ///
- ///
- ///
- [HttpPost]
- public HttpResponseMessage DeleteByKey(Guid key)
- {
- var foundMember = Services.MemberService.GetByKey(key);
- if (foundMember == null)
- {
- return HandleContentNotFound(key, false);
- }
- Services.MemberService.Delete(foundMember);
-
- return Request.CreateResponse(HttpStatusCode.OK);
- }
-
- ///
- /// Exports member data based on their unique Id
- ///
- /// The unique member identifier
- ///
- [HttpGet]
- public HttpResponseMessage ExportMemberData(Guid key)
- {
- var currentUser = Security.CurrentUser;
-
- var httpResponseMessage = Request.CreateResponse();
- if (currentUser.HasAccessToSensitiveData() == false)
- {
- httpResponseMessage.StatusCode = HttpStatusCode.Forbidden;
- return httpResponseMessage;
- }
-
- var member = ((MemberService)Services.MemberService).ExportMember(key);
-
- var fileName = $"{member.Name}_{member.Email}.txt";
-
- httpResponseMessage.Content = new ObjectContent(member, new JsonMediaTypeFormatter { Indent = true });
- httpResponseMessage.Content.Headers.Add("x-filename", fileName);
- httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
- httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
- httpResponseMessage.Content.Headers.ContentDisposition.FileName = fileName;
- httpResponseMessage.StatusCode = HttpStatusCode.OK;
-
- return httpResponseMessage;
- }
- }
-
-
-}
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index e582cff00d..a9a1838b3b 100755
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -308,7 +308,6 @@
-
@@ -367,11 +366,9 @@
-
-
@@ -456,8 +453,5 @@
Mvc\web.config
-
-
-
\ No newline at end of file
diff --git a/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForMediaAttribute.cs b/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForMediaAttribute.cs
deleted file mode 100644
index 60e2889fd5..0000000000
--- a/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForMediaAttribute.cs
+++ /dev/null
@@ -1,137 +0,0 @@
-using System;
-using System.Web.Http;
-using System.Web.Http.Controllers;
-using System.Web.Http.Filters;
-using Umbraco.Core;
-using Umbraco.Core.Models;
-using Umbraco.Web.Composing;
-using Umbraco.Web.Editors;
-
-namespace Umbraco.Web.WebApi.Filters
-{
- ///
- /// Auth filter to check if the current user has access to the content item
- ///
- ///
- /// Since media doesn't have permissions, this simply checks start node access
- ///
- internal sealed class EnsureUserPermissionForMediaAttribute : ActionFilterAttribute
- {
- private readonly int? _nodeId;
- private readonly string _paramName;
-
- public enum DictionarySource
- {
- ActionArguments,
- RequestForm,
- RequestQueryString
- }
-
- ///
- /// This constructor will only be able to test the start node access
- ///
- public EnsureUserPermissionForMediaAttribute(int nodeId)
- {
- _nodeId = nodeId;
- }
-
- public EnsureUserPermissionForMediaAttribute(string paramName)
- {
- if (paramName == null) throw new ArgumentNullException(nameof(paramName));
- if (string.IsNullOrEmpty(paramName)) throw new ArgumentException("Value can't be empty.", nameof(paramName));
-
- _paramName = paramName;
- }
-
- // TODO: v8 guess this is not used anymore, source is ignored?!
- public EnsureUserPermissionForMediaAttribute(string paramName, DictionarySource source)
- {
- if (paramName == null) throw new ArgumentNullException(nameof(paramName));
- if (string.IsNullOrEmpty(paramName)) throw new ArgumentException("Value can't be empty.", nameof(paramName));
-
- _paramName = paramName;
- }
-
- public override bool AllowMultiple => true;
-
- private int GetNodeIdFromParameter(object parameterValue)
- {
- if (parameterValue is int)
- {
- return (int) parameterValue;
- }
-
- var guidId = Guid.Empty;
- if (parameterValue is Guid)
- {
- guidId = (Guid)parameterValue;
- }
- else if (parameterValue is GuidUdi)
- {
- guidId = ((GuidUdi) parameterValue).Guid;
- }
-
- if (guidId != Guid.Empty)
- {
- var found = Current.Services.EntityService.GetId(guidId, UmbracoObjectTypes.Media);
- if (found)
- return found.Result;
- }
-
- throw new InvalidOperationException("The id type: " + parameterValue.GetType() + " is not a supported id");
- }
-
- public override void OnActionExecuting(HttpActionContext actionContext)
- {
- if (Current.UmbracoContext.Security.CurrentUser == null)
- {
- throw new HttpResponseException(System.Net.HttpStatusCode.Unauthorized);
- }
-
- int nodeId;
- if (_nodeId.HasValue == false)
- {
- var parts = _paramName.Split(new [] { '.' }, StringSplitOptions.RemoveEmptyEntries);
-
- if (actionContext.ActionArguments[parts[0]] == null)
- {
- throw new InvalidOperationException("No argument found for the current action with the name: " + _paramName);
- }
-
- if (parts.Length == 1)
- {
- nodeId = GetNodeIdFromParameter(actionContext.ActionArguments[parts[0]]);
- }
- else
- {
- //now we need to see if we can get the property of whatever object it is
- var pType = actionContext.ActionArguments[parts[0]].GetType();
- var prop = pType.GetProperty(parts[1]);
- if (prop == null)
- {
- throw new InvalidOperationException("No argument found for the current action with the name: " + _paramName);
- }
- nodeId = GetNodeIdFromParameter(prop.GetValue(actionContext.ActionArguments[parts[0]]));
- }
- }
- else
- {
- nodeId = _nodeId.Value;
- }
-
- if (MediaController.CheckPermissions(
- actionContext.Request.Properties,
- Current.UmbracoContext.Security.CurrentUser,
- Current.Services.MediaService,
- Current.Services.EntityService,
- nodeId))
- {
- base.OnActionExecuting(actionContext);
- }
- else
- {
- throw new HttpResponseException(System.Net.HttpStatusCode.Unauthorized);
- }
- }
- }
-}