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;
+ }
+
+
+ }
+}