diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs index 3d8b23995a..c3f0a87dde 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs @@ -1,189 +1,210 @@ -// using System; -// using System.Collections.Generic; -// using System.Linq; -// using System.Net; -// using System.Net.Http; -// using System.Web.Http; -// using Umbraco.Core; -// using Umbraco.Core.Cache; -// using Umbraco.Web.Composing; -// using Umbraco.Core.Configuration; -// using Umbraco.Core.Dictionary; -// using Umbraco.Core.Logging; -// using Umbraco.Core.Models; -// using Umbraco.Core.Persistence; -// using Umbraco.Core.Services; -// using Umbraco.Core.Strings; -// using Umbraco.Web.Models.ContentEditing; -// using Umbraco.Web.Mvc; -// using Umbraco.Web.WebApi.Filters; -// using Constants = Umbraco.Core.Constants; -// using Umbraco.Core.Mapping; -// using Umbraco.Web.Routing; -// -// namespace Umbraco.Web.Editors -// { -// /// -// /// An API controller used for dealing with member types -// /// -// [PluginController("UmbracoApi")] -// [UmbracoTreeAuthorize(new string[] { Constants.Trees.MemberTypes, Constants.Trees.Members})] -// public class MemberTypeController : ContentTypeControllerBase -// { -// public MemberTypeController( -// ICultureDictionary cultureDictionary, -// IGlobalSettings globalSettings, -// IUmbracoContextAccessor umbracoContextAccessor, -// ISqlContext sqlContext, -// ServiceContext services, -// AppCaches appCaches, -// IProfilingLogger logger, -// IRuntimeState runtimeState, -// IShortStringHelper shortStringHelper, -// UmbracoMapper umbracoMapper, -// IPublishedUrlProvider publishedUrlProvider, -// EditorValidatorCollection editorValidatorCollection) -// : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider, editorValidatorCollection) -// { -// } -// -// [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] -// public MemberTypeDisplay GetById(int id) -// { -// var ct = Services.MemberTypeService.Get(id); -// if (ct == null) -// { -// throw new HttpResponseException(HttpStatusCode.NotFound); -// } -// -// var dto = Mapper.Map(ct); -// return dto; -// } -// -// /// -// /// Deletes a document type with a given ID -// /// -// /// -// /// -// [HttpDelete] -// [HttpPost] -// [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] -// public HttpResponseMessage DeleteById(int id) -// { -// var foundType = Services.MemberTypeService.Get(id); -// if (foundType == null) -// { -// throw new HttpResponseException(HttpStatusCode.NotFound); -// } -// -// Services.MemberTypeService.Delete(foundType, Security.CurrentUser.Id); -// return Request.CreateResponse(HttpStatusCode.OK); -// } -// -// /// -// /// Returns the available compositions for this content type -// /// -// /// -// /// -// /// This is normally an empty list but if additional content type aliases are passed in, any content types containing those aliases will be filtered out -// /// along with any content types that have matching property types that are included in the filtered content types -// /// -// /// -// /// This is normally an empty list but if additional property type aliases are passed in, any content types that have these aliases will be filtered out. -// /// This is required because in the case of creating/modifying a content type because new property types being added to it are not yet persisted so cannot -// /// be looked up via the db, they need to be passed in. -// /// -// /// -// -// [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] -// public HttpResponseMessage GetAvailableCompositeMemberTypes(int contentTypeId, -// [FromUri]string[] filterContentTypes, -// [FromUri]string[] filterPropertyTypes) -// { -// var result = PerformGetAvailableCompositeContentTypes(contentTypeId, UmbracoObjectTypes.MemberType, filterContentTypes, filterPropertyTypes, false) -// .Select(x => new -// { -// contentType = x.Item1, -// allowed = x.Item2 -// }); -// return Request.CreateResponse(result); -// } -// -// [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] -// public MemberTypeDisplay GetEmpty() -// { -// var ct = new MemberType(ShortStringHelper, -1); -// ct.Icon = Constants.Icons.Member; -// -// var dto = Mapper.Map(ct); -// return dto; -// } -// -// -// /// -// /// Returns all member types -// /// -// public IEnumerable GetAllTypes() -// { -// return Services.MemberTypeService.GetAll() -// .Select(Mapper.Map); -// } -// -// [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] -// public MemberTypeDisplay PostSave(MemberTypeSave contentTypeSave) -// { -// //get the persisted member type -// var ctId = Convert.ToInt32(contentTypeSave.Id); -// var ct = ctId > 0 ? Services.MemberTypeService.Get(ctId) : null; -// -// if (Security.CurrentUser.HasAccessToSensitiveData() == false) -// { -// //We need to validate if any properties on the contentTypeSave have had their IsSensitiveValue changed, -// //and if so, we need to check if the current user has access to sensitive values. If not, we have to return an error -// var props = contentTypeSave.Groups.SelectMany(x => x.Properties); -// if (ct != null) -// { -// foreach (var prop in props) -// { -// // Id 0 means the property was just added, no need to look it up -// if (prop.Id == 0) -// continue; -// -// var foundOnContentType = ct.PropertyTypes.FirstOrDefault(x => x.Id == prop.Id); -// if (foundOnContentType == null) -// throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, "No property type with id " + prop.Id + " found on the content type")); -// if (ct.IsSensitiveProperty(foundOnContentType.Alias) && prop.IsSensitiveData == false) -// { -// //if these don't match, then we cannot continue, this user is not allowed to change this value -// throw new HttpResponseException(HttpStatusCode.Forbidden); -// } -// } -// } -// else -// { -// //if it is new, then we can just verify if any property has sensitive data turned on which is not allowed -// if (props.Any(prop => prop.IsSensitiveData)) -// { -// throw new HttpResponseException(HttpStatusCode.Forbidden); -// } -// } -// } -// -// -// var savedCt = PerformPostSave( -// contentTypeSave: contentTypeSave, -// getContentType: i => ct, -// saveContentType: type => Services.MemberTypeService.Save(type)); -// -// var display = Mapper.Map(savedCt); -// -// display.AddSuccessNotification( -// Services.TextService.Localize("speechBubbles/memberTypeSavedHeader"), -// string.Empty); -// -// return display; -// } -// -// -// } -// } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +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.Persistence; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; +using Umbraco.Web.Models.ContentEditing; +using Constants = Umbraco.Core.Constants; +using Umbraco.Core.Mapping; +using Umbraco.Web.BackOffice.Controllers; +using Umbraco.Web.BackOffice.Filters; +using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Exceptions; +using Umbraco.Web.Routing; +using Umbraco.Web.Security; + +namespace Umbraco.Web.Editors +{ + /// + /// An API controller used for dealing with member types + /// + [PluginController("UmbracoApi")] + [UmbracoTreeAuthorize(new string[] { Constants.Trees.MemberTypes, Constants.Trees.Members})] + public class MemberTypeController : ContentTypeControllerBase + { + private readonly IMemberTypeService _memberTypeService; + private readonly IWebSecurity _webSecurity; + private readonly IShortStringHelper _shortStringHelper; + private readonly UmbracoMapper _umbracoMapper; + private readonly ILocalizedTextService _localizedTextService; + + public MemberTypeController( + ICultureDictionary cultureDictionary, + EditorValidatorCollection editorValidatorCollection, + IContentTypeService contentTypeService, + IMediaTypeService mediaTypeService, + IMemberTypeService memberTypeService, + UmbracoMapper umbracoMapper, + ILocalizedTextService localizedTextService, + IWebSecurity webSecurity, + IShortStringHelper shortStringHelper) + : base(cultureDictionary, + editorValidatorCollection, + contentTypeService, + mediaTypeService, + memberTypeService, + umbracoMapper, + localizedTextService) + { + _memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService)); + _webSecurity = webSecurity ?? throw new ArgumentNullException(nameof(webSecurity)); + _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); + _umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper)); + _localizedTextService = + localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService)); + } + + [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] + public MemberTypeDisplay GetById(int id) + { + var ct = _memberTypeService.Get(id); + if (ct == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + var dto =_umbracoMapper.Map(ct); + return dto; + } + + /// + /// Deletes a document type with a given ID + /// + /// + /// + [HttpDelete] + [HttpPost] + [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] + public IActionResult DeleteById(int id) + { + var foundType = _memberTypeService.Get(id); + if (foundType == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + _memberTypeService.Delete(foundType, _webSecurity.CurrentUser.Id); + return Ok(); + } + + /// + /// Returns the available compositions for this content type + /// + /// + /// + /// This is normally an empty list but if additional content type aliases are passed in, any content types containing those aliases will be filtered out + /// along with any content types that have matching property types that are included in the filtered content types + /// + /// + /// This is normally an empty list but if additional property type aliases are passed in, any content types that have these aliases will be filtered out. + /// This is required because in the case of creating/modifying a content type because new property types being added to it are not yet persisted so cannot + /// be looked up via the db, they need to be passed in. + /// + /// + + [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] + public IActionResult GetAvailableCompositeMemberTypes(int contentTypeId, + [FromQuery]string[] filterContentTypes, + [FromQuery]string[] filterPropertyTypes) + { + var result = PerformGetAvailableCompositeContentTypes(contentTypeId, UmbracoObjectTypes.MemberType, filterContentTypes, filterPropertyTypes, false) + .Select(x => new + { + contentType = x.Item1, + allowed = x.Item2 + }); + return Ok(result); + } + + [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] + public MemberTypeDisplay GetEmpty() + { + var ct = new MemberType(_shortStringHelper, -1); + ct.Icon = Constants.Icons.Member; + + var dto =_umbracoMapper.Map(ct); + return dto; + } + + + /// + /// Returns all member types + /// + public IEnumerable GetAllTypes() + { + return _memberTypeService.GetAll() + .Select(_umbracoMapper.Map); + } + + [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] + public ActionResult PostSave(MemberTypeSave contentTypeSave) + { + //get the persisted member type + var ctId = Convert.ToInt32(contentTypeSave.Id); + var ct = ctId > 0 ? _memberTypeService.Get(ctId) : null; + + if (_webSecurity.CurrentUser.HasAccessToSensitiveData() == false) + { + //We need to validate if any properties on the contentTypeSave have had their IsSensitiveValue changed, + //and if so, we need to check if the current user has access to sensitive values. If not, we have to return an error + var props = contentTypeSave.Groups.SelectMany(x => x.Properties); + if (ct != null) + { + foreach (var prop in props) + { + // Id 0 means the property was just added, no need to look it up + if (prop.Id == 0) + continue; + + var foundOnContentType = ct.PropertyTypes.FirstOrDefault(x => x.Id == prop.Id); + if (foundOnContentType == null) + { + return NotFound(new + { Message = "No property type with id " + prop.Id + " found on the content type" }); + } + + if (ct.IsSensitiveProperty(foundOnContentType.Alias) && prop.IsSensitiveData == false) + { + //if these don't match, then we cannot continue, this user is not allowed to change this value + throw new HttpResponseException(HttpStatusCode.Forbidden); + } + } + } + else + { + //if it is new, then we can just verify if any property has sensitive data turned on which is not allowed + if (props.Any(prop => prop.IsSensitiveData)) + { + throw new HttpResponseException(HttpStatusCode.Forbidden); + } + } + } + + + var savedCt = PerformPostSave( + contentTypeSave: contentTypeSave, + getContentType: i => ct, + saveContentType: type => _memberTypeService.Save(type)); + + var display =_umbracoMapper.Map(savedCt); + + display.AddSuccessNotification( + _localizedTextService.Localize("speechBubbles/memberTypeSavedHeader"), + string.Empty); + + return display; + } + + + } +}