diff --git a/src/Umbraco.Configuration/Models/ContentSettings.cs b/src/Umbraco.Configuration/Models/ContentSettings.cs index 5bc31814b7..b852410b0a 100644 --- a/src/Umbraco.Configuration/Models/ContentSettings.cs +++ b/src/Umbraco.Configuration/Models/ContentSettings.cs @@ -20,7 +20,11 @@ namespace Umbraco.Configuration.Models { new ImagingAutoFillUploadField { - Alias = "umbracoFile" + Alias = "umbracoFile", + WidthFieldAlias = "umbracoWidth", + HeightFieldAlias ="umbracoHeight", + ExtensionFieldAlias = "umbracoExtension", + LengthFieldAlias = "umbracoBytes" } }; diff --git a/src/Umbraco.Core/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs index cd978cc392..1df4e4a0fb 100644 --- a/src/Umbraco.Core/IO/MediaFileSystem.cs +++ b/src/Umbraco.Core/IO/MediaFileSystem.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; -using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Strings; diff --git a/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResult.cs b/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResult.cs index 52214a9b38..6bf1f28907 100644 --- a/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResult.cs +++ b/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResult.cs @@ -3,7 +3,7 @@ /// /// Used when determining available compositions for a given content type /// - internal class ContentTypeAvailableCompositionsResult + public class ContentTypeAvailableCompositionsResult { public ContentTypeAvailableCompositionsResult(IContentTypeComposition composition, bool allowed) { diff --git a/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResults.cs b/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResults.cs index b4e038c14c..3d863d83c1 100644 --- a/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResults.cs +++ b/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResults.cs @@ -6,7 +6,7 @@ namespace Umbraco.Core.Models /// /// Used when determining available compositions for a given content type /// - internal class ContentTypeAvailableCompositionsResults + public class ContentTypeAvailableCompositionsResults { public ContentTypeAvailableCompositionsResults() { diff --git a/src/Umbraco.Core/ServiceContextExtensions.cs b/src/Umbraco.Core/ServiceContextExtensions.cs deleted file mode 100644 index 3af918815f..0000000000 --- a/src/Umbraco.Core/ServiceContextExtensions.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using Umbraco.Core.Models; -using Umbraco.Core.Services; - -namespace Umbraco.Core -{ - public static class ServiceContextExtensions - { - public static IContentTypeBaseService GetContentTypeService(this ServiceContext services) - where T : IContentTypeComposition - { - if (typeof(T).Implements()) - return services.ContentTypeService as IContentTypeBaseService; - if (typeof(T).Implements()) - return services.MediaTypeService as IContentTypeBaseService; - if (typeof(T).Implements()) - return services.MemberTypeService as IContentTypeBaseService; - throw new ArgumentException("Type " + typeof(T).FullName + " does not have a service."); - } - } -} diff --git a/src/Umbraco.Core/Services/ContentTypeServiceExtensions.cs b/src/Umbraco.Core/Services/ContentTypeServiceExtensions.cs index 37f1e5127f..8d21c21276 100644 --- a/src/Umbraco.Core/Services/ContentTypeServiceExtensions.cs +++ b/src/Umbraco.Core/Services/ContentTypeServiceExtensions.cs @@ -39,7 +39,7 @@ namespace Umbraco.Core.Services /// /// Wether the composite content types should be applicable for an element type /// - internal static ContentTypeAvailableCompositionsResults GetAvailableCompositeContentTypes(this IContentTypeService ctService, + public static ContentTypeAvailableCompositionsResults GetAvailableCompositeContentTypes(this IContentTypeService ctService, IContentTypeComposition source, IContentTypeComposition[] allContentTypes, string[] filterContentTypes = null, @@ -63,7 +63,7 @@ namespace Umbraco.Core.Services .Select(c => c.Alias) .Union(filterPropertyTypes) .ToArray(); - + var sourceId = source?.Id ?? 0; // find out if any content type uses this content type @@ -127,7 +127,7 @@ namespace Umbraco.Core.Services return new ContentTypeAvailableCompositionsResults(ancestors, result); } - + private static IContentTypeComposition[] GetAncestors(IContentTypeComposition ctype, IContentTypeComposition[] allContentTypes) { diff --git a/src/Umbraco.Infrastructure/Media/UploadAutoFillProperties.cs b/src/Umbraco.Infrastructure/Media/UploadAutoFillProperties.cs index 665693d91f..e7f71bd2f5 100644 --- a/src/Umbraco.Infrastructure/Media/UploadAutoFillProperties.cs +++ b/src/Umbraco.Infrastructure/Media/UploadAutoFillProperties.cs @@ -112,16 +112,16 @@ namespace Umbraco.Web.Media if (content == null) throw new ArgumentNullException(nameof(content)); if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig)); - if (content.Properties.Contains(autoFillConfig.WidthFieldAlias)) + if (!(autoFillConfig.WidthFieldAlias is null) && content.Properties.Contains(autoFillConfig.WidthFieldAlias)) content.Properties[autoFillConfig.WidthFieldAlias].SetValue(size.HasValue ? size.Value.Width.ToInvariantString() : string.Empty, culture, segment); - if (content.Properties.Contains(autoFillConfig.HeightFieldAlias)) + if (!(autoFillConfig.HeightFieldAlias is null) && content.Properties.Contains(autoFillConfig.HeightFieldAlias)) content.Properties[autoFillConfig.HeightFieldAlias].SetValue(size.HasValue ? size.Value.Height.ToInvariantString() : string.Empty, culture, segment); - if (content.Properties.Contains(autoFillConfig.LengthFieldAlias)) + if (!(autoFillConfig.LengthFieldAlias is null) && content.Properties.Contains(autoFillConfig.LengthFieldAlias)) content.Properties[autoFillConfig.LengthFieldAlias].SetValue(length, culture, segment); - if (content.Properties.Contains(autoFillConfig.ExtensionFieldAlias)) + if (!(autoFillConfig.ExtensionFieldAlias is null) && content.Properties.Contains(autoFillConfig.ExtensionFieldAlias)) content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(extension, culture, segment); } diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs index fb23b722f4..f2190eaf38 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs @@ -176,10 +176,10 @@ namespace Umbraco.Web.BackOffice.Controllers "contentApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( controller => controller.PostSave(null)) }, - // { - // "mediaApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( - // controller => controller.GetRootMedia()) - // }, + { + "mediaApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( + controller => controller.GetRootMedia()) + }, { "imagesApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( controller => controller.GetBigThumbnail("")) @@ -192,14 +192,14 @@ namespace Umbraco.Web.BackOffice.Controllers "treeApplicationApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( controller => controller.GetApplicationTrees(null, null, null, TreeUse.None)) }, - // { - // "contentTypeApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( - // controller => controller.GetAllowedChildren(0)) - // }, - // { - // "mediaTypeApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( - // controller => controller.GetAllowedChildren(0)) - // }, + { + "contentTypeApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( + controller => controller.GetAllowedChildren(0)) + }, + { + "mediaTypeApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( + controller => controller.GetAllowedChildren(0)) + }, { "macroRenderingApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( controller => controller.GetMacroParameters(0)) diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs index 313e197eb6..d51c6fd0bf 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; +using System.Net.Mime; using System.Text; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Mvc; @@ -30,10 +31,10 @@ using Constants = Umbraco.Core.Constants; using Umbraco.Extensions; using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.BackOffice.Filters; +using Umbraco.Web.BackOffice.ModelBinders; using Umbraco.Web.Common.ActionResults; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Exceptions; -using Umbraco.Web.Editors.Binders; using Umbraco.Web.Models.Mapping; using Umbraco.Web.Security; using Umbraco.Web.WebApi.Filters; @@ -395,6 +396,7 @@ namespace Umbraco.Web.Editors /// /// [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [DetermineAmbiguousActionByPassingParameters] public ContentItemDisplay GetEmpty(string contentTypeAlias, int parentId) { var contentType = _contentTypeService.Get(contentTypeAlias); @@ -418,6 +420,7 @@ namespace Umbraco.Web.Editors } [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [DetermineAmbiguousActionByPassingParameters] public ContentItemDisplay GetEmpty(int blueprintId, int parentId) { var blueprint = _contentService.GetBlueprintById(blueprintId); @@ -447,7 +450,7 @@ namespace Umbraco.Web.Editors public IActionResult GetNiceUrl(int id) { var url = _publishedUrlProvider.GetUrl(id); - return Content(url, "text/plain", Encoding.UTF8); + return Content(url, MediaTypeNames.Text.Plain, Encoding.UTF8); } /// @@ -459,7 +462,7 @@ namespace Umbraco.Web.Editors public IActionResult GetNiceUrl(Guid id) { var url = _publishedUrlProvider.GetUrl(id); - return Content(url, "text/plain", Encoding.UTF8); + return Content(url, MediaTypeNames.Text.Plain, Encoding.UTF8); } /// @@ -1635,7 +1638,7 @@ namespace Umbraco.Web.Editors _contentService.Move(toMove, move.ParentId, _webSecurity.GetUserId().ResultOr(0)); - return Content(toMove.Path, "text/plain", Encoding.UTF8); + return Content(toMove.Path, MediaTypeNames.Text.Plain, Encoding.UTF8); } /// @@ -1650,7 +1653,7 @@ namespace Umbraco.Web.Editors var c = _contentService.Copy(toCopy, copy.ParentId, copy.RelateToOriginal, copy.Recursive, _webSecurity.GetUserId().ResultOr(0)); - return Content(c.Path, "text/plain", Encoding.UTF8); + return Content(c.Path, MediaTypeNames.Text.Plain, Encoding.UTF8); } /// diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs index 2d7121e2e4..0791707dea 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs @@ -17,11 +17,10 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Extensions; -using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Models.ContentEditing; -namespace Umbraco.Web.Editors +namespace Umbraco.Web.BackOffice.Controllers { /// /// An abstract base controller used for media/content/members to try to reduce code replication. diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs similarity index 63% rename from src/Umbraco.Web/Editors/ContentTypeController.cs rename to src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs index 506eac6aac..b8a6a4d0bb 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs @@ -6,13 +6,15 @@ using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; -using System.Web.Http; using System.Xml; using System.Xml.Linq; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Dictionary; +using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; @@ -25,14 +27,17 @@ using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Models; 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.BackOffice.Filters; +using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Exceptions; +using Umbraco.Web.Editors; using Umbraco.Web.Routing; +using Umbraco.Web.Security; -namespace Umbraco.Web.Editors +namespace Umbraco.Web.BackOffice.Controllers { // TODO: We'll need to be careful about the security on this controller, when we start implementing // methods to modify content types we'll need to enforce security on the individual methods, we @@ -44,7 +49,6 @@ namespace Umbraco.Web.Editors /// [PluginController("UmbracoApi")] [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] - [EnableOverrideAuthorization] public class ContentTypeController : ContentTypeControllerBase { private readonly IEntityXmlSerializer _serializer; @@ -52,53 +56,96 @@ namespace Umbraco.Web.Editors private readonly PropertyEditorCollection _propertyEditors; private readonly IScopeProvider _scopeProvider; private readonly IIOHelper _ioHelper; + private readonly IContentTypeService _contentTypeService; + private readonly UmbracoMapper _umbracoMapper; + private readonly IWebSecurity _webSecurity; + private readonly IDataTypeService _dataTypeService; + private readonly IShortStringHelper _shortStringHelper; + private readonly ILocalizedTextService _localizedTextService; + private readonly IFileService _fileService; + private readonly ILogger _logger; + private readonly IContentService _contentService; + private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; + private readonly ILocalizationService _LocalizationService; + private readonly IMacroService _macroService; + private readonly IEntityService _entityService; + private readonly IHostingEnvironment _hostingEnvironment; - public ContentTypeController(IEntityXmlSerializer serializer, + + public ContentTypeController( ICultureDictionary cultureDictionary, - IGlobalSettings globalSettings, - IUmbracoContextAccessor umbracoContextAccessor, - ISqlContext sqlContext, - PropertyEditorCollection propertyEditors, - ServiceContext services, - AppCaches appCaches, - IProfilingLogger logger, - IRuntimeState runtimeState, - IScopeProvider scopeProvider, - IShortStringHelper shortStringHelper, + EditorValidatorCollection editorValidatorCollection, + IContentTypeService contentTypeService, + IMediaTypeService mediaTypeService, + IMemberTypeService memberTypeService, UmbracoMapper umbracoMapper, + ILocalizedTextService localizedTextService, + IEntityXmlSerializer serializer, + IGlobalSettings globalSettings, + PropertyEditorCollection propertyEditors, + IScopeProvider scopeProvider, IIOHelper ioHelper, - IPublishedUrlProvider publishedUrlProvider, - EditorValidatorCollection editorValidatorCollection) - : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider, editorValidatorCollection) + IWebSecurity webSecurity, + IDataTypeService dataTypeService, + IShortStringHelper shortStringHelper, + IFileService fileService, + ILogger logger, + IContentService contentService, + IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, + ILocalizationService localizationService, + IMacroService macroService, + IEntityService entityService, + IHostingEnvironment hostingEnvironment) + : base(cultureDictionary, + editorValidatorCollection, + contentTypeService, + mediaTypeService, + memberTypeService, + umbracoMapper, + localizedTextService) { _serializer = serializer; _globalSettings = globalSettings; _propertyEditors = propertyEditors; _scopeProvider = scopeProvider; _ioHelper = ioHelper; + _contentTypeService = contentTypeService; + _umbracoMapper = umbracoMapper; + _webSecurity = webSecurity; + _dataTypeService = dataTypeService; + _shortStringHelper = shortStringHelper; + _localizedTextService = localizedTextService; + _fileService = fileService; + _logger = logger; + _contentService = contentService; + _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; + _LocalizationService = localizationService; + _macroService = macroService; + _entityService = entityService; + _hostingEnvironment = hostingEnvironment; } public int GetCount() { - return Services.ContentTypeService.Count(); + return _contentTypeService.Count(); } [HttpGet] [UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)] public bool HasContentNodes(int id) { - return Services.ContentTypeService.HasContentNodes(id); + return _contentTypeService.HasContentNodes(id); } public DocumentTypeDisplay GetById(int id) { - var ct = Services.ContentTypeService.Get(id); + var ct = _contentTypeService.Get(id); if (ct == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } - var dto = Mapper.Map(ct); + var dto = _umbracoMapper.Map(ct); return dto; } @@ -109,16 +156,16 @@ namespace Umbraco.Web.Editors /// [HttpDelete] [HttpPost] - public HttpResponseMessage DeleteById(int id) + public IActionResult DeleteById(int id) { - var foundType = Services.ContentTypeService.Get(id); + var foundType = _contentTypeService.Get(id); if (foundType == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } - Services.ContentTypeService.Delete(foundType, Security.CurrentUser.Id); - return Request.CreateResponse(HttpStatusCode.OK); + _contentTypeService.Delete(foundType, _webSecurity.CurrentUser.Id); + return Ok(); } /// @@ -131,7 +178,7 @@ namespace Umbraco.Web.Editors Constants.Trees.MemberTypes, Constants.Trees.Members)] public IEnumerable GetAllPropertyTypeAliases() { - return Services.ContentTypeService.GetAllPropertyTypeAliases(); + return _contentTypeService.GetAllPropertyTypeAliases(); } /// @@ -155,7 +202,7 @@ namespace Umbraco.Web.Editors /// /// [HttpPost] - public HttpResponseMessage GetAvailableCompositeContentTypes(GetAvailableCompositionsFilter filter) + public IActionResult GetAvailableCompositeContentTypes(GetAvailableCompositionsFilter filter) { var result = PerformGetAvailableCompositeContentTypes(filter.ContentTypeId, UmbracoObjectTypes.DocumentType, filter.FilterContentTypes, filter.FilterPropertyTypes, filter.IsElement) .Select(x => new @@ -163,7 +210,7 @@ namespace Umbraco.Web.Editors contentType = x.Item1, allowed = x.Item2 }); - return Request.CreateResponse(result); + return Ok(result); } /// /// Returns where a particular composition has been used @@ -172,14 +219,14 @@ namespace Umbraco.Web.Editors /// /// [HttpPost] - public HttpResponseMessage GetWhereCompositionIsUsedInContentTypes(GetAvailableCompositionsFilter filter) + public IActionResult GetWhereCompositionIsUsedInContentTypes(GetAvailableCompositionsFilter filter) { var result = PerformGetWhereCompositionIsUsedInContentTypes(filter.ContentTypeId, UmbracoObjectTypes.DocumentType) .Select(x => new { contentType = x }); - return Request.CreateResponse(result); + return Ok(result); } [UmbracoTreeAuthorize( @@ -188,14 +235,14 @@ namespace Umbraco.Web.Editors Constants.Trees.MemberTypes, Constants.Trees.Members)] public ContentPropertyDisplay GetPropertyTypeScaffold(int id) { - var dataTypeDiff = Services.DataTypeService.GetDataType(id); + var dataTypeDiff = _dataTypeService.GetDataType(id); if (dataTypeDiff == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } - var configuration = Services.DataTypeService.GetDataType(id).Configuration; + var configuration = _dataTypeService.GetDataType(id).Configuration; var editor = _propertyEditors[dataTypeDiff.EditorAlias]; return new ContentPropertyDisplay() @@ -214,37 +261,37 @@ namespace Umbraco.Web.Editors /// [HttpDelete] [HttpPost] - public HttpResponseMessage DeleteContainer(int id) + public IActionResult DeleteContainer(int id) { - Services.ContentTypeService.DeleteContainer(id, Security.CurrentUser.Id); + _contentTypeService.DeleteContainer(id, _webSecurity.CurrentUser.Id); - return Request.CreateResponse(HttpStatusCode.OK); + return Ok(); } - public HttpResponseMessage PostCreateContainer(int parentId, string name) + public IActionResult PostCreateContainer(int parentId, string name) { - var result = Services.ContentTypeService.CreateContainer(parentId, name, Security.CurrentUser.Id); + var result = _contentTypeService.CreateContainer(parentId, name, _webSecurity.CurrentUser.Id); return result - ? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id - : Request.CreateNotificationValidationErrorResponse(result.Exception.Message); + ? Ok(result.Result) //return the id + : throw HttpResponseException.CreateNotificationValidationErrorResponse(result.Exception.Message); } - public HttpResponseMessage PostRenameContainer(int id, string name) + public IActionResult PostRenameContainer(int id, string name) { - var result = Services.ContentTypeService.RenameContainer(id, name, Security.CurrentUser.Id); + var result = _contentTypeService.RenameContainer(id, name, _webSecurity.CurrentUser.Id); return result - ? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id - : Request.CreateNotificationValidationErrorResponse(result.Exception.Message); + ? Ok(result.Result) //return the id + : throw HttpResponseException.CreateNotificationValidationErrorResponse(result.Exception.Message); } public CreatedContentTypeCollectionResult PostCreateCollection(int parentId, string collectionName, bool collectionCreateTemplate, string collectionItemName, bool collectionItemCreateTemplate, string collectionIcon, string collectionItemIcon) { // create item doctype - var itemDocType = new ContentType(ShortStringHelper, parentId); + var itemDocType = new ContentType(_shortStringHelper, parentId); itemDocType.Name = collectionItemName; - itemDocType.Alias = collectionItemName.ToSafeAlias(ShortStringHelper, true); + itemDocType.Alias = collectionItemName.ToSafeAlias(_shortStringHelper, true); itemDocType.Icon = collectionItemIcon; // create item doctype template @@ -255,12 +302,12 @@ namespace Umbraco.Web.Editors } // save item doctype - Services.ContentTypeService.Save(itemDocType); + _contentTypeService.Save(itemDocType); // create collection doctype - var collectionDocType = new ContentType(ShortStringHelper, parentId); + var collectionDocType = new ContentType(_shortStringHelper, parentId); collectionDocType.Name = collectionName; - collectionDocType.Alias = collectionName.ToSafeAlias(ShortStringHelper, true); + collectionDocType.Alias = collectionName.ToSafeAlias(_shortStringHelper, true); collectionDocType.Icon = collectionIcon; collectionDocType.IsContainer = true; collectionDocType.AllowedContentTypes = new List() @@ -276,16 +323,16 @@ namespace Umbraco.Web.Editors } // save collection doctype - Services.ContentTypeService.Save(collectionDocType); + _contentTypeService.Save(collectionDocType); // test if the parent exist and then allow the collection underneath - var parentCt = Services.ContentTypeService.Get(parentId); + var parentCt = _contentTypeService.Get(parentId); if (parentCt != null) { var allowedCts = parentCt.AllowedContentTypes.ToList(); allowedCts.Add(new ContentTypeSort(collectionDocType.Id, allowedCts.Count())); parentCt.AllowedContentTypes = allowedCts; - Services.ContentTypeService.Save(parentCt); + _contentTypeService.Save(parentCt); } return new CreatedContentTypeCollectionResult @@ -310,8 +357,8 @@ namespace Umbraco.Web.Editors var savedCt = PerformPostSave( contentTypeSave: contentTypeSave, - getContentType: i => Services.ContentTypeService.Get(i), - saveContentType: type => Services.ContentTypeService.Save(type), + getContentType: i => _contentTypeService.Get(i), + saveContentType: type => _contentTypeService.Save(type), beforeCreateNew: ctSave => { //create a default template if it doesn't exist -but only if default template is == to the content type @@ -338,21 +385,21 @@ namespace Umbraco.Web.Editors } }); - var display = Mapper.Map(savedCt); + var display = _umbracoMapper.Map(savedCt); display.AddSuccessNotification( - Services.TextService.Localize("speechBubbles/contentTypeSavedHeader"), + _localizedTextService.Localize("speechBubbles/contentTypeSavedHeader"), string.Empty); return display; } - public TemplateDisplay PostCreateDefaultTemplate(int id) + public ActionResult PostCreateDefaultTemplate(int id) { - var contentType = Services.ContentTypeService.Get(id); + var contentType = _contentTypeService.Get(id); if (contentType == null) { - throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound, "No content type found with id " + id)); + return NotFound("No content type found with id " + id); } var template = CreateTemplateForContentType(contentType.Alias, contentType.Name); @@ -361,18 +408,18 @@ namespace Umbraco.Web.Editors throw new InvalidOperationException("Could not create default template for content type with id " + id); } - return Mapper.Map(template); + return _umbracoMapper.Map(template); } private ITemplate CreateTemplateForContentType(string contentTypeAlias, string contentTypeName) { - var template = Services.FileService.GetTemplate(contentTypeAlias); + var template = _fileService.GetTemplate(contentTypeAlias); if (template == null) { - var tryCreateTemplate = Services.FileService.CreateTemplateForContentType(contentTypeAlias, contentTypeName); + var tryCreateTemplate = _fileService.CreateTemplateForContentType(contentTypeAlias, contentTypeName); if (tryCreateTemplate == false) { - Logger.Warn("Could not create a template for Content Type: \"{ContentTypeAlias}\", status: {Status}", + _logger.Warn("Could not create a template for Content Type: \"{ContentTypeAlias}\", status: {Status}", contentTypeAlias, tryCreateTemplate.Result.Result); } @@ -392,15 +439,15 @@ namespace Umbraco.Web.Editors IContentType ct; if (parentId != Constants.System.Root) { - var parent = Services.ContentTypeService.Get(parentId); - ct = parent != null ? new ContentType(ShortStringHelper, parent, string.Empty) : new ContentType(ShortStringHelper, parentId); + var parent = _contentTypeService.Get(parentId); + ct = parent != null ? new ContentType(_shortStringHelper, parent, string.Empty) : new ContentType(_shortStringHelper, parentId); } else - ct = new ContentType(ShortStringHelper, parentId); + ct = new ContentType(_shortStringHelper, parentId); ct.Icon = Constants.Icons.Content; - var dto = Mapper.Map(ct); + var dto = _umbracoMapper.Map(ct); return dto; } @@ -410,8 +457,8 @@ namespace Umbraco.Web.Editors /// public IEnumerable GetAll() { - var types = Services.ContentTypeService.GetAll(); - var basics = types.Select(Mapper.Map); + var types = _contentTypeService.GetAll(); + var basics = types.Select(_umbracoMapper.Map); return basics.Select(basic => { @@ -434,27 +481,27 @@ namespace Umbraco.Web.Editors IEnumerable types; if (contentId == Constants.System.Root) { - types = Services.ContentTypeService.GetAll().Where(x => x.AllowedAsRoot).ToList(); + types = _contentTypeService.GetAll().Where(x => x.AllowedAsRoot).ToList(); } else { - var contentItem = Services.ContentService.GetById(contentId); + var contentItem = _contentService.GetById(contentId); if (contentItem == null) { return Enumerable.Empty(); } - var contentType = Services.ContentTypeBaseServices.GetContentTypeOf(contentItem); + var contentType = _contentTypeBaseServiceProvider.GetContentTypeOf(contentItem); var ids = contentType.AllowedContentTypes.OrderBy(c => c.SortOrder).Select(x => x.Id.Value).ToArray(); if (ids.Any() == false) return Enumerable.Empty(); - types = Services.ContentTypeService.GetAll(ids).OrderBy(c => ids.IndexOf(c.Id)).ToList(); + types = _contentTypeService.GetAll(ids).OrderBy(c => ids.IndexOf(c.Id)).ToList(); } - var basics = types.Where(type => type.IsElement == false).Select(Mapper.Map).ToList(); + var basics = types.Where(type => type.IsElement == false).Select(_umbracoMapper.Map).ToList(); - var localizedTextService = Services.TextService; + var localizedTextService = _localizedTextService; foreach (var basic in basics) { basic.Name = localizedTextService.UmbracoDictionaryTranslate(CultureDictionary, basic.Name); @@ -462,7 +509,7 @@ namespace Umbraco.Web.Editors } //map the blueprints - var blueprints = Services.ContentService.GetBlueprintsForContentTypes(types.Select(x => x.Id).ToArray()).ToArray(); + var blueprints = _contentService.GetBlueprintsForContentTypes(types.Select(x => x.Id).ToArray()).ToArray(); foreach (var basic in basics) { var docTypeBluePrints = blueprints.Where(x => x.ContentTypeId == (int) basic.Id).ToArray(); @@ -480,12 +527,12 @@ namespace Umbraco.Web.Editors /// /// /// - public HttpResponseMessage PostMove(MoveOrCopy move) + public IActionResult PostMove(MoveOrCopy move) { return PerformMove( move, - getContentType: i => Services.ContentTypeService.Get(i), - doMove: (type, i) => Services.ContentTypeService.Move(type, i)); + getContentType: i => _contentTypeService.Get(i), + doMove: (type, i) => _contentTypeService.Move(type, i)); } /// @@ -493,18 +540,18 @@ namespace Umbraco.Web.Editors /// /// /// - public HttpResponseMessage PostCopy(MoveOrCopy copy) + public IActionResult PostCopy(MoveOrCopy copy) { return PerformCopy( copy, - getContentType: i => Services.ContentTypeService.Get(i), - doCopy: (type, i) => Services.ContentTypeService.Copy(type, i)); + getContentType: i => _contentTypeService.Get(i), + doCopy: (type, i) => _contentTypeService.Copy(type, i)); } [HttpGet] public HttpResponseMessage Export(int id) { - var contentType = Services.ContentTypeService.Get(id); + var contentType = _contentTypeService.Get(id); if (contentType == null) throw new NullReferenceException("No content type found with id " + id); var xml = _serializer.Serialize(contentType); @@ -532,21 +579,21 @@ namespace Umbraco.Web.Editors } [HttpPost] - public HttpResponseMessage Import(string file) + public IActionResult Import(string file) { var filePath = Path.Combine(_ioHelper.MapPath(Core.Constants.SystemDirectories.Data), file); if (string.IsNullOrEmpty(file) || !System.IO.File.Exists(filePath)) { - return Request.CreateResponse(HttpStatusCode.NotFound); + return NotFound(); } - var dataInstaller = new PackageDataInstallation(Logger, Services.FileService, Services.MacroService, Services.LocalizationService, - Services.DataTypeService, Services.EntityService, Services.ContentTypeService, Services.ContentService, _propertyEditors, _scopeProvider, ShortStringHelper, _globalSettings, Services.TextService); + var dataInstaller = new PackageDataInstallation(_logger, _fileService, _macroService, _LocalizationService, + _dataTypeService, _entityService, _contentTypeService, _contentService, _propertyEditors, _scopeProvider, _shortStringHelper, _globalSettings, _localizedTextService); var xd = new XmlDocument {XmlResolver = null}; xd.Load(filePath); - var userId = Security.GetUserId().ResultOr(0); + var userId = _webSecurity.GetUserId().ResultOr(0); var element = XElement.Parse(xd.InnerXml); dataInstaller.ImportDocumentType(element, userId); @@ -557,70 +604,61 @@ namespace Umbraco.Web.Editors } catch (Exception ex) { - Logger.Error(ex, "Error cleaning up temporary udt file in App_Data: {File}", filePath); + _logger.Error(ex, "Error cleaning up temporary udt file in App_Data: {File}", filePath); } - return Request.CreateResponse(HttpStatusCode.OK); + return Ok(); } [HttpPost] [FileUploadCleanupFilter(false)] - public async Task Upload() + public async Task> Upload(List file) { - if (Request.Content.IsMimeMultipartContent() == false) - { - throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); - } - - var root = _ioHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "FileUploads"); - //ensure it exists - Directory.CreateDirectory(root); - var provider = new MultipartFormDataStreamProvider(root); - var result = await Request.Content.ReadAsMultipartAsync(provider); - - //must have a file - if (result.FileData.Count == 0) - { - throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); - } var model = new ContentTypeImportModel(); - var file = result.FileData[0]; - var fileName = file.Headers.ContentDisposition.FileName.Trim('\"'); - var ext = fileName.Substring(fileName.LastIndexOf('.') + 1).ToLower(); - - // renaming the file because MultipartFormDataStreamProvider has created a random fileName instead of using the name from the - // content-disposition for more than 6 years now. Creating a CustomMultipartDataStreamProvider deriving from MultipartFormDataStreamProvider - // seems like a cleaner option, but I'm not sure where to put it and renaming only takes one line of code. - System.IO.File.Move(result.FileData[0].LocalFileName, root + "\\" + fileName); - - if (ext.InvariantEquals("udt")) + foreach (var formFile in file) { - model.TempFileName = Path.Combine(root, fileName); + var fileName = formFile.FileName.Trim('\"'); + var ext = fileName.Substring(fileName.LastIndexOf('.') + 1).ToLower(); - model.UploadedFiles.Add(new ContentPropertyFile + var root = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempFileUploads); + var tempPath = Path.Combine(root,fileName); + + using (var stream = System.IO.File.Create(tempPath)) { - TempFilePath = model.TempFileName - }); + await formFile.CopyToAsync(stream); + } - var xd = new XmlDocument + if (ext.InvariantEquals("udt")) { - XmlResolver = null - }; - xd.Load(model.TempFileName); + model.TempFileName = Path.Combine(root, fileName); - model.Alias = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Alias")?.FirstChild.Value; - model.Name = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Name")?.FirstChild.Value; - } - else - { - model.Notifications.Add(new BackOfficeNotification( - Services.TextService.Localize("speechBubbles/operationFailedHeader"), - Services.TextService.Localize("media/disallowedFileType"), - NotificationStyle.Warning)); + model.UploadedFiles.Add(new ContentPropertyFile + { + TempFilePath = model.TempFileName + }); + + var xd = new XmlDocument + { + XmlResolver = null + }; + xd.Load(model.TempFileName); + + model.Alias = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Alias")?.FirstChild.Value; + model.Name = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Name")?.FirstChild.Value; + } + else + { + model.Notifications.Add(new BackOfficeNotification( + _localizedTextService.Localize("speechBubbles/operationFailedHeader"), + _localizedTextService.Localize("media/disallowedFileType"), + NotificationStyle.Warning)); + } } + + return model; } diff --git a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeControllerBase.cs similarity index 78% rename from src/Umbraco.Web/Editors/ContentTypeControllerBase.cs rename to src/Umbraco.Web.BackOffice/Controllers/ContentTypeControllerBase.cs index 3ffcb840ae..5357ee3ac3 100644 --- a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeControllerBase.cs @@ -3,33 +3,30 @@ using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Mime; using System.Text; -using System.Web.Http; -using System.Web.Http.ModelBinding; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.Net.Http.Headers; using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; using Umbraco.Core.Dictionary; using Umbraco.Core.Exceptions; -using Umbraco.Core.Logging; using Umbraco.Core.Mapping; using Umbraco.Core.Models; -using Umbraco.Core.Persistence; using Umbraco.Core.Services; -using Umbraco.Core.Strings; +using Umbraco.Extensions; +using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Exceptions; +using Umbraco.Web.Editors; using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Mvc; -using Umbraco.Web.Routing; -using Umbraco.Web.WebApi; -using HttpResponseException = System.Web.Http.HttpResponseException; -namespace Umbraco.Web.Editors +namespace Umbraco.Web.BackOffice.Controllers { /// /// Am abstract API controller providing functionality used for dealing with content and media types /// [PluginController("UmbracoApi")] - [PrefixlessBodyModelValidator] + //[PrefixlessBodyModelValidator] //TODO reintroduce public abstract class ContentTypeControllerBase : UmbracoAuthorizedJsonController where TContentType : class, IContentTypeComposition { @@ -37,24 +34,28 @@ namespace Umbraco.Web.Editors protected ContentTypeControllerBase( ICultureDictionary cultureDictionary, - IGlobalSettings globalSettings, - IUmbracoContextAccessor umbracoContextAccessor, - ISqlContext sqlContext, - ServiceContext services, - AppCaches appCaches, - IProfilingLogger logger, - IRuntimeState runtimeState, - IShortStringHelper shortStringHelper, + EditorValidatorCollection editorValidatorCollection, + IContentTypeService contentTypeService, + IMediaTypeService mediaTypeService, + IMemberTypeService memberTypeService, UmbracoMapper umbracoMapper, - IPublishedUrlProvider publishedUrlProvider, - EditorValidatorCollection editorValidatorCollection) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) + ILocalizedTextService localizedTextService) { - _editorValidatorCollection = editorValidatorCollection; - CultureDictionary = cultureDictionary; + _editorValidatorCollection = editorValidatorCollection ?? throw new ArgumentNullException(nameof(editorValidatorCollection)); + CultureDictionary = cultureDictionary ?? throw new ArgumentNullException(nameof(cultureDictionary)); + ContentTypeService = contentTypeService ?? throw new ArgumentNullException(nameof(contentTypeService)); + MediaTypeService = mediaTypeService ?? throw new ArgumentNullException(nameof(mediaTypeService)); + MemberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService)); + UmbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper)); + LocalizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService)); } protected ICultureDictionary CultureDictionary { get; } + public IContentTypeService ContentTypeService { get; } + public IMediaTypeService MediaTypeService { get; } + public IMemberTypeService MemberTypeService { get; } + public UmbracoMapper UmbracoMapper { get; } + public ILocalizedTextService LocalizedTextService { get; } /// /// Returns the available composite content types for a given content type @@ -89,35 +90,35 @@ namespace Umbraco.Web.Editors case UmbracoObjectTypes.DocumentType: if (contentTypeId > 0) { - source = Services.ContentTypeService.Get(contentTypeId); - if (source == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); + source = ContentTypeService.Get(contentTypeId); + if (source == null) throw new HttpResponseException(HttpStatusCode.NotFound); } - allContentTypes = Services.ContentTypeService.GetAll().Cast().ToArray(); + allContentTypes = ContentTypeService.GetAll().Cast().ToArray(); break; case UmbracoObjectTypes.MediaType: if (contentTypeId > 0) { - source = Services.MediaTypeService.Get(contentTypeId); - if (source == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); + source =MediaTypeService.Get(contentTypeId); + if (source == null) throw new HttpResponseException(HttpStatusCode.NotFound); } - allContentTypes = Services.MediaTypeService.GetAll().Cast().ToArray(); + allContentTypes =MediaTypeService.GetAll().Cast().ToArray(); break; case UmbracoObjectTypes.MemberType: if (contentTypeId > 0) { - source = Services.MemberTypeService.Get(contentTypeId); - if (source == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); + source = MemberTypeService.Get(contentTypeId); + if (source == null) throw new HttpResponseException(HttpStatusCode.NotFound); } - allContentTypes = Services.MemberTypeService.GetAll().Cast().ToArray(); + allContentTypes = MemberTypeService.GetAll().Cast().ToArray(); break; default: throw new ArgumentOutOfRangeException("The entity type was not a content type"); } - var availableCompositions = Services.ContentTypeService.GetAvailableCompositeContentTypes(source, allContentTypes, filterContentTypes, filterPropertyTypes, isElement); + var availableCompositions = ContentTypeService.GetAvailableCompositeContentTypes(source, allContentTypes, filterContentTypes, filterPropertyTypes, isElement); @@ -126,7 +127,7 @@ namespace Umbraco.Web.Editors var ancestors = availableCompositions.Ancestors.Select(x => x.Alias); return availableCompositions.Results - .Select(x => new Tuple(Mapper.Map(x.Composition), x.Allowed)) + .Select(x => new Tuple(UmbracoMapper.Map(x.Composition), x.Allowed)) .Select(x => { //we need to ensure that the item is enabled if it is already selected @@ -160,9 +161,9 @@ namespace Umbraco.Web.Editors switch (type) { case UmbracoObjectTypes.DocumentType: - return Services.ContentTypeService.GetContainers(contentType as IContentType); + return ContentTypeService.GetContainers(contentType as IContentType); case UmbracoObjectTypes.MediaType: - return Services.MediaTypeService.GetContainers(contentType as IMediaType); + return MediaTypeService.GetContainers(contentType as IMediaType); case UmbracoObjectTypes.MemberType: return new EntityContainer[0]; default: @@ -187,15 +188,15 @@ namespace Umbraco.Web.Editors switch (type) { case UmbracoObjectTypes.DocumentType: - source = Services.ContentTypeService.Get(contentTypeId); + source = ContentTypeService.Get(contentTypeId); break; case UmbracoObjectTypes.MediaType: - source = Services.MediaTypeService.Get(contentTypeId); + source =MediaTypeService.Get(contentTypeId); break; case UmbracoObjectTypes.MemberType: - source = Services.MemberTypeService.Get(contentTypeId); + source = MemberTypeService.Get(contentTypeId); break; default: @@ -203,7 +204,7 @@ namespace Umbraco.Web.Editors } if (source == null) - throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); + throw new HttpResponseException(HttpStatusCode.NotFound); id = source.Id; } @@ -213,15 +214,15 @@ namespace Umbraco.Web.Editors switch (type) { case UmbracoObjectTypes.DocumentType: - composedOf = Services.ContentTypeService.GetComposedOf(id); + composedOf = ContentTypeService.GetComposedOf(id); break; case UmbracoObjectTypes.MediaType: - composedOf = Services.MediaTypeService.GetComposedOf(id); + composedOf =MediaTypeService.GetComposedOf(id); break; case UmbracoObjectTypes.MemberType: - composedOf = Services.MemberTypeService.GetComposedOf(id); + composedOf = MemberTypeService.GetComposedOf(id); break; default: @@ -235,7 +236,7 @@ namespace Umbraco.Web.Editors } return composedOf - .Select(Mapper.Map) + .Select(UmbracoMapper.Map) .Select(TranslateName) .ToList(); } @@ -269,11 +270,11 @@ namespace Umbraco.Web.Editors // it in fact cannot be the same as any content type alias (member, content or media) because // this would interfere with how ModelsBuilder works and also how many of the published caches // works since that is based on aliases. - var allAliases = Services.ContentTypeService.GetAllContentTypeAliases(); + var allAliases = ContentTypeService.GetAllContentTypeAliases(); var exists = allAliases.InvariantContains(contentTypeSave.Alias); if (exists && (ctId == 0 || !ct.Alias.InvariantEquals(contentTypeSave.Alias))) { - ModelState.AddModelError("Alias", Services.TextService.Localize("editcontenttype/aliasAlreadyExists")); + ModelState.AddModelError("Alias", LocalizedTextService.Localize("editcontenttype/aliasAlreadyExists")); } // execute the external validators @@ -298,7 +299,7 @@ namespace Umbraco.Web.Editors //This mapping will cause a lot of content type validation to occur which we need to deal with try { - Mapper.Map(contentTypeSave, ct); + UmbracoMapper.Map(contentTypeSave, ct); } catch (Exception ex) { @@ -338,7 +339,7 @@ namespace Umbraco.Web.Editors try { //This mapping will cause a lot of content type validation to occur which we need to deal with - newCt = Mapper.Map(contentTypeSave); + newCt = UmbracoMapper.Map(contentTypeSave); } catch (Exception ex) { @@ -390,7 +391,7 @@ namespace Umbraco.Web.Editors /// /// /// - protected HttpResponseMessage PerformMove( + protected IActionResult PerformMove( MoveOrCopy move, Func getContentType, Func>> doMove) @@ -398,29 +399,27 @@ namespace Umbraco.Web.Editors var toMove = getContentType(move.Id); if (toMove == null) { - return Request.CreateResponse(HttpStatusCode.NotFound); + return NotFound(); } var result = doMove(toMove, move.ParentId); if (result.Success) { - var response = Request.CreateResponse(HttpStatusCode.OK); - response.Content = new StringContent(toMove.Path, Encoding.UTF8, "text/plain"); - return response; + return Content(toMove.Path, MediaTypeNames.Text.Plain, Encoding.UTF8); } switch (result.Result.Result) { case MoveOperationStatusType.FailedParentNotFound: - return Request.CreateResponse(HttpStatusCode.NotFound); + return NotFound(); case MoveOperationStatusType.FailedCancelledByEvent: //returning an object of INotificationModel will ensure that any pending // notification messages are added to the response. - return Request.CreateValidationErrorResponse(new SimpleNotificationModel()); + throw HttpResponseException.CreateValidationErrorResponse(new SimpleNotificationModel()); case MoveOperationStatusType.FailedNotAllowedByPath: var notificationModel = new SimpleNotificationModel(); - notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByPath"), ""); - return Request.CreateValidationErrorResponse(notificationModel); + notificationModel.AddErrorNotification(LocalizedTextService.Localize("moveOrCopy/notAllowedByPath"), ""); + throw HttpResponseException.CreateValidationErrorResponse(notificationModel); default: throw new ArgumentOutOfRangeException(); } @@ -433,7 +432,7 @@ namespace Umbraco.Web.Editors /// /// /// - protected HttpResponseMessage PerformCopy( + protected IActionResult PerformCopy( MoveOrCopy move, Func getContentType, Func>> doCopy) @@ -441,30 +440,27 @@ namespace Umbraco.Web.Editors var toMove = getContentType(move.Id); if (toMove == null) { - return Request.CreateResponse(HttpStatusCode.NotFound); + return NotFound(); } var result = doCopy(toMove, move.ParentId); if (result.Success) { - var copy = result.Result.Entity; - var response = Request.CreateResponse(HttpStatusCode.OK); - response.Content = new StringContent(copy.Path, Encoding.UTF8, "text/plain"); - return response; + return Content(toMove.Path, MediaTypeNames.Text.Plain, Encoding.UTF8); } switch (result.Result.Result) { case MoveOperationStatusType.FailedParentNotFound: - return Request.CreateResponse(HttpStatusCode.NotFound); + return NotFound(); case MoveOperationStatusType.FailedCancelledByEvent: //returning an object of INotificationModel will ensure that any pending // notification messages are added to the response. - return Request.CreateValidationErrorResponse(new SimpleNotificationModel()); + throw HttpResponseException.CreateValidationErrorResponse(new SimpleNotificationModel()); case MoveOperationStatusType.FailedNotAllowedByPath: var notificationModel = new SimpleNotificationModel(); - notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByPath"), ""); - return Request.CreateValidationErrorResponse(notificationModel); + notificationModel.AddErrorNotification(LocalizedTextService.Localize("moveOrCopy/notAllowedByPath"), ""); + throw HttpResponseException.CreateValidationErrorResponse(notificationModel); default: throw new ArgumentOutOfRangeException(); } @@ -481,7 +477,7 @@ namespace Umbraco.Web.Editors where TPropertyType : PropertyTypeBasic where TContentTypeDisplay : ContentTypeCompositionDisplay { - var service = Services.GetContentTypeService(); + var service = GetContentTypeService(); var validateAttempt = service.ValidateComposition(composition); if (validateAttempt == false) { @@ -490,15 +486,27 @@ namespace Umbraco.Web.Editors var invalidPropertyAliases = validateAttempt.Result.Distinct(); AddCompositionValidationErrors(contentTypeSave, invalidPropertyAliases); - var display = Mapper.Map(composition); + var display = UmbracoMapper.Map(composition); //map the 'save' data on top - display = Mapper.Map(contentTypeSave, display); + display = UmbracoMapper.Map(contentTypeSave, display); display.Errors = ModelState.ToErrorDictionary(); - throw new HttpResponseException(Request.CreateValidationErrorResponse(display)); + throw HttpResponseException.CreateValidationErrorResponse(display); } return null; } + public IContentTypeBaseService GetContentTypeService() + where T : IContentTypeComposition + { + if (typeof(T).Implements()) + return ContentTypeService as IContentTypeBaseService; + if (typeof(T).Implements()) + return MediaTypeService as IContentTypeBaseService; + if (typeof(T).Implements()) + return MemberTypeService as IContentTypeBaseService; + throw new ArgumentException("Type " + typeof(T).FullName + " does not have a service."); + } + /// /// Adds errors to the model state if any invalid aliases are found then throws an error response if there are errors /// @@ -570,18 +578,18 @@ namespace Umbraco.Web.Editors if (ctId > 0) { //Required data is invalid so we cannot continue - forDisplay = Mapper.Map(ct); + forDisplay = UmbracoMapper.Map(ct); //map the 'save' data on top - forDisplay = Mapper.Map(contentTypeSave, forDisplay); + forDisplay = UmbracoMapper.Map(contentTypeSave, forDisplay); } else { //map the 'save' data to display - forDisplay = Mapper.Map(contentTypeSave); + forDisplay = UmbracoMapper.Map(contentTypeSave); } forDisplay.Errors = ModelState.ToErrorDictionary(); - return new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay)); + return HttpResponseException.CreateValidationErrorResponse(forDisplay); } } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs index 3f0ab63c65..9db0cc2c75 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Data; using System.Linq; using System.Net; +using System.Net.Mime; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; @@ -316,7 +317,7 @@ namespace Umbraco.Web.BackOffice.Controllers var result = _dataTypeService.Move(toMove, move.ParentId); if (result.Success) { - return Content(toMove.Path,"text/plain", Encoding.UTF8); + return Content(toMove.Path,MediaTypeNames.Text.Plain, Encoding.UTF8); } switch (result.Result.Result) diff --git a/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs b/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs index a1f7782ea6..4197a4726e 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs @@ -33,11 +33,11 @@ namespace Umbraco.Web.BackOffice.Controllers } else if (parameterDescriptor.ParameterType == typeof(string)) { - canUse = true; + canUse &= true; } else { - canUse = false; + canUse &= true; } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs index 0a44d360d9..efe14a2c3c 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs @@ -4,9 +4,12 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Mime; using System.Text; using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.IO; using Umbraco.Core.Logging; @@ -17,6 +20,7 @@ using Umbraco.Web.Models.ContentEditing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Dictionary; using Umbraco.Core.Events; +using Umbraco.Core.Hosting; using Umbraco.Core.Models.ContentEditing; using Umbraco.Core.Models.Editors; using Umbraco.Core.Models.Entities; @@ -30,14 +34,14 @@ using Constants = Umbraco.Core.Constants; using Umbraco.Core.Mapping; using Umbraco.Core.Strings; using Umbraco.Extensions; -using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.BackOffice.Filters; +using Umbraco.Web.BackOffice.ModelBinders; +using Umbraco.Web.Common.ActionResults; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Exceptions; -using Umbraco.Web.Editors.Binders; using Umbraco.Web.Security; -namespace Umbraco.Web.Editors +namespace Umbraco.Web.BackOffice.Controllers { /// /// This controller is decorated with the UmbracoApplicationAuthorizeAttribute which means that any user requesting @@ -47,8 +51,8 @@ namespace Umbraco.Web.Editors [UmbracoApplicationAuthorize(Constants.Applications.Media)] public class MediaController : ContentControllerBase { + private readonly IShortStringHelper _shortStringHelper; private readonly IContentSettings _contentSettings; - private readonly IIOHelper _ioHelper; private readonly IMediaTypeService _mediaTypeService; private readonly IMediaService _mediaService; private readonly IEntityService _entityService; @@ -66,7 +70,6 @@ namespace Umbraco.Web.Editors IEventMessagesFactory eventMessages, ILocalizedTextService localizedTextService, IContentSettings contentSettings, - IIOHelper ioHelper, IMediaTypeService mediaTypeService, IMediaService mediaService, IEntityService entityService, @@ -77,11 +80,12 @@ namespace Umbraco.Web.Editors IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IRelationService relationService, PropertyEditorCollection propertyEditors, - IMediaFileSystem mediaFileSystem) + IMediaFileSystem mediaFileSystem, + IHostingEnvironment hostingEnvironment) : base(cultureDictionary, logger, shortStringHelper, eventMessages, localizedTextService) { + _shortStringHelper = shortStringHelper; _contentSettings = contentSettings; - _ioHelper = ioHelper; _mediaTypeService = mediaTypeService; _mediaService = mediaService; _entityService = entityService; @@ -94,6 +98,7 @@ namespace Umbraco.Web.Editors _relationService = relationService; _propertyEditors = propertyEditors; _mediaFileSystem = mediaFileSystem; + _hostingEnvironment = hostingEnvironment; } /// @@ -268,6 +273,7 @@ namespace Umbraco.Web.Editors private int[] _userStartNodes; private readonly PropertyEditorCollection _propertyEditors; private readonly IMediaFileSystem _mediaFileSystem; + private readonly IHostingEnvironment _hostingEnvironment; protected int[] UserStartNodes @@ -278,7 +284,7 @@ namespace Umbraco.Web.Editors /// /// Returns the child media objects - using the entity INT id /// - [FilterAllowedOutgoingMedia(typeof(IEnumerable>), "Items")] // TODO introduce when moved to .NET Core//[FilterAllowedOutgoingMedia(typeof(IEnumerable>), "Items")] + [FilterAllowedOutgoingMedia(typeof(IEnumerable>), "Items")] [DetermineAmbiguousActionByPassingParameters] public PagedResult> GetChildren(int id, int pageNumber = 0, @@ -417,7 +423,7 @@ namespace Umbraco.Web.Editors /// [EnsureUserPermissionForMedia("id")] [HttpPost] - public HttpResponseMessage DeleteById(int id) + public IActionResult DeleteById(int id) { var foundMedia = GetObjectFromRequest(() => _mediaService.GetById(id)); @@ -434,7 +440,7 @@ namespace Umbraco.Web.Editors { //returning an object of INotificationModel will ensure that any pending // notification messages are added to the response. - return Request.CreateValidationErrorResponse(new SimpleNotificationModel()); + throw HttpResponseException.CreateValidationErrorResponse(new SimpleNotificationModel()); } } else @@ -444,11 +450,11 @@ namespace Umbraco.Web.Editors { //returning an object of INotificationModel will ensure that any pending // notification messages are added to the response. - return Request.CreateValidationErrorResponse(new SimpleNotificationModel()); + throw HttpResponseException.CreateValidationErrorResponse(new SimpleNotificationModel()); } } - return Request.CreateResponse(HttpStatusCode.OK); + return Ok(); } /// @@ -457,7 +463,7 @@ namespace Umbraco.Web.Editors /// /// [EnsureUserPermissionForMedia("move.Id")] - public HttpResponseMessage PostMove(MoveOrCopy move) + public IActionResult PostMove(MoveOrCopy move) { var toMove = ValidateMoveOrCopy(move); var destinationParentID = move.ParentId; @@ -467,17 +473,15 @@ namespace Umbraco.Web.Editors if (sourceParentID == destinationParentID) { - return Request.CreateValidationErrorResponse(new SimpleNotificationModel(new BackOfficeNotification("",_localizedTextService.Localize("media/moveToSameFolderFailed"),NotificationStyle.Error))); + throw HttpResponseException.CreateValidationErrorResponse(new SimpleNotificationModel(new BackOfficeNotification("",_localizedTextService.Localize("media/moveToSameFolderFailed"),NotificationStyle.Error))); } if (moveResult == false) { - return Request.CreateValidationErrorResponse(new SimpleNotificationModel()); + throw HttpResponseException.CreateValidationErrorResponse(new SimpleNotificationModel()); } else { - var response = Request.CreateResponse(HttpStatusCode.OK); - response.Content = new StringContent(toMove.Path, Encoding.UTF8, "text/plain"); - return response; + return Content(toMove.Path, MediaTypeNames.Text.Plain, Encoding.UTF8); } } @@ -487,7 +491,7 @@ namespace Umbraco.Web.Editors /// [FileUploadCleanupFilter] [MediaItemSaveValidation] - // [OutgoingEditorModelEvent] // TODO introduce when moved to .NET Core + [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] public MediaItemDisplay PostSave( [ModelBinder(typeof(MediaItemBinder))] MediaItemSave contentItem) @@ -534,7 +538,7 @@ namespace Umbraco.Web.Editors // add the model state to the outgoing object and throw validation response var forDisplay = _umbracoMapper.Map(contentItem.PersistedContent); forDisplay.Errors = ModelState.ToErrorDictionary(); - throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay)); + throw HttpResponseException.CreateValidationErrorResponse(forDisplay); } } @@ -567,7 +571,7 @@ namespace Umbraco.Web.Editors // is no Id to redirect to! if (saveStatus.Result.Result == OperationResultType.FailedCancelledByEvent && IsCreatingAction(contentItem.Action)) { - throw new HttpResponseException(Request.CreateValidationErrorResponse(display)); + throw HttpResponseException.CreateValidationErrorResponse(display); } } @@ -583,11 +587,11 @@ namespace Umbraco.Web.Editors /// [HttpDelete] [HttpPost] - public HttpResponseMessage EmptyRecycleBin() + public IActionResult EmptyRecycleBin() { _mediaService.EmptyRecycleBin(_webSecurity.GetUserId().ResultOr(Constants.Security.SuperUserId)); - return Request.CreateNotificationSuccessResponse(_localizedTextService.Localize("defaultdialogs/recycleBinIsEmpty")); + return new UmbracoNotificationSuccessResponse(_localizedTextService.Localize("defaultdialogs/recycleBinIsEmpty")); } /// @@ -596,17 +600,17 @@ namespace Umbraco.Web.Editors /// /// [EnsureUserPermissionForMedia("sorted.ParentId")] - public HttpResponseMessage PostSort(ContentSortOrder sorted) + public IActionResult PostSort(ContentSortOrder sorted) { if (sorted == null) { - return Request.CreateResponse(HttpStatusCode.NotFound); + return NotFound(); } //if there's nothing to sort just return ok if (sorted.IdSortOrder.Length == 0) { - return Request.CreateResponse(HttpStatusCode.OK); + return Ok(); } var mediaService = _mediaService; @@ -619,9 +623,9 @@ namespace Umbraco.Web.Editors if (mediaService.Sort(sortedMedia) == false) { Logger.Warn("Media sorting failed, this was probably caused by an event being cancelled"); - return Request.CreateValidationErrorResponse("Media sorting failed, this was probably caused by an event being cancelled"); + throw HttpResponseException.CreateValidationErrorResponse("Media sorting failed, this was probably caused by an event being cancelled"); } - return Request.CreateResponse(HttpStatusCode.OK); + return Ok(); } catch (Exception ex) { @@ -630,13 +634,18 @@ namespace Umbraco.Web.Editors } } - public MediaItemDisplay PostAddFolder(PostedFolder folder) + public ActionResult PostAddFolder(PostedFolder folder) { - var intParentId = GetParentIdAsInt(folder.ParentId, validatePermissions:true); + var parentId = GetParentIdAsInt(folder.ParentId, validatePermissions:true); + if (!parentId.HasValue) + { + return NotFound("The passed id doesn't exist"); + } + var mediaService = _mediaService; - var f = mediaService.CreateMedia(folder.Name, intParentId, Constants.Conventions.MediaTypes.Folder); + var f = mediaService.CreateMedia(folder.Name, parentId.Value, Constants.Conventions.MediaTypes.Folder); mediaService.Save(f, _webSecurity.CurrentUser.Id); return _umbracoMapper.Map(f); @@ -649,39 +658,32 @@ namespace Umbraco.Web.Editors /// /// We cannot validate this request with attributes (nicely) due to the nature of the multi-part for data. /// - [FileUploadCleanupFilter(false)] - public async Task PostAddFile() + public async Task PostAddFile([FromForm]string path, [FromForm]string currentFolder, [FromForm]string contentTypeAlias, List file) { - if (Request.Content.IsMimeMultipartContent() == false) - { - throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); - } - - var root = _ioHelper.MapPath(Constants.SystemDirectories.TempFileUploads); + var root = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempFileUploads); //ensure it exists Directory.CreateDirectory(root); - var provider = new MultipartFormDataStreamProvider(root); - - var result = await Request.Content.ReadAsMultipartAsync(provider); //must have a file - if (result.FileData.Count == 0) + if (file.Count == 0) { - return Request.CreateResponse(HttpStatusCode.NotFound); + return NotFound(); } //get the string json from the request - string currentFolderId = result.FormData["currentFolder"]; - int parentId = GetParentIdAsInt(currentFolderId, validatePermissions: true); - + var parentId = GetParentIdAsInt(currentFolder, validatePermissions: true); + if (!parentId.HasValue) + { + return NotFound("The passed id doesn't exist"); + } var tempFiles = new PostedFiles(); 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")) + if (!string.IsNullOrEmpty(path)) { - var folders = result.FormData["path"].Split('/'); + var folders = path.Split('/'); for (int i = 0; i < folders.Length - 1; i++) { @@ -704,11 +706,11 @@ namespace Umbraco.Web.Editors else { //get current parent - var mediaRoot = mediaService.GetById(parentId); + var mediaRoot = mediaService.GetById(parentId.Value); //if the media root is null, something went wrong, we'll abort if (mediaRoot == null) - return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, + return Problem( "The folder: " + folderName + " could not be used for storing images, its ID: " + parentId + " returned null"); @@ -728,9 +730,9 @@ namespace Umbraco.Web.Editors } //get the files - foreach (var file in result.FileData) + foreach (var formFile in file) { - var fileName = file.Headers.ContentDisposition.FileName.Trim(new[] { '\"' }).TrimEnd(); + var fileName = formFile.FileName.Trim(new[] { '\"' }).TrimEnd(); var safeFileName = fileName.ToSafeFileName(ShortStringHelper); var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower(); @@ -738,7 +740,7 @@ namespace Umbraco.Web.Editors { var mediaType = Constants.Conventions.MediaTypes.File; - if (result.FormData["contentTypeAlias"] == Constants.Conventions.MediaTypes.AutoSelect) + if (contentTypeAlias == Constants.Conventions.MediaTypes.AutoSelect) { if (_contentSettings.ImageFileTypes.Contains(ext)) { @@ -747,36 +749,27 @@ namespace Umbraco.Web.Editors } else { - mediaType = result.FormData["contentTypeAlias"]; + mediaType = contentTypeAlias; } var mediaItemName = fileName.ToFriendlyName(); - var f = mediaService.CreateMedia(mediaItemName, parentId, mediaType, _webSecurity.CurrentUser.Id); + var f = mediaService.CreateMedia(mediaItemName, parentId.Value, 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) + + await using (var stream = formFile.OpenReadStream()) { - f.SetValue(_mediaFileSystem, ShortStringHelper, _contentTypeBaseServiceProvider, Constants.Conventions.Media.File,fileName, fs); + f.SetValue(_mediaFileSystem,_shortStringHelper, _contentTypeBaseServiceProvider, Constants.Conventions.Media.File,fileName, stream); } + var saveResult = mediaService.Save(f, _webSecurity.CurrentUser.Id); if (saveResult == false) { AddCancelMessage(tempFiles, message: _localizedTextService.Localize("speechBubbles/operationCancelledText") + " -- " + mediaItemName); } - else - { - tempFiles.UploadedFiles.Add(new ContentPropertyFile - { - FileName = fileName, - PropertyAlias = Constants.Conventions.Media.File, - TempFilePath = file.LocalFileName - }); - } + } else { @@ -788,19 +781,16 @@ namespace Umbraco.Web.Editors } //Different response if this is a 'blueimp' request - if (Request.GetQueryNameValuePairs().Any(x => x.Key == "origin")) + if (HttpContext.Request.Query.Any(x => x.Key == "origin")) { - var origin = Request.GetQueryNameValuePairs().First(x => x.Key == "origin"); + var origin = HttpContext.Request.Query.First(x => x.Key == "origin"); if (origin.Value == "blueimp") { - return Request.CreateResponse(HttpStatusCode.OK, - tempFiles, - //Don't output the angular xsrf stuff, blue imp doesn't like that - new JsonMediaTypeFormatter()); + return new JsonResult(tempFiles); //Don't output the angular xsrf stuff, blue imp doesn't like that } } - return Request.CreateResponse(HttpStatusCode.OK, tempFiles); + return Ok(); } private IMedia FindInChildren(int mediaId, string nameToFind, string contentTypeAlias) @@ -827,7 +817,7 @@ namespace Umbraco.Web.Editors /// and if that check fails an unauthorized exception will occur /// /// - private ActionResult GetParentIdAsInt(string parentId, bool validatePermissions) + private int? GetParentIdAsInt(string parentId, bool validatePermissions) { int intParentId; @@ -851,7 +841,7 @@ namespace Umbraco.Web.Editors } else { - return NotFound("The passed id doesn't exist"); + return null; } } else diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs new file mode 100644 index 0000000000..bed8ae3183 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs @@ -0,0 +1,351 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Core; +using Umbraco.Core.Dictionary; +using Umbraco.Core.Mapping; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; +using Umbraco.Web.BackOffice.Filters; +using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Exceptions; +using Umbraco.Web.Editors; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Security; + +namespace Umbraco.Web.BackOffice.Controllers +{ + // TODO: We'll need to be careful about the security on this controller, when we start implementing + // methods to modify content types we'll need to enforce security on the individual methods, we + // cannot put security on the whole controller because things like GetAllowedChildren are required for content editing. + + /// + /// An API controller used for dealing with content types + /// + [PluginController("UmbracoApi")] + [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] + public class MediaTypeController : ContentTypeControllerBase + { + private readonly IContentTypeService _contentTypeService; + private readonly IEntityService _entityService; + private readonly ILocalizedTextService _localizedTextService; + private readonly IMediaService _mediaService; + private readonly IMediaTypeService _mediaTypeService; + private readonly IShortStringHelper _shortStringHelper; + private readonly UmbracoMapper _umbracoMapper; + private readonly IWebSecurity _webSecurity; + + public MediaTypeController(ICultureDictionary cultureDictionary, + EditorValidatorCollection editorValidatorCollection, + IContentTypeService contentTypeService, + IMediaTypeService mediaTypeService, + IMemberTypeService memberTypeService, + UmbracoMapper umbracoMapper, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper, + IEntityService entityService, + IMediaService mediaService, + IWebSecurity webSecurity) + : base( + cultureDictionary, + editorValidatorCollection, + contentTypeService, + mediaTypeService, + memberTypeService, + umbracoMapper, + localizedTextService) + { + _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); + _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); + _mediaTypeService = mediaTypeService ?? throw new ArgumentNullException(nameof(mediaTypeService)); + _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); + _umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper)); + _contentTypeService = contentTypeService ?? throw new ArgumentNullException(nameof(contentTypeService)); + _webSecurity = webSecurity ?? throw new ArgumentNullException(nameof(webSecurity)); + _localizedTextService = + localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService)); + } + + public int GetCount() => _contentTypeService.Count(); + + [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] + public MediaTypeDisplay GetById(int id) + { + var ct = _mediaTypeService.Get(id); + if (ct == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + var dto = _umbracoMapper.Map(ct); + return dto; + } + + /// + /// Deletes a media type with a given ID + /// + /// + /// + [HttpDelete] + [HttpPost] + public IActionResult DeleteById(int id) + { + var foundType = _mediaTypeService.Get(id); + if (foundType == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + _mediaTypeService.Delete(foundType, _webSecurity.CurrentUser.Id); + return Ok(); + } + + /// + /// Returns the available compositions for this content type + /// This has been wrapped in a dto instead of simple parameters to support having multiple parameters in post request + /// body + /// + /// + /// + /// 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. + /// + /// + /// Filter applied when resolving compositions + /// + /// + [HttpPost] + public IActionResult GetAvailableCompositeMediaTypes(GetAvailableCompositionsFilter filter) + { + var result = PerformGetAvailableCompositeContentTypes(filter.ContentTypeId, UmbracoObjectTypes.MediaType, + filter.FilterContentTypes, filter.FilterPropertyTypes, filter.IsElement) + .Select(x => new + { + contentType = x.Item1, + allowed = x.Item2 + }); + return Ok(result); + } + + /// + /// Returns where a particular composition has been used + /// This has been wrapped in a dto instead of simple parameters to support having multiple parameters in post request + /// body + /// + /// + /// + [HttpPost] + public IActionResult GetWhereCompositionIsUsedInContentTypes(GetAvailableCompositionsFilter filter) + { + var result = + PerformGetWhereCompositionIsUsedInContentTypes(filter.ContentTypeId, UmbracoObjectTypes.MediaType) + .Select(x => new + { + contentType = x + }); + return Ok(result); + } + + public MediaTypeDisplay GetEmpty(int parentId) + { + IMediaType mt; + if (parentId != Constants.System.Root) + { + var parent = _mediaTypeService.Get(parentId); + mt = parent != null + ? new MediaType(_shortStringHelper, parent, string.Empty) + : new MediaType(_shortStringHelper, parentId); + } + else + mt = new MediaType(_shortStringHelper, parentId); + + mt.Icon = Constants.Icons.MediaImage; + + var dto = _umbracoMapper.Map(mt); + return dto; + } + + + /// + /// Returns all media types + /// + public IEnumerable GetAll() => + _mediaTypeService.GetAll() + .Select(_umbracoMapper.Map); + + /// + /// Deletes a media type container with a given ID + /// + /// + /// + [HttpDelete] + [HttpPost] + public IActionResult DeleteContainer(int id) + { + _mediaTypeService.DeleteContainer(id, _webSecurity.CurrentUser.Id); + + return Ok(); + } + + public IActionResult PostCreateContainer(int parentId, string name) + { + var result = _mediaTypeService.CreateContainer(parentId, name, _webSecurity.CurrentUser.Id); + + return result + ? Ok(result.Result) //return the id + : throw HttpResponseException.CreateNotificationValidationErrorResponse(result.Exception.Message); + } + + public IActionResult PostRenameContainer(int id, string name) + { + var result = _mediaTypeService.RenameContainer(id, name, _webSecurity.CurrentUser.Id); + + return result + ? Ok(result.Result) //return the id + : throw HttpResponseException.CreateNotificationValidationErrorResponse(result.Exception.Message); + } + + public MediaTypeDisplay PostSave(MediaTypeSave contentTypeSave) + { + var savedCt = PerformPostSave( + contentTypeSave, + i => _mediaTypeService.Get(i), + type => _mediaTypeService.Save(type)); + + var display = _umbracoMapper.Map(savedCt); + + display.AddSuccessNotification( + _localizedTextService.Localize("speechBubbles/mediaTypeSavedHeader"), + string.Empty); + + return display; + } + + /// + /// Move the media type + /// + /// + /// + public IActionResult PostMove(MoveOrCopy move) + { + return PerformMove( + move, + i => _mediaTypeService.Get(i), + (type, i) => _mediaTypeService.Move(type, i)); + } + + /// + /// Copy the media type + /// + /// + /// + public IActionResult PostCopy(MoveOrCopy copy) + { + return PerformCopy( + copy, + i => _mediaTypeService.Get(i), + (type, i) => _mediaTypeService.Copy(type, i)); + } + + + #region GetAllowedChildren + + /// + /// Returns the allowed child content type objects for the content item id passed in - based on an INT id + /// + /// + [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] + [DetermineAmbiguousActionByPassingParameters] + public IEnumerable GetAllowedChildren(int contentId) + { + if (contentId == Constants.System.RecycleBinContent) + return Enumerable.Empty(); + + IEnumerable types; + if (contentId == Constants.System.Root) + { + types = _mediaTypeService.GetAll().ToList(); + + //if no allowed root types are set, just return everything + if (types.Any(x => x.AllowedAsRoot)) + types = types.Where(x => x.AllowedAsRoot); + } + else + { + var contentItem = _mediaService.GetById(contentId); + if (contentItem == null) + { + return Enumerable.Empty(); + } + + var contentType = _mediaTypeService.Get(contentItem.ContentTypeId); + var ids = contentType.AllowedContentTypes.OrderBy(c => c.SortOrder).Select(x => x.Id.Value).ToArray(); + + if (ids.Any() == false) return Enumerable.Empty(); + + types = _mediaTypeService.GetAll(ids).OrderBy(c => ids.IndexOf(c.Id)).ToList(); + } + + var basics = types.Select(_umbracoMapper.Map).ToList(); + + foreach (var basic in basics) + { + basic.Name = TranslateItem(basic.Name); + basic.Description = TranslateItem(basic.Description); + } + + return basics.OrderBy(c => contentId == Constants.System.Root ? c.Name : string.Empty); + } + + /// + /// Returns the allowed child content type objects for the content item id passed in - based on a GUID id + /// + /// + [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] + [DetermineAmbiguousActionByPassingParameters] + public IEnumerable GetAllowedChildren(Guid contentId) + { + var entity = _entityService.Get(contentId); + if (entity != null) + { + return GetAllowedChildren(entity.Id); + } + + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + /// + /// Returns the allowed child content type objects for the content item id passed in - based on a UDI id + /// + /// + [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] + [DetermineAmbiguousActionByPassingParameters] + public IEnumerable GetAllowedChildren(Udi contentId) + { + var guidUdi = contentId as GuidUdi; + if (guidUdi != null) + { + var entity = _entityService.Get(guidUdi.Guid); + if (entity != null) + { + return GetAllowedChildren(entity.Id); + } + } + + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + #endregion + } +} diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs new file mode 100644 index 0000000000..3d8b23995a --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs @@ -0,0 +1,189 @@ +// 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; +// } +// +// +// } +// } diff --git a/src/Umbraco.Web.BackOffice/Filters/EnsureUserPermissionForMediaAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/EnsureUserPermissionForMediaAttribute.cs index b75b684f5c..526e95cf96 100644 --- a/src/Umbraco.Web.BackOffice/Filters/EnsureUserPermissionForMediaAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/EnsureUserPermissionForMediaAttribute.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc.Filters; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; +using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Editors; using Umbraco.Web.Security; diff --git a/src/Umbraco.Web.BackOffice/Filters/MediaItemSaveValidationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/MediaItemSaveValidationAttribute.cs new file mode 100644 index 0000000000..963ba2260a --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Filters/MediaItemSaveValidationAttribute.cs @@ -0,0 +1,114 @@ +using System; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Web.BackOffice.Controllers; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Security; + +namespace Umbraco.Web.BackOffice.Filters +{ + /// + /// Validates the incoming model + /// + internal class MediaItemSaveValidationAttribute : TypeFilterAttribute + { + public MediaItemSaveValidationAttribute() : base(typeof(MediaItemSaveValidationFilter)) + { + } + + private sealed class MediaItemSaveValidationFilter : IActionFilter + { + private readonly IEntityService _entityService; + + + private readonly ILogger _logger; + private readonly IMediaService _mediaService; + private readonly ILocalizedTextService _textService; + private readonly IWebSecurity _webSecurity; + + public MediaItemSaveValidationFilter(ILogger logger, IWebSecurity webSecurity, + ILocalizedTextService textService, IMediaService mediaService, IEntityService entityService) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _webSecurity = webSecurity ?? throw new ArgumentNullException(nameof(webSecurity)); + _textService = textService ?? throw new ArgumentNullException(nameof(textService)); + _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); + _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); + } + + public void OnActionExecuting(ActionExecutingContext context) + { + var model = (MediaItemSave) context.ActionArguments["contentItem"]; + var contentItemValidator = new MediaSaveModelValidator(_logger, _webSecurity, _textService); + + if (ValidateUserAccess(model, context)) + { + //now do each validation step + if (contentItemValidator.ValidateExistingContent(model, context)) + if (contentItemValidator.ValidateProperties(model, model, context)) + contentItemValidator.ValidatePropertiesData(model, model, model.PropertyCollectionDto, + context.ModelState); + } + } + + /// + /// Checks if the user has access to post a content item based on whether it's being created or saved. + /// + /// + /// + private bool ValidateUserAccess(MediaItemSave mediaItem, ActionExecutingContext actionContext) + { + //We now need to validate that the user is allowed to be doing what they are doing. + //Then if it is new, we need to lookup those permissions on the parent. + IMedia contentToCheck; + int contentIdToCheck; + switch (mediaItem.Action) + { + case ContentSaveAction.Save: + contentToCheck = mediaItem.PersistedContent; + contentIdToCheck = contentToCheck.Id; + break; + case ContentSaveAction.SaveNew: + contentToCheck = _mediaService.GetById(mediaItem.ParentId); + + if (mediaItem.ParentId != Constants.System.Root) + { + contentToCheck = _mediaService.GetById(mediaItem.ParentId); + contentIdToCheck = contentToCheck.Id; + } + else + { + contentIdToCheck = mediaItem.ParentId; + } + + break; + default: + //we don't support this for media + actionContext.Result = new NotFoundResult(); + return false; + } + + if (MediaController.CheckPermissions( + actionContext.HttpContext.Items, + _webSecurity.CurrentUser, + _mediaService, _entityService, + contentIdToCheck, contentToCheck) == false) + { + actionContext.Result = new ForbidResult(); + return false; + } + + return true; + } + + public void OnActionExecuted(ActionExecutedContext context) + { + + } + } + } +} diff --git a/src/Umbraco.Web/Editors/Filters/MediaSaveModelValidator.cs b/src/Umbraco.Web.BackOffice/Filters/MediaSaveModelValidator.cs similarity index 93% rename from src/Umbraco.Web/Editors/Filters/MediaSaveModelValidator.cs rename to src/Umbraco.Web.BackOffice/Filters/MediaSaveModelValidator.cs index 19a2c12cbb..c7610f9ea1 100644 --- a/src/Umbraco.Web/Editors/Filters/MediaSaveModelValidator.cs +++ b/src/Umbraco.Web.BackOffice/Filters/MediaSaveModelValidator.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Security; -namespace Umbraco.Web.Editors.Filters +namespace Umbraco.Web.BackOffice.Filters { /// /// Validator for diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs b/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs index 993566e5b1..0e9444ae3a 100644 --- a/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs +++ b/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Web.Models.ContentEditing; -namespace Umbraco.Web.Editors.Binders +namespace Umbraco.Web.BackOffice.ModelBinders { internal class BlueprintItemBinder : ContentItemBinder { diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs b/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs index 4152a7fd08..cb3dc73b85 100644 --- a/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs +++ b/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs @@ -1,25 +1,18 @@ using System; -using System.Drawing.Drawing2D; -using System.IO; using System.Linq; -using System.Net; using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Umbraco.Core; using Umbraco.Core.Hosting; using Umbraco.Core.Mapping; using Umbraco.Core.Models; -using Umbraco.Core.Models.Editors; using Umbraco.Core.Serialization; using Umbraco.Core.Services; -using Umbraco.Core.Services.Implement; -using Umbraco.Extensions; -using Umbraco.Web.Common.Exceptions; +using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.Mapping; -namespace Umbraco.Web.Editors.Binders +namespace Umbraco.Web.BackOffice.ModelBinders { /// /// The model binder for diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/ContentModelBinderHelper.cs b/src/Umbraco.Web.BackOffice/ModelBinders/ContentModelBinderHelper.cs index cfbf8e4027..adea4dcc3c 100644 --- a/src/Umbraco.Web.BackOffice/ModelBinders/ContentModelBinderHelper.cs +++ b/src/Umbraco.Web.BackOffice/ModelBinders/ContentModelBinderHelper.cs @@ -2,9 +2,7 @@ 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; @@ -13,7 +11,7 @@ using Umbraco.Extensions; using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Models.ContentEditing; -namespace Umbraco.Web.Editors.Binders +namespace Umbraco.Web.BackOffice.ModelBinders { /// /// Helper methods to bind media/member models diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/MediaItemBinder.cs b/src/Umbraco.Web.BackOffice/ModelBinders/MediaItemBinder.cs index 7f7831eab2..a8f105a24a 100644 --- a/src/Umbraco.Web.BackOffice/ModelBinders/MediaItemBinder.cs +++ b/src/Umbraco.Web.BackOffice/ModelBinders/MediaItemBinder.cs @@ -6,9 +6,10 @@ using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Serialization; using Umbraco.Core.Services; +using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.Models.ContentEditing; -namespace Umbraco.Web.Editors.Binders +namespace Umbraco.Web.BackOffice.ModelBinders { /// /// The model binder for diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/MemberBinder.cs b/src/Umbraco.Web.BackOffice/ModelBinders/MemberBinder.cs index 23a4d3849f..117cf7c089 100644 --- a/src/Umbraco.Web.BackOffice/ModelBinders/MemberBinder.cs +++ b/src/Umbraco.Web.BackOffice/ModelBinders/MemberBinder.cs @@ -11,8 +11,9 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Umbraco.Core.Hosting; using Umbraco.Core.Mapping; using Umbraco.Core.Serialization; +using Umbraco.Web.BackOffice.Controllers; -namespace Umbraco.Web.Editors.Binders +namespace Umbraco.Web.BackOffice.ModelBinders { /// /// The model binder for diff --git a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj index 8bb44b5f34..911a157223 100644 --- a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj +++ b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj @@ -12,6 +12,7 @@ + diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs index 9799c18651..0712dfeb74 100644 --- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs @@ -159,24 +159,16 @@ namespace Umbraco.Web.Editors "userGroupsApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( controller => controller.PostSaveUserGroup(null)) }, - { - "mediaApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( - controller => controller.GetRootMedia()) - }, - //TODO reintroduce + //TODO reintroduce // { // "imagesApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( // controller => controller.GetBigThumbnail("")) // }, - { - "contentTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( - controller => controller.GetAllowedChildren(0)) - }, - { - "mediaTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( - controller => controller.GetAllowedChildren(0)) - }, + // { + // "contentTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // controller => controller.GetAllowedChildren(0)) + // }, { "currentUserApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( @@ -199,10 +191,10 @@ namespace Umbraco.Web.Editors // "logApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( // controller => controller.GetPagedEntityLog(0, 0, 0, Direction.Ascending, null)) // }, - { - "memberApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( - controller => controller.GetByKey(Guid.Empty)) - }, + // { + // "memberApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // controller => controller.GetByKey(Guid.Empty)) + // }, // { // "packageInstallApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( // controller => controller.Fetch(string.Empty)) @@ -223,10 +215,10 @@ namespace Umbraco.Web.Editors // "stylesheetApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( // controller => controller.GetAll()) // }, - { - "memberTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( - controller => controller.GetAllTypes()) - }, + // { + // "memberTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // controller => controller.GetAllTypes()) + // }, { "memberGroupApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( controller => controller.GetAllGroups()) diff --git a/src/Umbraco.Web/Editors/Filters/MediaItemSaveValidationAttribute.cs b/src/Umbraco.Web/Editors/Filters/MediaItemSaveValidationAttribute.cs deleted file mode 100644 index 8e07bc6e7c..0000000000 --- a/src/Umbraco.Web/Editors/Filters/MediaItemSaveValidationAttribute.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Net; -using System.Net.Http; -using System.Web.Http.Controllers; -using System.Web.Http.Filters; -using Umbraco.Core; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Services; -using Umbraco.Web.Composing; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Security; -using Umbraco.Web.WebApi; - -namespace Umbraco.Web.Editors.Filters -{ - /// - /// Validates the incoming model - /// - internal class MediaItemSaveValidationAttribute : ActionFilterAttribute - { - private readonly ILogger _logger; - private readonly IWebSecurity _webSecurity; - private readonly ILocalizedTextService _textService; - private readonly IMediaService _mediaService; - private readonly IEntityService _entityService; - - public MediaItemSaveValidationAttribute() : this(Current.Logger, Current.UmbracoContextAccessor.UmbracoContext.Security, Current.Services.TextService, Current.Services.MediaService, Current.Services.EntityService) - { - } - - public MediaItemSaveValidationAttribute(ILogger logger, IWebSecurity webSecurity, ILocalizedTextService textService, IMediaService mediaService, IEntityService entityService) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _webSecurity = webSecurity ?? throw new ArgumentNullException(nameof(webSecurity)); - _textService = textService ?? throw new ArgumentNullException(nameof(textService)); - _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); - _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); - } - - public override void OnActionExecuting(HttpActionContext actionContext) - { - var model = (MediaItemSave)actionContext.ActionArguments["contentItem"]; - var contentItemValidator = new MediaSaveModelValidator(_logger, _webSecurity, _textService); - - if (ValidateUserAccess(model, actionContext)) - { - //now do each validation step - if (contentItemValidator.ValidateExistingContent(model, actionContext)) - if (contentItemValidator.ValidateProperties(model, model, actionContext)) - contentItemValidator.ValidatePropertiesData(model, model, model.PropertyCollectionDto, actionContext.ModelState); - } - } - - /// - /// Checks if the user has access to post a content item based on whether it's being created or saved. - /// - /// - /// - private bool ValidateUserAccess(MediaItemSave mediaItem, HttpActionContext actionContext) - { - //We now need to validate that the user is allowed to be doing what they are doing. - //Then if it is new, we need to lookup those permissions on the parent. - IMedia contentToCheck; - int contentIdToCheck; - switch (mediaItem.Action) - { - case ContentSaveAction.Save: - contentToCheck = mediaItem.PersistedContent; - contentIdToCheck = contentToCheck.Id; - break; - case ContentSaveAction.SaveNew: - contentToCheck = _mediaService.GetById(mediaItem.ParentId); - - if (mediaItem.ParentId != Constants.System.Root) - { - contentToCheck = _mediaService.GetById(mediaItem.ParentId); - contentIdToCheck = contentToCheck.Id; - } - else - { - contentIdToCheck = mediaItem.ParentId; - } - - break; - default: - //we don't support this for media - actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.NotFound); - return false; - } - - if (MediaController.CheckPermissions( - actionContext.Request.Properties, - _webSecurity.CurrentUser, - _mediaService, _entityService, - contentIdToCheck, contentToCheck) == false) - { - actionContext.Response = actionContext.Request.CreateUserNoAccessResponse(); - return false; - } - - return true; - } - } -} diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs deleted file mode 100644 index e6130ca1a9..0000000000 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ /dev/null @@ -1,348 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Models; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Mvc; -using Umbraco.Web.WebApi.Filters; -using System.Web.Http; -using System.Net; -using System.Net.Http; -using Umbraco.Web.WebApi; -using Umbraco.Core.Services; -using System; -using System.Web.Http.Controllers; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; -using Umbraco.Core.Dictionary; -using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; -using Umbraco.Core.Strings; -using Umbraco.Web.Composing; -using Constants = Umbraco.Core.Constants; -using IMediaType = Umbraco.Core.Models.IMediaType; -using Umbraco.Core.Mapping; -using Umbraco.Web.Routing; - -namespace Umbraco.Web.Editors -{ - // TODO: We'll need to be careful about the security on this controller, when we start implementing - // methods to modify content types we'll need to enforce security on the individual methods, we - // cannot put security on the whole controller because things like GetAllowedChildren are required for content editing. - - /// - /// An API controller used for dealing with content types - /// - [PluginController("UmbracoApi")] - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] - [EnableOverrideAuthorization] - [MediaTypeControllerControllerConfiguration] - public class MediaTypeController : ContentTypeControllerBase - { - private readonly IShortStringHelper _shortStringHelper; - private readonly IEntityService _entityService; - - public MediaTypeController( - ICultureDictionary cultureDictionary, - IGlobalSettings globalSettings, - IUmbracoContextAccessor umbracoContextAccessor, - ISqlContext sqlContext, - ServiceContext services, - AppCaches appCaches, - IProfilingLogger logger, - IRuntimeState runtimeState, - IShortStringHelper shortStringHelper, - UmbracoMapper umbracoMapper, - IEntityService entityService, - IPublishedUrlProvider publishedUrlProvider, - EditorValidatorCollection editorValidatorCollection) - : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider, editorValidatorCollection) - { - _shortStringHelper = shortStringHelper; - _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); - } - - /// - /// Configures this controller with a custom action selector - /// - private class MediaTypeControllerControllerConfigurationAttribute : Attribute, IControllerConfiguration - { - public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor) - { - controllerSettings.Services.Replace(typeof(IHttpActionSelector), new ParameterSwapControllerActionSelector( - new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetAllowedChildren", "contentId", typeof(int), typeof(Guid), typeof(Udi)))); - } - } - - public int GetCount() - { - return Services.ContentTypeService.Count(); - } - - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] - public MediaTypeDisplay GetById(int id) - { - var ct = Services.MediaTypeService.Get(id); - if (ct == null) - { - throw new HttpResponseException(HttpStatusCode.NotFound); - } - - var dto = Mapper.Map(ct); - return dto; - } - - /// - /// Deletes a media type with a given ID - /// - /// - /// - [HttpDelete] - [HttpPost] - public HttpResponseMessage DeleteById(int id) - { - var foundType = Services.MediaTypeService.Get(id); - if (foundType == null) - { - throw new HttpResponseException(HttpStatusCode.NotFound); - } - - Services.MediaTypeService.Delete(foundType, Security.CurrentUser.Id); - return Request.CreateResponse(HttpStatusCode.OK); - } - - /// - /// Returns the available compositions for this content type - /// This has been wrapped in a dto instead of simple parameters to support having multiple parameters in post request body - /// - /// - /// - /// 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. - /// - /// - /// Filter applied when resolving compositions - /// - [HttpPost] - public HttpResponseMessage GetAvailableCompositeMediaTypes(GetAvailableCompositionsFilter filter) - { - var result = PerformGetAvailableCompositeContentTypes(filter.ContentTypeId, UmbracoObjectTypes.MediaType, filter.FilterContentTypes, filter.FilterPropertyTypes, filter.IsElement) - .Select(x => new - { - contentType = x.Item1, - allowed = x.Item2 - }); - return Request.CreateResponse(result); - } - /// - /// Returns where a particular composition has been used - /// This has been wrapped in a dto instead of simple parameters to support having multiple parameters in post request body - /// - /// - /// - [HttpPost] - public HttpResponseMessage GetWhereCompositionIsUsedInContentTypes(GetAvailableCompositionsFilter filter) - { - var result = PerformGetWhereCompositionIsUsedInContentTypes(filter.ContentTypeId, UmbracoObjectTypes.MediaType) - .Select(x => new - { - contentType = x - }); - return Request.CreateResponse(result); - } - public MediaTypeDisplay GetEmpty(int parentId) - { - IMediaType mt; - if (parentId != Constants.System.Root) - { - var parent = Services.MediaTypeService.Get(parentId); - mt = parent != null ? new MediaType(_shortStringHelper, parent, string.Empty) : new MediaType(_shortStringHelper, parentId); - } - else - mt = new MediaType(_shortStringHelper, parentId); - - mt.Icon = Constants.Icons.MediaImage; - - var dto = Mapper.Map(mt); - return dto; - } - - - /// - /// Returns all media types - /// - public IEnumerable GetAll() - { - return Services.MediaTypeService.GetAll() - .Select(Mapper.Map); - } - - /// - /// Deletes a media type container with a given ID - /// - /// - /// - [HttpDelete] - [HttpPost] - public HttpResponseMessage DeleteContainer(int id) - { - Services.MediaTypeService.DeleteContainer(id, Security.CurrentUser.Id); - - return Request.CreateResponse(HttpStatusCode.OK); - } - - public HttpResponseMessage PostCreateContainer(int parentId, string name) - { - var result = Services.MediaTypeService.CreateContainer(parentId, name, Security.CurrentUser.Id); - - return result - ? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id - : Request.CreateNotificationValidationErrorResponse(result.Exception.Message); - } - - public HttpResponseMessage PostRenameContainer(int id, string name) - { - - var result = Services.MediaTypeService.RenameContainer(id, name, Security.CurrentUser.Id); - - return result - ? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id - : Request.CreateNotificationValidationErrorResponse(result.Exception.Message); - } - - public MediaTypeDisplay PostSave(MediaTypeSave contentTypeSave) - { - var savedCt = PerformPostSave( - contentTypeSave: contentTypeSave, - getContentType: i => Services.MediaTypeService.Get(i), - saveContentType: type => Services.MediaTypeService.Save(type)); - - var display = Mapper.Map(savedCt); - - display.AddSuccessNotification( - Services.TextService.Localize("speechBubbles/mediaTypeSavedHeader"), - string.Empty); - - return display; - } - - - #region GetAllowedChildren - /// - /// Returns the allowed child content type objects for the content item id passed in - based on an INT id - /// - /// - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] - public IEnumerable GetAllowedChildren(int contentId) - { - if (contentId == Constants.System.RecycleBinContent) - return Enumerable.Empty(); - - IEnumerable types; - if (contentId == Constants.System.Root) - { - types = Services.MediaTypeService.GetAll().ToList(); - - //if no allowed root types are set, just return everything - if (types.Any(x => x.AllowedAsRoot)) - types = types.Where(x => x.AllowedAsRoot); - } - else - { - var contentItem = Services.MediaService.GetById(contentId); - if (contentItem == null) - { - return Enumerable.Empty(); - } - - var contentType = Services.MediaTypeService.Get(contentItem.ContentTypeId); - var ids = contentType.AllowedContentTypes.OrderBy(c => c.SortOrder).Select(x => x.Id.Value).ToArray(); - - if (ids.Any() == false) return Enumerable.Empty(); - - types = Services.MediaTypeService.GetAll(ids).OrderBy(c => ids.IndexOf(c.Id)).ToList(); - } - - var basics = types.Select(Mapper.Map).ToList(); - - foreach (var basic in basics) - { - basic.Name = TranslateItem(basic.Name); - basic.Description = TranslateItem(basic.Description); - } - - return basics.OrderBy(c => contentId == Constants.System.Root ? c.Name : string.Empty); - } - - /// - /// Returns the allowed child content type objects for the content item id passed in - based on a GUID id - /// - /// - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] - public IEnumerable GetAllowedChildren(Guid contentId) - { - var entity = _entityService.Get(contentId); - if (entity != null) - { - return GetAllowedChildren(entity.Id); - } - - throw new HttpResponseException(HttpStatusCode.NotFound); - } - - /// - /// Returns the allowed child content type objects for the content item id passed in - based on a UDI id - /// - /// - [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] - public IEnumerable GetAllowedChildren(Udi contentId) - { - var guidUdi = contentId as GuidUdi; - if (guidUdi != null) - { - var entity = _entityService.Get(guidUdi.Guid); - if (entity != null) - { - return GetAllowedChildren(entity.Id); - } - } - - throw new HttpResponseException(HttpStatusCode.NotFound); - } - - #endregion - - /// - /// Move the media type - /// - /// - /// - public HttpResponseMessage PostMove(MoveOrCopy move) - { - return PerformMove( - move, - getContentType: i => Services.MediaTypeService.Get(i), - doMove: (type, i) => Services.MediaTypeService.Move(type, i)); - } - - /// - /// Copy the media type - /// - /// - /// - public HttpResponseMessage PostCopy(MoveOrCopy copy) - { - return PerformCopy( - copy, - getContentType: i => Services.MediaTypeService.Get(i), - doCopy: (type, i) => Services.MediaTypeService.Copy(type, i)); - } - - - } -} diff --git a/src/Umbraco.Web/Editors/MemberTypeController.cs b/src/Umbraco.Web/Editors/MemberTypeController.cs deleted file mode 100644 index 50d31de5a8..0000000000 --- a/src/Umbraco.Web/Editors/MemberTypeController.cs +++ /dev/null @@ -1,189 +0,0 @@ -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; - } - - - } -} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a9a1838b3b..5e6098a078 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -163,7 +163,6 @@ - @@ -259,7 +258,6 @@ - @@ -293,7 +291,6 @@ - @@ -306,7 +303,6 @@ - @@ -330,7 +326,6 @@ - @@ -338,7 +333,6 @@ -