Migrated more stuff for MediaController
Signed-off-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
@@ -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
|
||||
/// </remarks>
|
||||
[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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures this controller with a custom action selector
|
||||
/// </summary>
|
||||
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))));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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<MediaItemDisplay>(emptyContent);
|
||||
var emptyContent = _mediaService.CreateMedia("", parentId, contentType.Alias, _webSecurity.GetUserId().ResultOr(Constants.Security.SuperUserId));
|
||||
var mapped = _umbracoMapper.Map<MediaItemDisplay>(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<ContentApp>();
|
||||
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
|
||||
/// <returns></returns>
|
||||
// [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<MediaItemDisplay>(foundContent);
|
||||
return _umbracoMapper.Map<MediaItemDisplay>(foundContent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -165,9 +173,10 @@ namespace Umbraco.Web.Editors
|
||||
/// <returns></returns>
|
||||
// [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<MediaItemDisplay>(foundContent);
|
||||
return _umbracoMapper.Map<MediaItemDisplay>(foundContent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -185,6 +194,7 @@ namespace Umbraco.Web.Editors
|
||||
/// <returns></returns>
|
||||
// [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
|
||||
/// <param name="ids"></param>
|
||||
/// <returns></returns>
|
||||
//[FilterAllowedOutgoingMedia(typeof(IEnumerable<MediaItemDisplay>))] // TODO introduce when moved to .NET Core
|
||||
public IEnumerable<MediaItemDisplay> GetByIds([FromUri]int[] ids)
|
||||
public IEnumerable<MediaItemDisplay> GetByIds([FromQuery]int[] ids)
|
||||
{
|
||||
var foundMedia = Services.MediaService.GetByIds(ids);
|
||||
return foundMedia.Select(media => Mapper.Map<MediaItemDisplay>(media));
|
||||
var foundMedia = _mediaService.GetByIds(ids);
|
||||
return foundMedia.Select(media => _umbracoMapper.Map<MediaItemDisplay>(media));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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<IMedia>().Where(x => folderTypes.Contains(x.ContentTypeId)),
|
||||
_sqlContext.Query<IMedia>().Where(x => folderTypes.Contains(x.ContentTypeId)),
|
||||
Ordering.By("Name", Direction.Ascending));
|
||||
|
||||
return new PagedResult<ContentItemBasic<ContentPropertyBasic>>(total, pageNumber, pageSize)
|
||||
{
|
||||
Items = children.Select(Mapper.Map<IMedia, ContentItemBasic<ContentPropertyBasic>>)
|
||||
Items = children.Select(_umbracoMapper.Map<IMedia, ContentItemBasic<ContentPropertyBasic>>)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -249,8 +259,8 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
// TODO: Add permissions check!
|
||||
|
||||
return Services.MediaService.GetRootMedia()
|
||||
.Select(Mapper.Map<IMedia, ContentItemBasic<ContentPropertyBasic>>);
|
||||
return _mediaService.GetRootMedia()
|
||||
.Select(_umbracoMapper.Map<IMedia, ContentItemBasic<ContentPropertyBasic>>);
|
||||
}
|
||||
|
||||
#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)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the child media objects - using the entity INT id
|
||||
/// </summary>
|
||||
//[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic>>), "Items")] // TODO introduce when moved to .NET Core
|
||||
[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic>>), "Items")] // TODO introduce when moved to .NET Core//[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic>>), "Items")]
|
||||
[DetermineAmbiguousActionByPassingParameters]
|
||||
public PagedResult<ContentItemBasic<ContentPropertyBasic>> GetChildren(int id,
|
||||
int pageNumber = 0,
|
||||
int pageSize = 0,
|
||||
@@ -283,13 +294,13 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
if (pageNumber > 0)
|
||||
return new PagedResult<ContentItemBasic<ContentPropertyBasic>>(0, 0, 0);
|
||||
var nodes = Services.MediaService.GetByIds(UserStartNodes).ToArray();
|
||||
var nodes = _mediaService.GetByIds(UserStartNodes).ToArray();
|
||||
if (nodes.Length == 0)
|
||||
return new PagedResult<ContentItemBasic<ContentPropertyBasic>>(0, 0, 0);
|
||||
if (pageSize < nodes.Length) pageSize = nodes.Length; // bah
|
||||
var pr = new PagedResult<ContentItemBasic<ContentPropertyBasic>>(nodes.Length, pageNumber, pageSize)
|
||||
{
|
||||
Items = nodes.Select(Mapper.Map<IMedia, ContentItemBasic<ContentPropertyBasic>>)
|
||||
Items = nodes.Select(_umbracoMapper.Map<IMedia, ContentItemBasic<ContentPropertyBasic>>)
|
||||
};
|
||||
return pr;
|
||||
}
|
||||
@@ -304,11 +315,11 @@ namespace Umbraco.Web.Editors
|
||||
if (filter.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
//add the default text filter
|
||||
queryFilter = SqlContext.Query<IMedia>()
|
||||
queryFilter = _sqlContext.Query<IMedia>()
|
||||
.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<ContentItemBasic<ContentPropertyBasic>>(totalChildren, pageNumber, pageSize);
|
||||
pagedResult.Items = children
|
||||
.Select(Mapper.Map<IMedia, ContentItemBasic<ContentPropertyBasic>>);
|
||||
.Select(_umbracoMapper.Map<IMedia, ContentItemBasic<ContentPropertyBasic>>);
|
||||
|
||||
return pagedResult;
|
||||
}
|
||||
@@ -345,7 +356,8 @@ namespace Umbraco.Web.Editors
|
||||
/// <param name="orderBySystemField"></param>
|
||||
/// <param name="filter"></param>
|
||||
/// <returns></returns>
|
||||
//[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic>>), "Items")] // TODO introduce when moved to .NET Core
|
||||
[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic>>), "Items")]
|
||||
[DetermineAmbiguousActionByPassingParameters]
|
||||
public PagedResult<ContentItemBasic<ContentPropertyBasic>> 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
|
||||
/// <param name="orderBySystemField"></param>
|
||||
/// <param name="filter"></param>
|
||||
/// <returns></returns>
|
||||
//[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic>>), "Items")] // TODO introduce when moved to .NET Core
|
||||
[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic>>), "Items")]
|
||||
[DetermineAmbiguousActionByPassingParameters]
|
||||
public PagedResult<ContentItemBasic<ContentPropertyBasic>> 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<MediaItemDisplay>(contentItem.PersistedContent);
|
||||
var forDisplay = _umbracoMapper.Map<MediaItemDisplay>(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<MediaItemDisplay>(contentItem.PersistedContent);
|
||||
var display = _umbracoMapper.Map<MediaItemDisplay>(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"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -596,7 +609,7 @@ namespace Umbraco.Web.Editors
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
var mediaService = Services.MediaService;
|
||||
var mediaService = _mediaService;
|
||||
var sortedMedia = new List<IMedia>();
|
||||
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<MediaItemDisplay>(f);
|
||||
return _umbracoMapper.Map<MediaItemDisplay>(f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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<IMedia>().Where(x => x.Name == nameToFind));
|
||||
var children = _mediaService.GetPagedChildren(mediaId, page, pageSize, out total,
|
||||
_sqlContext.Query<IMedia>().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
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
private int GetParentIdAsInt(string parentId, bool validatePermissions)
|
||||
private ActionResult<int> 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<string, object>(),
|
||||
Security.CurrentUser,
|
||||
Services.MediaService,
|
||||
Services.EntityService,
|
||||
new Dictionary<object, object>(),
|
||||
_webSecurity.CurrentUser,
|
||||
_mediaService,
|
||||
_entityService,
|
||||
intParentId) == false)
|
||||
{
|
||||
throw new HttpResponseException(Request.CreateResponse(
|
||||
throw new HttpResponseException(
|
||||
HttpStatusCode.Forbidden,
|
||||
new SimpleNotificationModel(new BackOfficeNotification(
|
||||
Services.TextService.Localize("speechBubbles/operationFailedHeader"),
|
||||
Services.TextService.Localize("speechBubbles/invalidUserPermissionsText"),
|
||||
NotificationStyle.Warning))));
|
||||
_localizedTextService.Localize("speechBubbles/operationFailedHeader"),
|
||||
_localizedTextService.Localize("speechBubbles/invalidUserPermissionsText"),
|
||||
NotificationStyle.Warning)));
|
||||
}
|
||||
|
||||
return intParentId;
|
||||
@@ -879,7 +891,7 @@ namespace Umbraco.Web.Editors
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
var mediaService = Services.MediaService;
|
||||
var mediaService = _mediaService;
|
||||
var toMove = mediaService.GetById(model.Id);
|
||||
if (toMove == null)
|
||||
{
|
||||
@@ -889,12 +901,12 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
//cannot move if the content item is not allowed at the root unless there are
|
||||
//none allowed at root (in which case all should be allowed at root)
|
||||
var mediaTypeService = Services.MediaTypeService;
|
||||
var mediaTypeService = _mediaTypeService;
|
||||
if (toMove.ContentType.AllowedAsRoot == false && mediaTypeService.GetAll().Any(ct => ct.AllowedAsRoot))
|
||||
{
|
||||
var notificationModel = new SimpleNotificationModel();
|
||||
notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedAtRoot"), "");
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(notificationModel));
|
||||
notificationModel.AddErrorNotification(_localizedTextService.Localize("moveOrCopy/notAllowedAtRoot"), "");
|
||||
throw HttpResponseException.CreateValidationErrorResponse(notificationModel);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -906,21 +918,21 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
//check if the item is allowed under this one
|
||||
var parentContentType = Services.MediaTypeService.Get(parent.ContentTypeId);
|
||||
var parentContentType = _mediaTypeService.Get(parent.ContentTypeId);
|
||||
if (parentContentType.AllowedContentTypes.Select(x => x.Id).ToArray()
|
||||
.Any(x => x.Value == toMove.ContentType.Id) == false)
|
||||
{
|
||||
var notificationModel = new SimpleNotificationModel();
|
||||
notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByContentType"), "");
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(notificationModel));
|
||||
notificationModel.AddErrorNotification(_localizedTextService.Localize("moveOrCopy/notAllowedByContentType"), "");
|
||||
throw HttpResponseException.CreateValidationErrorResponse(notificationModel);
|
||||
}
|
||||
|
||||
// Check on paths
|
||||
if ((string.Format(",{0},", parent.Path)).IndexOf(string.Format(",{0},", toMove.Id), StringComparison.Ordinal) > -1)
|
||||
{
|
||||
var notificationModel = new SimpleNotificationModel();
|
||||
notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByPath"), "");
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(notificationModel));
|
||||
notificationModel.AddErrorNotification(_localizedTextService.Localize("moveOrCopy/notAllowedByPath"), "");
|
||||
throw HttpResponseException.CreateValidationErrorResponse(notificationModel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -938,7 +950,7 @@ namespace Umbraco.Web.Editors
|
||||
/// <param name="nodeId">The content to lookup, if the contentItem is not specified</param>
|
||||
/// <param name="media">Specifies the already resolved content item to check against, setting this ignores the nodeId</param>
|
||||
/// <returns></returns>
|
||||
internal static bool CheckPermissions(IDictionary<string, object> storage, IUser user, IMediaService mediaService, IEntityService entityService, int nodeId, IMedia media = null)
|
||||
internal static bool CheckPermissions(IDictionary<object, object> storage, IUser user, IMediaService mediaService, IEntityService entityService, int nodeId, IMedia media = null)
|
||||
{
|
||||
if (storage == null) throw new ArgumentNullException("storage");
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
@@ -977,7 +989,7 @@ namespace Umbraco.Web.Editors
|
||||
var objectType = ObjectTypes.GetUmbracoObjectType(entityType);
|
||||
var udiType = ObjectTypes.GetUdiType(objectType);
|
||||
|
||||
var relations = Services.RelationService.GetPagedParentEntitiesByChildId(id, pageNumber - 1, pageSize, out var totalRecords, objectType);
|
||||
var relations = _relationService.GetPagedParentEntitiesByChildId(id, pageNumber - 1, pageSize, out var totalRecords, objectType);
|
||||
|
||||
return new PagedResult<EntityBasic>(totalRecords, pageNumber, pageSize)
|
||||
{
|
||||
476
src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
Normal file
476
src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
Normal file
@@ -0,0 +1,476 @@
|
||||
// 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 Microsoft.AspNetCore.Identity;
|
||||
// using Microsoft.AspNetCore.Mvc;
|
||||
// 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.Extensions;
|
||||
// using Umbraco.Web.BackOffice.Filters;
|
||||
// using Umbraco.Web.Common.Attributes;
|
||||
// using Umbraco.Web.Common.Exceptions;
|
||||
// using Umbraco.Web.Routing;
|
||||
//
|
||||
// namespace Umbraco.Web.Editors
|
||||
// {
|
||||
// /// <remarks>
|
||||
// /// 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.
|
||||
// /// </remarks>
|
||||
// [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<MemberBasic> 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<MemberBasic>(0, 0, 0);
|
||||
// }
|
||||
//
|
||||
// var pagedResult = new PagedResult<MemberBasic>(totalRecords, pageNumber, pageSize)
|
||||
// {
|
||||
// Items = members
|
||||
// .Select(x => Mapper.Map<MemberBasic>(x))
|
||||
// };
|
||||
// return pagedResult;
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Returns a display node with a list view to render members
|
||||
// /// </summary>
|
||||
// /// <param name="listName"></param>
|
||||
// /// <returns></returns>
|
||||
// public MemberListDisplay GetListNodeDisplay(string listName)
|
||||
// {
|
||||
// var foundType = Services.MemberTypeService.Get(listName);
|
||||
// var name = foundType != null ? foundType.Name : listName;
|
||||
//
|
||||
// var apps = new List<ContentApp>();
|
||||
// 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;
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Gets the content json for the member
|
||||
// /// </summary>
|
||||
// /// <param name="key"></param>
|
||||
// /// <returns></returns>
|
||||
// // [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<MemberDisplay>(foundMember);
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Gets an empty content item for the
|
||||
// /// </summary>
|
||||
// /// <param name="contentTypeAlias"></param>
|
||||
// /// <returns></returns>
|
||||
// // [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<MemberDisplay>(emptyContent);
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Saves member
|
||||
// /// </summary>
|
||||
// /// <returns></returns>
|
||||
// [FileUploadCleanupFilter]
|
||||
// // [OutgoingEditorModelEvent] // TODO introduce when moved to .NET Core
|
||||
// [MemberSaveValidation]
|
||||
// public async Task<MemberDisplay> 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<MemberDisplay>(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<MemberDisplay>(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;
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Maps the property values to the persisted entity
|
||||
// /// </summary>
|
||||
// /// <param name="contentItem"></param>
|
||||
// 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<IMember, MemberSave>(
|
||||
// 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;
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Update the member security data
|
||||
// /// </summary>
|
||||
// /// <param name="contentItem"></param>
|
||||
// /// <returns>
|
||||
// /// If the password has been reset then this method will return the reset/generated password, otherwise will return null.
|
||||
// /// </returns>
|
||||
// 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<bool> 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;
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Permanently deletes a member
|
||||
// /// </summary>
|
||||
// /// <param name="key"></param>
|
||||
// /// <returns></returns>
|
||||
// ///
|
||||
// [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);
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Exports member data based on their unique Id
|
||||
// /// </summary>
|
||||
// /// <param name="key">The unique <see cref="Guid">member identifier</see></param>
|
||||
// /// <returns><see cref="HttpResponseMessage"/></returns>
|
||||
// [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<MemberExportModel>(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;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
@@ -0,0 +1,180 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Common.Exceptions;
|
||||
using Umbraco.Web.Editors;
|
||||
using Umbraco.Web.Security;
|
||||
|
||||
namespace Umbraco.Web.WebApi.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Auth filter to check if the current user has access to the content item
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Since media doesn't have permissions, this simply checks start node access
|
||||
/// </remarks>
|
||||
internal sealed class EnsureUserPermissionForMediaAttribute : TypeFilterAttribute
|
||||
{
|
||||
public EnsureUserPermissionForMediaAttribute(int nodeId)
|
||||
: base(typeof(EnsureUserPermissionForMediaFilter))
|
||||
{
|
||||
Arguments = new object[]
|
||||
{
|
||||
nodeId
|
||||
};
|
||||
}
|
||||
|
||||
public EnsureUserPermissionForMediaAttribute(string paramName)
|
||||
: base(typeof(EnsureUserPermissionForMediaFilter))
|
||||
{
|
||||
Arguments = new object[]
|
||||
{
|
||||
paramName
|
||||
};
|
||||
}
|
||||
private sealed class EnsureUserPermissionForMediaFilter : IActionFilter
|
||||
{
|
||||
private readonly int? _nodeId;
|
||||
private readonly string _paramName;
|
||||
private readonly IWebSecurity _webSecurity;
|
||||
private readonly IEntityService _entityService;
|
||||
private readonly IMediaService _mediaService;
|
||||
|
||||
/// <summary>
|
||||
/// This constructor will only be able to test the start node access
|
||||
/// </summary>
|
||||
public EnsureUserPermissionForMediaFilter(
|
||||
IWebSecurity webSecurity,
|
||||
IEntityService entityService,
|
||||
IMediaService mediaService,
|
||||
int nodeId)
|
||||
:this(webSecurity, entityService, mediaService, nodeId, null)
|
||||
{
|
||||
_nodeId = nodeId;
|
||||
}
|
||||
|
||||
public EnsureUserPermissionForMediaFilter(
|
||||
IWebSecurity webSecurity,
|
||||
IEntityService entityService,
|
||||
IMediaService mediaService,
|
||||
string paramName)
|
||||
:this(webSecurity, entityService, mediaService,null, paramName)
|
||||
{
|
||||
if (paramName == null) throw new ArgumentNullException(nameof(paramName));
|
||||
if (string.IsNullOrEmpty(paramName))
|
||||
throw new ArgumentException("Value can't be empty.", nameof(paramName));
|
||||
}
|
||||
|
||||
private EnsureUserPermissionForMediaFilter(
|
||||
IWebSecurity webSecurity,
|
||||
IEntityService entityService,
|
||||
IMediaService mediaService,
|
||||
int? nodeId, string paramName)
|
||||
{
|
||||
_webSecurity = webSecurity ?? throw new ArgumentNullException(nameof(webSecurity));
|
||||
_entityService = entityService ?? throw new ArgumentNullException(nameof(entityService));
|
||||
_mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService));
|
||||
|
||||
_paramName = paramName;
|
||||
|
||||
if (nodeId.HasValue)
|
||||
{
|
||||
_nodeId = nodeId.Value;
|
||||
}
|
||||
}
|
||||
|
||||
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 = _entityService.GetId(guidId, UmbracoObjectTypes.Media);
|
||||
if (found)
|
||||
return found.Result;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("The id type: " + parameterValue.GetType() +
|
||||
" is not a supported id");
|
||||
}
|
||||
|
||||
public void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
if (_webSecurity.CurrentUser == null)
|
||||
{
|
||||
throw new HttpResponseException(System.Net.HttpStatusCode.Unauthorized);
|
||||
}
|
||||
|
||||
int nodeId;
|
||||
if (_nodeId.HasValue == false)
|
||||
{
|
||||
var parts = _paramName.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (context.ActionArguments[parts[0]] == null)
|
||||
{
|
||||
throw new InvalidOperationException("No argument found for the current action with the name: " +
|
||||
_paramName);
|
||||
}
|
||||
|
||||
if (parts.Length == 1)
|
||||
{
|
||||
nodeId = GetNodeIdFromParameter(context.ActionArguments[parts[0]]);
|
||||
}
|
||||
else
|
||||
{
|
||||
//now we need to see if we can get the property of whatever object it is
|
||||
var pType = context.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(context.ActionArguments[parts[0]]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeId = _nodeId.Value;
|
||||
}
|
||||
|
||||
if (MediaController.CheckPermissions(
|
||||
context.HttpContext.Items,
|
||||
_webSecurity.CurrentUser,
|
||||
_mediaService,
|
||||
_entityService,
|
||||
nodeId))
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new HttpResponseException(System.Net.HttpStatusCode.Unauthorized);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnActionExecuted(ActionExecutedContext context)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,95 +157,14 @@ namespace Umbraco.Web.Editors.Binders
|
||||
{
|
||||
throw new ArgumentNullException(nameof(bindingContext));
|
||||
}
|
||||
var modelName = bindingContext.ModelName;
|
||||
|
||||
var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
|
||||
var model = await _modelBinderHelper.BindModelFromMultipartRequestAsync<ContentItemSave>(_jsonSerializer, _hostingEnvironment, bindingContext);
|
||||
|
||||
if (valueProviderResult == ValueProviderResult.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);
|
||||
|
||||
var value = valueProviderResult.FirstValue;
|
||||
|
||||
// Check if the argument value is null or empty
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var model = _jsonSerializer.Deserialize<ContentItemSave>(value);
|
||||
if (model is null)
|
||||
{
|
||||
// Non-integer arguments result in model state errors
|
||||
bindingContext.ModelState.TryAddModelError(
|
||||
modelName, $"Cannot deserialize {modelName} as {nameof(ContentItemSave)}.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//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
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
model.PersistedContent = ContentControllerBase.IsCreatingAction(model.Action) ? CreateNew(model) : GetExisting(model);
|
||||
|
||||
//create the dto from the persisted model
|
||||
@@ -270,5 +189,7 @@ namespace Umbraco.Web.Editors.Binders
|
||||
|
||||
bindingContext.Result = ModelBindingResult.Success(model);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
using Umbraco.Core.Serialization;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.Common.Exceptions;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
@@ -17,73 +20,103 @@ namespace Umbraco.Web.Editors.Binders
|
||||
/// </summary>
|
||||
internal class ContentModelBinderHelper
|
||||
{
|
||||
// public TModelSave BindModelFromMultipartRequest<TModelSave>(ActionContext actionContext,
|
||||
// ModelBindingContext bindingContext)
|
||||
// where TModelSave : IHaveUploadedFiles
|
||||
// {
|
||||
// var result = actionContext.ReadAsMultipart(Constants.SystemDirectories.TempFileUploads);
|
||||
//
|
||||
// var model = actionContext.GetModelFromMultipartRequest<TModelSave>(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<T> BindModelFromMultipartRequestAsync<T>(
|
||||
IJsonSerializer jsonSerializer,
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
ModelBindingContext bindingContext)
|
||||
where T: class, IHaveUploadedFiles
|
||||
{
|
||||
var modelName = bindingContext.ModelName;
|
||||
|
||||
/// <summary>
|
||||
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<T>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// we will now assign all of the values in the 'save' model to the DTO object
|
||||
/// </summary>
|
||||
/// <param name="saveModel"></param>
|
||||
|
||||
@@ -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
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// The model binder for <see cref="T:Umbraco.Web.Models.ContentEditing.MediaItemSave" />
|
||||
// /// </summary>
|
||||
// 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();
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Creates the model from the request and binds it to the context
|
||||
// /// </summary>
|
||||
// /// <param name="bindingContext"></param>
|
||||
// /// <returns></returns>
|
||||
// public Task BindModelAsync(ModelBindingContext bindingContext)
|
||||
// {
|
||||
// var actionContext = bindingContext.ActionContext;
|
||||
// var model = _modelBinderHelper.BindModelFromMultipartRequest<MediaItemSave>(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<IMedia, ContentPropertyCollectionDto>(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
|
||||
{
|
||||
/// <summary>
|
||||
/// The model binder for <see cref="T:Umbraco.Web.Models.ContentEditing.MediaItemSave" />
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the model from the request and binds it to the context
|
||||
/// </summary>
|
||||
/// <param name="bindingContext"></param>
|
||||
/// <returns></returns>
|
||||
public async Task BindModelAsync(ModelBindingContext bindingContext)
|
||||
{
|
||||
|
||||
var model = await _modelBinderHelper.BindModelFromMultipartRequestAsync<MediaItemSave>(_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<IMedia, ContentPropertyCollectionDto>(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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// The model binder for <see cref="T:Umbraco.Web.Models.ContentEditing.MemberSave" />
|
||||
// /// </summary>
|
||||
// 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();
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Creates the model from the request and binds it to the context
|
||||
// /// </summary>
|
||||
// /// <param name="actionContext"></param>
|
||||
// /// <param name="bindingContext"></param>
|
||||
// /// <returns></returns>
|
||||
// public Task BindModelAsync(ModelBindingContext bindingContext)
|
||||
// {
|
||||
// var actionContext = bindingContext.ActionContext;
|
||||
// var model = _modelBinderHelper.BindModelFromMultipartRequest<MemberSave>(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<IMember, ContentPropertyCollectionDto>(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;
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Returns an IMember instance used to bind values to and save (depending on the membership scenario)
|
||||
// /// </summary>
|
||||
// /// <param name="model"></param>
|
||||
// /// <returns></returns>
|
||||
// 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;
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Gets an instance of IMember used when creating a member
|
||||
// /// </summary>
|
||||
// /// <param name="model"></param>
|
||||
// /// <returns></returns>
|
||||
// /// <remarks>
|
||||
// /// Depending on whether a custom membership provider is configured this will return different results.
|
||||
// /// </remarks>
|
||||
// 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);
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 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.
|
||||
// /// </summary>
|
||||
// /// <param name="contentType"></param>
|
||||
// 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<string> 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
|
||||
{
|
||||
/// <summary>
|
||||
/// The model binder for <see cref="T:Umbraco.Web.Models.ContentEditing.MemberSave" />
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the model from the request and binds it to the context
|
||||
/// </summary>
|
||||
/// <param name="actionContext"></param>
|
||||
/// <param name="bindingContext"></param>
|
||||
/// <returns></returns>
|
||||
public async Task BindModelAsync(ModelBindingContext bindingContext)
|
||||
{
|
||||
var model = await _modelBinderHelper.BindModelFromMultipartRequestAsync<MemberSave>(_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<IMember, ContentPropertyCollectionDto>(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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an IMember instance used to bind values to and save (depending on the membership scenario)
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance of IMember used when creating a member
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// Depending on whether a custom membership provider is configured this will return different results.
|
||||
/// </remarks>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="contentType"></param>
|
||||
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<string> exclude)
|
||||
{
|
||||
//remove all properties based on the exclusion list
|
||||
foreach (var remove in exclude)
|
||||
{
|
||||
if (contentType.PropertyTypeExists(remove))
|
||||
{
|
||||
contentType.RemovePropertyType(remove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
[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<MemberBasic> 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<MemberBasic>(0, 0, 0);
|
||||
}
|
||||
|
||||
var pagedResult = new PagedResult<MemberBasic>(totalRecords, pageNumber, pageSize)
|
||||
{
|
||||
Items = members
|
||||
.Select(x => Mapper.Map<MemberBasic>(x))
|
||||
};
|
||||
return pagedResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a display node with a list view to render members
|
||||
/// </summary>
|
||||
/// <param name="listName"></param>
|
||||
/// <returns></returns>
|
||||
public MemberListDisplay GetListNodeDisplay(string listName)
|
||||
{
|
||||
var foundType = Services.MemberTypeService.Get(listName);
|
||||
var name = foundType != null ? foundType.Name : listName;
|
||||
|
||||
var apps = new List<ContentApp>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content json for the member
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
// [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<MemberDisplay>(foundMember);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an empty content item for the
|
||||
/// </summary>
|
||||
/// <param name="contentTypeAlias"></param>
|
||||
/// <returns></returns>
|
||||
// [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<MemberDisplay>(emptyContent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves member
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[FileUploadCleanupFilter]
|
||||
// [OutgoingEditorModelEvent] // TODO introduce when moved to .NET Core
|
||||
[MemberSaveValidation]
|
||||
public async Task<MemberDisplay> 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<MemberDisplay>(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<MemberDisplay>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps the property values to the persisted entity
|
||||
/// </summary>
|
||||
/// <param name="contentItem"></param>
|
||||
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<IMember, MemberSave>(
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the member security data
|
||||
/// </summary>
|
||||
/// <param name="contentItem"></param>
|
||||
/// <returns>
|
||||
/// If the password has been reset then this method will return the reset/generated password, otherwise will return null.
|
||||
/// </returns>
|
||||
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<bool> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Permanently deletes a member
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
///
|
||||
[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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports member data based on their unique Id
|
||||
/// </summary>
|
||||
/// <param name="key">The unique <see cref="Guid">member identifier</see></param>
|
||||
/// <returns><see cref="HttpResponseMessage"/></returns>
|
||||
[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<MemberExportModel>(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -308,7 +308,6 @@
|
||||
<Compile Include="Mvc\UmbracoViewPage.cs" />
|
||||
<Compile Include="Editors\MemberTypeController.cs" />
|
||||
<Compile Include="Editors\EntityController.cs" />
|
||||
<Compile Include="Editors\MemberController.cs" />
|
||||
<Compile Include="Editors\CurrentUserController.cs" />
|
||||
<Compile Include="ImageCropperTemplateExtensions.cs" />
|
||||
<Compile Include="Mvc\UmbracoVirtualNodeRouteHandler.cs" />
|
||||
@@ -367,11 +366,9 @@
|
||||
<Compile Include="CdfLogger.cs" />
|
||||
<Compile Include="Controllers\UmbLoginController.cs" />
|
||||
<Compile Include="UrlHelperExtensions.cs" />
|
||||
<Compile Include="Editors\MediaController.cs" />
|
||||
<Compile Include="UrlHelperRenderExtensions.cs" />
|
||||
<Compile Include="WebApi\IsBackOfficeAttribute.cs" />
|
||||
<Compile Include="Editors\Filters\ContentModelValidator.cs" />
|
||||
<Compile Include="WebApi\Filters\EnsureUserPermissionForMediaAttribute.cs" />
|
||||
<Compile Include="WebApi\Filters\FileUploadCleanupFilterAttribute.cs" />
|
||||
<Compile Include="WebApi\Filters\OutgoingDateTimeFormatAttribute.cs" />
|
||||
<Compile Include="WebApi\Filters\UmbracoApplicationAuthorizeAttribute.cs" />
|
||||
@@ -456,8 +453,5 @@
|
||||
<Link>Mvc\web.config</Link>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Editors\Binders" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Auth filter to check if the current user has access to the content item
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Since media doesn't have permissions, this simply checks start node access
|
||||
/// </remarks>
|
||||
internal sealed class EnsureUserPermissionForMediaAttribute : ActionFilterAttribute
|
||||
{
|
||||
private readonly int? _nodeId;
|
||||
private readonly string _paramName;
|
||||
|
||||
public enum DictionarySource
|
||||
{
|
||||
ActionArguments,
|
||||
RequestForm,
|
||||
RequestQueryString
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This constructor will only be able to test the start node access
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user