diff --git a/src/Umbraco.Core/Models/TemplateQuery/OperatorFactory.cs b/src/Umbraco.Core/Models/TemplateQuery/OperatorFactory.cs index 0bae72186a..ba9c318615 100644 --- a/src/Umbraco.Core/Models/TemplateQuery/OperatorFactory.cs +++ b/src/Umbraco.Core/Models/TemplateQuery/OperatorFactory.cs @@ -2,7 +2,7 @@ using System; namespace Umbraco.Web.Models.TemplateQuery { - internal static class OperatorFactory + public static class OperatorFactory { public static Operator FromString(string stringOperator) { diff --git a/src/Umbraco.Core/Models/TemplateQuery/QueryConditionExtensions.cs b/src/Umbraco.Core/Models/TemplateQuery/QueryConditionExtensions.cs index f01d247b1f..089c0e13ba 100644 --- a/src/Umbraco.Core/Models/TemplateQuery/QueryConditionExtensions.cs +++ b/src/Umbraco.Core/Models/TemplateQuery/QueryConditionExtensions.cs @@ -4,7 +4,7 @@ using System.Reflection; namespace Umbraco.Web.Models.TemplateQuery { - internal static class QueryConditionExtensions + public static class QueryConditionExtensions { private static Lazy StringContainsMethodInfo => new Lazy(() => typeof(string).GetMethod("Contains", new[] {typeof(string)})); @@ -71,4 +71,4 @@ namespace Umbraco.Web.Models.TemplateQuery return predicate; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Services/ContentServiceExtensions.cs b/src/Umbraco.Core/Services/ContentServiceExtensions.cs index dfe02ba690..3e4fe272f9 100644 --- a/src/Umbraco.Core/Services/ContentServiceExtensions.cs +++ b/src/Umbraco.Core/Services/ContentServiceExtensions.cs @@ -16,7 +16,7 @@ namespace Umbraco.Core.Services private static readonly Regex AnchorRegex = new Regex("", RegexOptions.Compiled); - internal static IEnumerable GetAnchorValuesFromRTEs(this IContentService contentService, int id, string culture = "*") + public static IEnumerable GetAnchorValuesFromRTEs(this IContentService contentService, int id, string culture = "*") { var result = new List(); var content = contentService.GetById(id); @@ -36,7 +36,7 @@ namespace Umbraco.Core.Services } - internal static IEnumerable GetAnchorValuesFromRTEContent(this IContentService contentService, string rteContent) + public static IEnumerable GetAnchorValuesFromRTEContent(this IContentService contentService, string rteContent) { var result = new List(); var matches = AnchorRegex.Matches(rteContent); diff --git a/src/Umbraco.Infrastructure/Models/ContentTypeImportModel.cs b/src/Umbraco.Infrastructure/Models/ContentTypeImportModel.cs index 3ccbe7329d..edcb4e568f 100644 --- a/src/Umbraco.Infrastructure/Models/ContentTypeImportModel.cs +++ b/src/Umbraco.Infrastructure/Models/ContentTypeImportModel.cs @@ -6,7 +6,7 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models { [DataContract(Name = "contentTypeImportModel")] - public class ContentTypeImportModel : INotificationModel, IHaveUploadedFiles + public class ContentTypeImportModel : INotificationModel { [DataMember(Name = "alias")] public string Alias { get; set; } @@ -19,7 +19,5 @@ namespace Umbraco.Web.Models [DataMember(Name = "tempFileName")] public string TempFileName { get; set; } - - public List UploadedFiles { get; } = new List(); } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs index f2190eaf38..3b72ca4d2d 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs @@ -216,10 +216,10 @@ namespace Umbraco.Web.BackOffice.Controllers // "currentUserApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( // controller => controller.PostChangePassword(null)) // }, - // { - // "entityApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( - // controller => controller.GetById(0, UmbracoEntityTypes.Media)) - // }, + { + "entityApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( + controller => controller.GetById(0, UmbracoEntityTypes.Media)) + }, { "dataTypeApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( controller => controller.GetById(0)) diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs index 9ee5fa92e0..936ce02ee5 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs @@ -600,10 +600,8 @@ namespace Umbraco.Web.BackOffice.Controllers } [HttpPost] - [FileUploadCleanupFilter(false)] public async Task> Upload(List file) { - var model = new ContentTypeImportModel(); foreach (var formFile in file) @@ -623,11 +621,6 @@ namespace Umbraco.Web.BackOffice.Controllers { model.TempFileName = Path.Combine(root, fileName); - model.UploadedFiles.Add(new ContentPropertyFile - { - TempFilePath = model.TempFileName - }); - var xd = new XmlDocument { XmlResolver = null diff --git a/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs b/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs index 4197a4726e..b206cbde01 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs @@ -1,14 +1,24 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Linq; +using System.Net.Http; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ActionConstraints; using Microsoft.AspNetCore.Routing; using Umbraco.Core; +using Umbraco.Extensions; +using Umbraco.Web.BackOffice.ModelBinders; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace Umbraco.Web.BackOffice.Controllers { public class DetermineAmbiguousActionByPassingParameters : ActionMethodSelectorAttribute { + private string _requestBody = null; + public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action) { var parameters = action.Parameters; @@ -17,27 +27,37 @@ namespace Umbraco.Web.BackOffice.Controllers var canUse = true; foreach (var parameterDescriptor in parameters) { - var value = routeContext.HttpContext.Request.Query[parameterDescriptor.Name]; + var values = GetValue(parameterDescriptor, routeContext); - if (parameterDescriptor.ParameterType == typeof(Udi)) + var type = parameterDescriptor.ParameterType; + + if(typeof(IEnumerable).IsAssignableFrom(type)) { - canUse &= UdiParser.TryParse(value, out _); + type = type.GetElementType(); } - else if (parameterDescriptor.ParameterType == typeof(int)) + + foreach (var value in values) { - canUse &= int.TryParse(value, out _); - } - else if (parameterDescriptor.ParameterType == typeof(Guid)) - { - canUse &= Guid.TryParse(value, out _); - } - else if (parameterDescriptor.ParameterType == typeof(string)) - { - canUse &= true; - } - else - { - canUse &= true; + if (type == typeof(Udi)) + { + canUse &= UdiParser.TryParse(value.ToString(), out _); + } + else if (type == typeof(int)) + { + canUse &= int.TryParse(value.ToString(), out _); + } + else if (type == typeof(Guid)) + { + canUse &= Guid.TryParse(value.ToString(), out _); + } + else if (type == typeof(string)) + { + canUse &= true; + } + else + { + canUse &= true; + } } } @@ -47,5 +67,36 @@ namespace Umbraco.Web.BackOffice.Controllers return true; } + + private IEnumerable GetValue(ParameterDescriptor descriptor, RouteContext routeContext) + { + if (routeContext.HttpContext.Request.Query.ContainsKey(descriptor.Name)) + { + return routeContext.HttpContext.Request.Query[descriptor.Name]; + } + + if (descriptor.BindingInfo.BinderType == typeof(FromJsonPathAttribute.JsonPathBinder)) + { + // IMPORTANT: Ensure the requestBody can be read multiple times. + routeContext.HttpContext.Request.EnableBuffering(); + + var body = _requestBody ??= routeContext.HttpContext.Request.GetRawBodyString(); + + var jToken = JsonConvert.DeserializeObject(body); + + return jToken[descriptor.Name].Values(); + } + + if (routeContext.HttpContext.Request.Method.InvariantEquals(HttpMethod.Post.ToString()) && routeContext.HttpContext.Request.Form.ContainsKey(descriptor.Name)) + { + return routeContext.HttpContext.Request.Form[descriptor.Name]; + } + + return null; + + } } + } + + diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs similarity index 81% rename from src/Umbraco.Web/Editors/EntityController.cs rename to src/Umbraco.Web.BackOffice/Controllers/EntityController.cs index 483834868f..05c8b3a213 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs @@ -1,39 +1,37 @@ using System; using System.Collections.Generic; using System.Net; -using System.Web.Http; using Umbraco.Core; using Umbraco.Core.Models.Membership; using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Mvc; using System.Linq; using System.Net.Http; -using System.Net.Http.Formatting; using System.Reflection; +using Microsoft.AspNetCore.Http; using Umbraco.Core.Models; -using System.Web.Http.Controllers; -using System.Web.Http.ModelBinding; -using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; -using Umbraco.Core.Logging; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Core.Mapping; using Umbraco.Core.Models.Entities; using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Core.Xml; +using Umbraco.Extensions; +using Umbraco.Web.BackOffice.ModelBinders; using Umbraco.Web.Models; -using Umbraco.Web.Models.Mapping; using Umbraco.Web.Models.TemplateQuery; using Umbraco.Web.Search; using Umbraco.Web.Services; using Umbraco.Web.Trees; -using Umbraco.Web.WebApi; -using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; -using Umbraco.Core.Mapping; +using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Exceptions; +using Umbraco.Web.Common.ModelBinders; +using Umbraco.Web.Models.Mapping; using Umbraco.Web.Routing; +using Umbraco.Web.Security; -namespace Umbraco.Web.Editors +namespace Umbraco.Web.BackOffice.Controllers { /// /// The API controller used for getting entity objects, basic name, icon, id representation of umbraco objects that are based on CMSNode @@ -46,7 +44,6 @@ namespace Umbraco.Web.Editors /// /// Some objects such as macros are not based on CMSNode /// - [EntityControllerConfiguration] [PluginController("UmbracoApi")] public class EntityController : UmbracoAuthorizedJsonController { @@ -54,47 +51,66 @@ namespace Umbraco.Web.Editors private readonly UmbracoTreeSearcher _treeSearcher; private readonly SearchableTreeCollection _searchableTreeCollection; private readonly IPublishedContentQuery _publishedContentQuery; + private readonly IShortStringHelper _shortStringHelper; + private readonly IEntityService _entityService; + private readonly IWebSecurity _webSecurity; + private readonly IPublishedUrlProvider _publishedUrlProvider; + private readonly IContentService _contentService; + private readonly UmbracoMapper _umbracoMapper; + private readonly IDataTypeService _dataTypeService; + private readonly ISqlContext _sqlContext; + private readonly ILocalizedTextService _localizedTextService; + private readonly IFileService _fileService; + private readonly IContentTypeService _contentTypeService; + private readonly IMediaTypeService _mediaTypeService; + private readonly IMacroService _macroService; + private readonly IUserService _userService; + private readonly ILocalizationService _localizationService; public EntityController( - IGlobalSettings globalSettings, - IUmbracoContextAccessor umbracoContextAccessor, - ISqlContext sqlContext, - ServiceContext services, - AppCaches appCaches, - IProfilingLogger logger, - IRuntimeState runtimeState, ITreeService treeService, - SearchableTreeCollection searchableTreeCollection, UmbracoTreeSearcher treeSearcher, + SearchableTreeCollection searchableTreeCollection, + IPublishedContentQuery publishedContentQuery, IShortStringHelper shortStringHelper, - UmbracoMapper umbracoMapper, + IEntityService entityService, + IWebSecurity webSecurity, IPublishedUrlProvider publishedUrlProvider, - IPublishedContentQuery publishedContentQuery) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) + IContentService contentService, + UmbracoMapper umbracoMapper, + IDataTypeService dataTypeService, + ISqlContext sqlContext, + ILocalizedTextService localizedTextService, + IFileService fileService, + IContentTypeService contentTypeService, + IMediaTypeService mediaTypeService, + IMacroService macroService, + IUserService userService, + ILocalizationService localizationService) { - _treeService = treeService; - _searchableTreeCollection = searchableTreeCollection; - _treeSearcher = treeSearcher; - _publishedContentQuery = publishedContentQuery; - } - - /// - /// Configures this controller with a custom action selector - /// - private class EntityControllerConfigurationAttribute : Attribute, IControllerConfiguration - { - public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor) - { - controllerSettings.Services.Replace(typeof(IHttpActionSelector), new ParameterSwapControllerActionSelector( - - //This is a special case, we'll accept a String here so that we can get page members when the special "all-members" - //id is passed in eventually we'll probably want to support GUID + Udi too - new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetPagedChildren", "id", typeof(int), typeof(string)), - new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetPath", "id", typeof(int), typeof(Guid), typeof(Udi)), - new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetUrlAndAnchors", "id", typeof(int), typeof(Guid), typeof(Udi)), - new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetById", "id", typeof(int), typeof(Guid), typeof(Udi)), - new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetByIds", "ids", typeof(int[]), typeof(Guid[]), typeof(Udi[])))); - } + _treeService = treeService ?? throw new ArgumentNullException(nameof(treeService)); + _treeSearcher = treeSearcher ?? throw new ArgumentNullException(nameof(treeSearcher)); + _searchableTreeCollection = searchableTreeCollection ?? + throw new ArgumentNullException(nameof(searchableTreeCollection)); + _publishedContentQuery = + publishedContentQuery ?? throw new ArgumentNullException(nameof(publishedContentQuery)); + _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); + _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); + _webSecurity = webSecurity ?? throw new ArgumentNullException(nameof(webSecurity)); + _publishedUrlProvider = + publishedUrlProvider ?? throw new ArgumentNullException(nameof(publishedUrlProvider)); + _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); + _umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper)); + _dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService)); + _sqlContext = sqlContext ?? throw new ArgumentNullException(nameof(sqlContext)); + _localizedTextService = + localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService)); + _fileService = fileService ?? throw new ArgumentNullException(nameof(fileService)); + _contentTypeService = contentTypeService ?? throw new ArgumentNullException(nameof(contentTypeService)); + _mediaTypeService = mediaTypeService ?? throw new ArgumentNullException(nameof(mediaTypeService)); + _macroService = macroService ?? throw new ArgumentNullException(nameof(macroService)); + _userService = userService ?? throw new ArgumentNullException(nameof(userService)); + _localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService)); } /// @@ -105,7 +121,7 @@ namespace Umbraco.Web.Editors /// public dynamic GetSafeAlias(string value, bool camelCase = true) { - var returnValue = string.IsNullOrWhiteSpace(value) ? string.Empty : value.ToSafeAlias(ShortStringHelper, camelCase); + var returnValue = string.IsNullOrWhiteSpace(value) ? string.Empty : value.ToSafeAlias(_shortStringHelper, camelCase); dynamic returnObj = new System.Dynamic.ExpandoObject(); returnObj.alias = returnValue; returnObj.original = value; @@ -134,7 +150,7 @@ namespace Umbraco.Web.Editors //TODO: This uses the internal UmbracoTreeSearcher, this instead should delgate to the ISearchableTree implementation for the type - var ignoreUserStartNodes = dataTypeKey.HasValue && Services.DataTypeService.IsDataTypeIgnoringUserStartNodes(dataTypeKey.Value); + var ignoreUserStartNodes = dataTypeKey.HasValue && _dataTypeService.IsDataTypeIgnoringUserStartNodes(dataTypeKey.Value); return ExamineSearch(query, type, searchFrom, ignoreUserStartNodes); } @@ -159,7 +175,7 @@ namespace Umbraco.Web.Editors if (string.IsNullOrEmpty(query)) return result; - var allowedSections = Security.CurrentUser.AllowedSections.ToArray(); + var allowedSections = _webSecurity.CurrentUser.AllowedSections.ToArray(); foreach (var searchableTree in _searchableTreeCollection.SearchableApplicationTrees.OrderBy(t => t.Value.SortOrder)) { @@ -168,7 +184,7 @@ namespace Umbraco.Web.Editors var tree = _treeService.GetByAlias(searchableTree.Key); if (tree == null) continue; //shouldn't occur - result[Tree.GetRootNodeDisplayName(tree, Services.TextService)] = new TreeSearchResult + result[Tree.GetRootNodeDisplayName(tree, _localizedTextService)] = new TreeSearchResult { Results = searchableTree.Value.SearchableTree.Search(query, 200, 0, out var total), TreeAlias = searchableTree.Key, @@ -187,6 +203,7 @@ namespace Umbraco.Web.Editors /// /// /// + [DetermineAmbiguousActionByPassingParameters] public IEnumerable GetPath(int id, UmbracoEntityTypes type) { var foundContent = GetResultForId(id, type); @@ -200,6 +217,7 @@ namespace Umbraco.Web.Editors /// /// /// + [DetermineAmbiguousActionByPassingParameters] public IEnumerable GetPath(Guid id, UmbracoEntityTypes type) { var foundContent = GetResultForKey(id, type); @@ -213,6 +231,7 @@ namespace Umbraco.Web.Editors /// /// /// + [DetermineAmbiguousActionByPassingParameters] public IEnumerable GetPath(Udi id, UmbracoEntityTypes type) { var guidUdi = id as GuidUdi; @@ -231,7 +250,7 @@ namespace Umbraco.Web.Editors /// The URL or path to the item public HttpResponseMessage GetUrl(Udi udi, string culture = "*") { - var intId = Services.EntityService.GetId(udi); + var intId = _entityService.GetId(udi); if (!intId.Success) throw new HttpResponseException(HttpStatusCode.NotFound); UmbracoEntityTypes entityType; @@ -270,7 +289,7 @@ namespace Umbraco.Web.Editors if (type == UmbracoEntityTypes.Document) { - var foundUrl = PublishedUrlProvider.GetUrl(id, culture: culture); + var foundUrl = _publishedUrlProvider.GetUrl(id, culture: culture); if (string.IsNullOrEmpty(foundUrl) == false && foundUrl != "#") { returnUrl = foundUrl; @@ -332,28 +351,30 @@ namespace Umbraco.Web.Editors nodeContextId: id, getPath: nodeid => { - var ent = Services.EntityService.Get(nodeid); + var ent = _entityService.Get(nodeid); return ent.Path.Split(',').Reverse(); }, publishedContentExists: i => _publishedContentQuery.Content(i) != null); } [HttpGet] + [DetermineAmbiguousActionByPassingParameters] public UrlAndAnchors GetUrlAndAnchors(Udi id, string culture = "*") { - var intId = Services.EntityService.GetId(id); + var intId = _entityService.GetId(id); if (!intId.Success) throw new HttpResponseException(HttpStatusCode.NotFound); return GetUrlAndAnchors(intId.Result, culture); } [HttpGet] + [DetermineAmbiguousActionByPassingParameters] public UrlAndAnchors GetUrlAndAnchors(int id, string culture = "*") { culture = culture ?? ClientCulture(); - var url = PublishedUrlProvider.GetUrl(id, culture: culture); - var anchorValues = Services.ContentService.GetAnchorValuesFromRTEs(id, culture); + var url = _publishedUrlProvider.GetUrl(id, culture: culture); + var anchorValues = _contentService.GetAnchorValuesFromRTEs(id, culture); return new UrlAndAnchors(url, anchorValues); } @@ -361,7 +382,7 @@ namespace Umbraco.Web.Editors [HttpPost] public IEnumerable GetAnchors(AnchorsModel model) { - var anchorValues = Services.ContentService.GetAnchorValuesFromRTEContent(model.RteContent); + var anchorValues = _contentService.GetAnchorValuesFromRTEContent(model.RteContent); return anchorValues; } @@ -374,6 +395,7 @@ namespace Umbraco.Web.Editors /// /// /// + [DetermineAmbiguousActionByPassingParameters] public EntityBasic GetById(int id, UmbracoEntityTypes type) { return GetResultForId(id, type); @@ -385,6 +407,7 @@ namespace Umbraco.Web.Editors /// /// /// + [DetermineAmbiguousActionByPassingParameters] public EntityBasic GetById(Guid id, UmbracoEntityTypes type) { return GetResultForKey(id, type); @@ -396,6 +419,7 @@ namespace Umbraco.Web.Editors /// /// /// + [DetermineAmbiguousActionByPassingParameters] public EntityBasic GetById(Udi id, UmbracoEntityTypes type) { var guidUdi = id as GuidUdi; @@ -419,6 +443,7 @@ namespace Umbraco.Web.Editors /// [HttpGet] [HttpPost] + [DetermineAmbiguousActionByPassingParameters] public IEnumerable GetByIds([FromJsonPath]int[] ids, UmbracoEntityTypes type) { if (ids == null) @@ -439,6 +464,7 @@ namespace Umbraco.Web.Editors /// [HttpGet] [HttpPost] + [DetermineAmbiguousActionByPassingParameters] public IEnumerable GetByIds([FromJsonPath]Guid[] ids, UmbracoEntityTypes type) { if (ids == null) @@ -461,7 +487,8 @@ namespace Umbraco.Web.Editors /// [HttpGet] [HttpPost] - public IEnumerable GetByIds([FromJsonPath]Udi[] ids, [FromUri]UmbracoEntityTypes type) + [DetermineAmbiguousActionByPassingParameters] + public IEnumerable GetByIds([FromJsonPath]Udi[] ids, [FromQuery]UmbracoEntityTypes type) { if (ids == null) { @@ -500,18 +527,18 @@ namespace Umbraco.Web.Editors // root is special: we reduce it to start nodes if the user's start node is not the default, then we need to return their start nodes if (id == Constants.System.Root && startNodes.Length > 0 && startNodes.Contains(Constants.System.Root) == false && !ignoreUserStartNodes) { - var nodes = Services.EntityService.GetAll(objectType.Value, startNodes).ToArray(); + var nodes = _entityService.GetAll(objectType.Value, startNodes).ToArray(); if (nodes.Length == 0) return Enumerable.Empty(); - var pr = new List(nodes.Select(Mapper.Map)); + var pr = new List(nodes.Select(_umbracoMapper.Map)); return pr; } // else proceed as usual - return Services.EntityService.GetChildren(id, objectType.Value) + return _entityService.GetChildren(id, objectType.Value) .WhereNotNull() - .Select(Mapper.Map); + .Select(_umbracoMapper.Map); } //now we need to convert the unknown ones switch (type) @@ -537,6 +564,7 @@ namespace Umbraco.Web.Editors /// /// /// + [DetermineAmbiguousActionByPassingParameters] public PagedResult GetPagedChildren( string id, UmbracoEntityTypes type, @@ -598,6 +626,7 @@ namespace Umbraco.Web.Editors /// /// /// + [DetermineAmbiguousActionByPassingParameters] public PagedResult GetPagedChildren( int id, UmbracoEntityTypes type, @@ -628,22 +657,22 @@ namespace Umbraco.Web.Editors { if (pageNumber > 0) return new PagedResult(0, 0, 0); - var nodes = Services.EntityService.GetAll(objectType.Value, startNodes).ToArray(); + var nodes = _entityService.GetAll(objectType.Value, startNodes).ToArray(); if (nodes.Length == 0) return new PagedResult(0, 0, 0); if (pageSize < nodes.Length) pageSize = nodes.Length; // bah var pr = new PagedResult(nodes.Length, pageNumber, pageSize) { - Items = nodes.Select(Mapper.Map) + Items = nodes.Select(_umbracoMapper.Map) }; return pr; } // else proceed as usual - entities = Services.EntityService.GetPagedChildren(id, objectType.Value, pageNumber - 1, pageSize, out totalRecords, + entities = _entityService.GetPagedChildren(id, objectType.Value, pageNumber - 1, pageSize, out totalRecords, filter.IsNullOrWhiteSpace() ? null - : SqlContext.Query().Where(x => x.Name.Contains(filter)), + : _sqlContext.Query().Where(x => x.Name.Contains(filter)), Ordering.By(orderBy, orderDirection)); @@ -657,9 +686,10 @@ namespace Umbraco.Web.Editors { Items = entities.Select(source => { - var target = Mapper.Map(source, context => + var target = _umbracoMapper.Map(source, context => { context.SetCulture(culture); + context.SetCulture(culture); }); //TODO: Why is this here and not in the mapping? target.AdditionalData["hasChildren"] = source.HasChildren; @@ -689,9 +719,9 @@ namespace Umbraco.Web.Editors switch (type) { case UmbracoEntityTypes.Document: - return Security.CurrentUser.CalculateContentStartNodeIds(Services.EntityService); + return _webSecurity.CurrentUser.CalculateContentStartNodeIds(_entityService); case UmbracoEntityTypes.Media: - return Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService); + return _webSecurity.CurrentUser.CalculateMediaStartNodeIds(_entityService); default: return Array.Empty(); } @@ -729,17 +759,17 @@ namespace Umbraco.Web.Editors var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes(dataTypeKey); entities = aids == null || aids.Contains(Constants.System.Root) || ignoreUserStartNodes - ? Services.EntityService.GetPagedDescendants(objectType.Value, pageNumber - 1, pageSize, out totalRecords, - SqlContext.Query().Where(x => x.Name.Contains(filter)), + ? _entityService.GetPagedDescendants(objectType.Value, pageNumber - 1, pageSize, out totalRecords, + _sqlContext.Query().Where(x => x.Name.Contains(filter)), Ordering.By(orderBy, orderDirection), includeTrashed: false) - : Services.EntityService.GetPagedDescendants(aids, objectType.Value, pageNumber - 1, pageSize, out totalRecords, - SqlContext.Query().Where(x => x.Name.Contains(filter)), + : _entityService.GetPagedDescendants(aids, objectType.Value, pageNumber - 1, pageSize, out totalRecords, + _sqlContext.Query().Where(x => x.Name.Contains(filter)), Ordering.By(orderBy, orderDirection)); } else { - entities = Services.EntityService.GetPagedDescendants(id, objectType.Value, pageNumber - 1, pageSize, out totalRecords, - SqlContext.Query().Where(x => x.Name.Contains(filter)), + entities = _entityService.GetPagedDescendants(id, objectType.Value, pageNumber - 1, pageSize, out totalRecords, + _sqlContext.Query().Where(x => x.Name.Contains(filter)), Ordering.By(orderBy, orderDirection)); } @@ -770,9 +800,9 @@ namespace Umbraco.Web.Editors } } - private bool IsDataTypeIgnoringUserStartNodes(Guid? dataTypeKey) => dataTypeKey.HasValue && Services.DataTypeService.IsDataTypeIgnoringUserStartNodes(dataTypeKey.Value); + private bool IsDataTypeIgnoringUserStartNodes(Guid? dataTypeKey) => dataTypeKey.HasValue && _dataTypeService.IsDataTypeIgnoringUserStartNodes(dataTypeKey.Value); - public IEnumerable GetAncestors(int id, UmbracoEntityTypes type, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings) + public IEnumerable GetAncestors(int id, UmbracoEntityTypes type, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection queryStrings) { return GetResultForAncestors(id, type, queryStrings); } @@ -798,7 +828,7 @@ namespace Umbraco.Web.Editors { // TODO: Need to check for Object types that support hierarchic here, some might not. - return Services.EntityService.GetChildren(id, objectType.Value) + return _entityService.GetChildren(id, objectType.Value) .WhereNotNull() .Select(MapEntities()); } @@ -814,14 +844,14 @@ namespace Umbraco.Web.Editors } } - private IEnumerable GetResultForAncestors(int id, UmbracoEntityTypes entityType, FormDataCollection queryStrings = null) + private IEnumerable GetResultForAncestors(int id, UmbracoEntityTypes entityType, FormCollection queryStrings = null) { var objectType = ConvertToObjectType(entityType); if (objectType.HasValue) { // TODO: Need to check for Object types that support hierarchic here, some might not. - var ids = Services.EntityService.Get(id).Path.Split(',').Select(int.Parse).Distinct().ToArray(); + var ids = _entityService.Get(id).Path.Split(',').Select(int.Parse).Distinct().ToArray(); var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes(queryStrings?.GetValue("dataTypeId")); if (ignoreUserStartNodes == false) @@ -830,10 +860,10 @@ namespace Umbraco.Web.Editors switch (entityType) { case UmbracoEntityTypes.Document: - aids = Security.CurrentUser.CalculateContentStartNodeIds(Services.EntityService); + aids = _webSecurity.CurrentUser.CalculateContentStartNodeIds(_entityService); break; case UmbracoEntityTypes.Media: - aids = Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService); + aids = _webSecurity.CurrentUser.CalculateMediaStartNodeIds(_entityService); break; } @@ -862,7 +892,7 @@ namespace Umbraco.Web.Editors return ids.Length == 0 ? Enumerable.Empty() - : Services.EntityService.GetAll(objectType.Value, ids) + : _entityService.GetAll(objectType.Value, ids) .WhereNotNull() .OrderBy(x => x.Level) .Select(MapEntities(culture)); @@ -889,7 +919,7 @@ namespace Umbraco.Web.Editors var objectType = ConvertToObjectType(entityType); if (objectType.HasValue) { - var entities = Services.EntityService.GetAll(objectType.Value, keys) + var entities = _entityService.GetAll(objectType.Value, keys) .WhereNotNull() .Select(MapEntities()); @@ -921,7 +951,7 @@ namespace Umbraco.Web.Editors var objectType = ConvertToObjectType(entityType); if (objectType.HasValue) { - var entities = Services.EntityService.GetAll(objectType.Value, ids) + var entities = _entityService.GetAll(objectType.Value, ids) .WhereNotNull() .Select(MapEntities()); @@ -950,12 +980,12 @@ namespace Umbraco.Web.Editors var objectType = ConvertToObjectType(entityType); if (objectType.HasValue) { - var found = Services.EntityService.Get(key, objectType.Value); + var found = _entityService.Get(key, objectType.Value); if (found == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } - return Mapper.Map(found); + return _umbracoMapper.Map(found); } //now we need to convert the unknown ones switch (entityType) @@ -982,7 +1012,7 @@ namespace Umbraco.Web.Editors var objectType = ConvertToObjectType(entityType); if (objectType.HasValue) { - var found = Services.EntityService.Get(id, objectType.Value); + var found = _entityService.Get(id, objectType.Value); if (found == null) { throw new HttpResponseException(HttpStatusCode.NotFound); @@ -1058,64 +1088,64 @@ namespace Umbraco.Web.Editors if (objectType.HasValue) { // TODO: Should we order this by something ? - var entities = Services.EntityService.GetAll(objectType.Value).WhereNotNull().Select(MapEntities()); + var entities = _entityService.GetAll(objectType.Value).WhereNotNull().Select(MapEntities()); return ExecutePostFilter(entities, postFilter); } //now we need to convert the unknown ones switch (entityType) { case UmbracoEntityTypes.Template: - var templates = Services.FileService.GetTemplates(); + var templates = _fileService.GetTemplates(); var filteredTemplates = ExecutePostFilter(templates, postFilter); return filteredTemplates.Select(MapEntities()); case UmbracoEntityTypes.Macro: //Get all macros from the macro service - var macros = Services.MacroService.GetAll().WhereNotNull().OrderBy(x => x.Name); + var macros = _macroService.GetAll().WhereNotNull().OrderBy(x => x.Name); var filteredMacros = ExecutePostFilter(macros, postFilter); return filteredMacros.Select(MapEntities()); case UmbracoEntityTypes.PropertyType: //get all document types, then combine all property types into one list - var propertyTypes = Services.ContentTypeService.GetAll().Cast() - .Concat(Services.MediaTypeService.GetAll()) + var propertyTypes = _contentTypeService.GetAll().Cast() + .Concat(_mediaTypeService.GetAll()) .ToArray() .SelectMany(x => x.PropertyTypes) .DistinctBy(composition => composition.Alias); var filteredPropertyTypes = ExecutePostFilter(propertyTypes, postFilter); - return Mapper.MapEnumerable(filteredPropertyTypes); + return _umbracoMapper.MapEnumerable(filteredPropertyTypes); case UmbracoEntityTypes.PropertyGroup: //get all document types, then combine all property types into one list - var propertyGroups = Services.ContentTypeService.GetAll().Cast() - .Concat(Services.MediaTypeService.GetAll()) + var propertyGroups = _contentTypeService.GetAll().Cast() + .Concat(_mediaTypeService.GetAll()) .ToArray() .SelectMany(x => x.PropertyGroups) .DistinctBy(composition => composition.Name); var filteredpropertyGroups = ExecutePostFilter(propertyGroups, postFilter); - return Mapper.MapEnumerable(filteredpropertyGroups); + return _umbracoMapper.MapEnumerable(filteredpropertyGroups); case UmbracoEntityTypes.User: - var users = Services.UserService.GetAll(0, int.MaxValue, out _); + var users = _userService.GetAll(0, int.MaxValue, out _); var filteredUsers = ExecutePostFilter(users, postFilter); - return Mapper.MapEnumerable(filteredUsers); + return _umbracoMapper.MapEnumerable(filteredUsers); case UmbracoEntityTypes.Stylesheet: if (!postFilter.IsNullOrWhiteSpace()) throw new NotSupportedException("Filtering on stylesheets is not currently supported"); - return Services.FileService.GetStylesheets().Select(MapEntities()); + return _fileService.GetStylesheets().Select(MapEntities()); case UmbracoEntityTypes.Language: if (!postFilter.IsNullOrWhiteSpace()) throw new NotSupportedException("Filtering on languages is not currently supported"); - return Services.LocalizationService.GetAllLanguages().Select(MapEntities()); + return _localizationService.GetAllLanguages().Select(MapEntities()); case UmbracoEntityTypes.DictionaryItem: if (!postFilter.IsNullOrWhiteSpace()) @@ -1220,7 +1250,7 @@ namespace Umbraco.Web.Editors private EntityBasic MapEntity(object entity, string culture = null) { culture = culture ?? ClientCulture(); - return Mapper.Map(entity, context => { context.SetCulture(culture); }); + return _umbracoMapper.Map(entity, context => { context.SetCulture(culture); }); } private string ClientCulture() => Request.ClientCulture(); @@ -1230,9 +1260,9 @@ namespace Umbraco.Web.Editors { var list = new List(); - foreach (var dictionaryItem in Services.LocalizationService.GetRootDictionaryItems().OrderBy(DictionaryItemSort())) + foreach (var dictionaryItem in _localizationService.GetRootDictionaryItems().OrderBy(DictionaryItemSort())) { - var item = Mapper.Map(dictionaryItem); + var item = _umbracoMapper.Map(dictionaryItem); list.Add(item); GetChildItemsForList(dictionaryItem, list); } @@ -1244,9 +1274,9 @@ namespace Umbraco.Web.Editors private void GetChildItemsForList(IDictionaryItem dictionaryItem, ICollection list) { - foreach (var childItem in Services.LocalizationService.GetDictionaryItemChildren(dictionaryItem.Key).OrderBy(DictionaryItemSort())) + foreach (var childItem in _localizationService.GetDictionaryItemChildren(dictionaryItem.Key).OrderBy(DictionaryItemSort())) { - var item = Mapper.Map(childItem); + var item = _umbracoMapper.Map(childItem); list.Add(item); GetChildItemsForList(childItem, list); diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs b/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs index cb3dc73b85..5b191d8734 100644 --- a/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs +++ b/src/Umbraco.Web.BackOffice/ModelBinders/ContentItemBinder.cs @@ -40,91 +40,7 @@ namespace Umbraco.Web.BackOffice.ModelBinders _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); _modelBinderHelper = new ContentModelBinderHelper(); } - // private readonly UmbracoMapper _umbracoMapper; - // private readonly ContentTypeService _contentTypeService; - // private readonly ContentService _contentService; - // private readonly ContentModelBinderHelper _modelBinderHelper; - // - // public ContentItemBinder(UmbracoMapper umbracoMapper, ContentTypeService contentTypeService, ContentService contentService) - // { - // _umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper)); - // _contentTypeService = contentTypeService ?? throw new ArgumentNullException(nameof(contentTypeService)); - // _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); - // _modelBinderHelper = new ContentModelBinderHelper(); - // } - // - // /// - // /// Creates the model from the request and binds it to the context - // /// - // /// - // /// - // /// - // public Task BindModelAsync(ModelBindingContext bindingContext) - // { - // var actionContext = bindingContext.ActionContext; - // - // var model = _modelBinderHelper.BindModelFromMultipartRequest(actionContext, bindingContext); - // if (model == null) - // { - // bindingContext.Result = ModelBindingResult.Failed(); - // return Task.CompletedTask; - // } - // - // model.PersistedContent = ContentControllerBase.IsCreatingAction(model.Action) ? CreateNew(model) : GetExisting(model); - // - // //create the dto from the persisted model - // if (model.PersistedContent != null) - // { - // foreach (var variant in model.Variants) - // { - // //map the property dto collection with the culture of the current variant - // variant.PropertyCollectionDto = _umbracoMapper.Map( - // model.PersistedContent, - // context => - // { - // // either of these may be null and that is ok, if it's invariant they will be null which is what is expected - // context.SetCulture(variant.Culture); - // context.SetSegment(variant.Segment); - // }); - // - // //now map all of the saved values to the dto - // _modelBinderHelper.MapPropertyValuesFromSaved(variant, variant.PropertyCollectionDto); - // } - // } - // - // return Task.CompletedTask; - // } - // - // public bool BindModel(ActionContext actionContext, ModelBindingContext bindingContext) - // { - // var model = _modelBinderHelper.BindModelFromMultipartRequest(actionContext, bindingContext); - // if (model == null) return false; - // - // model.PersistedContent = ContentControllerBase.IsCreatingAction(model.Action) ? CreateNew(model) : GetExisting(model); - // - // //create the dto from the persisted model - // if (model.PersistedContent != null) - // { - // foreach (var variant in model.Variants) - // { - // //map the property dto collection with the culture of the current variant - // variant.PropertyCollectionDto = _umbracoMapper.Map( - // model.PersistedContent, - // context => - // { - // // either of these may be null and that is ok, if it's invariant they will be null which is what is expected - // context.SetCulture(variant.Culture); - // context.SetSegment(variant.Segment); - // }); - // - // //now map all of the saved values to the dto - // _modelBinderHelper.MapPropertyValuesFromSaved(variant, variant.PropertyCollectionDto); - // } - // } - // - // return true; - // } - // + protected virtual IContent GetExisting(ContentItemSave model) { return _contentService.GetById(model.Id); diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/FromJsonPathAttribute.cs b/src/Umbraco.Web.BackOffice/ModelBinders/FromJsonPathAttribute.cs new file mode 100644 index 0000000000..371f146e00 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/ModelBinders/FromJsonPathAttribute.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Umbraco.Extensions; + +namespace Umbraco.Web.BackOffice.ModelBinders +{ + /// + /// Used to bind a value from an inner json property + /// + /// + /// An example would be if you had json like: + /// { ids: [1,2,3,4] } + /// And you had an action like: GetByIds(int[] ids, UmbracoEntityTypes type) + /// The ids array will not bind because the object being sent up is an object and not an array so the + /// normal json formatter will not figure this out. + /// This would also let you bind sub levels of the JSON being sent up too if you wanted with any jsonpath + /// + internal class FromJsonPathAttribute : ModelBinderAttribute + { + public FromJsonPathAttribute() : base(typeof(JsonPathBinder)) + { + + } + + internal class JsonPathBinder : IModelBinder + { + public async Task BindModelAsync(ModelBindingContext bindingContext) + { + if (bindingContext.HttpContext.Request.Method.Equals(HttpMethod.Get.ToString(), StringComparison.InvariantCultureIgnoreCase)) + { + return; + } + + + var strJson = bindingContext.HttpContext.Request.GetRawBodyString(); + + + if (string.IsNullOrWhiteSpace(strJson)) + { + return; + } + + var json = JsonConvert.DeserializeObject(strJson); + + //if no explicit json path then use the model name + var match = json.SelectToken(bindingContext.FieldName ?? bindingContext.ModelName); + + if (match == null) + { + return; + } + + var model = match.ToObject(bindingContext.ModelType); + + bindingContext.Result = ModelBindingResult.Success(model); + } + + } + } +} diff --git a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj index 911a157223..eecac784c8 100644 --- a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj +++ b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj @@ -14,6 +14,7 @@ + diff --git a/src/Umbraco.Web.Common/Extensions/HttpRequestExtensions.cs b/src/Umbraco.Web.Common/Extensions/HttpRequestExtensions.cs index b6652de6cf..48f50feeac 100644 --- a/src/Umbraco.Web.Common/Extensions/HttpRequestExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/HttpRequestExtensions.cs @@ -1,5 +1,8 @@ using System; +using System.IO; using System.Net; +using System.Text; +using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Umbraco.Core; @@ -20,7 +23,7 @@ namespace Umbraco.Extensions return new Uri(request.GetEncodedUrl(), UriKind.RelativeOrAbsolute).IsClientSideRequest(); } - internal static string ClientCulture(this HttpRequest request) + public static string ClientCulture(this HttpRequest request) { return request.Headers.TryGetValue("X-UMB-CULTURE", out var values) ? values[0] : null; } @@ -52,6 +55,21 @@ namespace Umbraco.Extensions { const string NullIpAddress = "::1"; return address != null && address.ToString() != NullIpAddress; + } + + public static string GetRawBodyString(this HttpRequest request, Encoding encoding = null) + { + request.Body.Seek(0, SeekOrigin.Begin); + + var reader = new StreamReader(request.Body, encoding ?? Encoding.UTF8); + + var result = reader.ReadToEnd(); + request.Body.Seek(0, SeekOrigin.Begin); + return result; + + + + } } } diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs index 0712dfeb74..ae069b69e0 100644 --- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs @@ -174,10 +174,7 @@ namespace Umbraco.Web.Editors "currentUserApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( controller => controller.PostChangePassword(null)) }, - { - "entityApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( - controller => controller.GetById(0, UmbracoEntityTypes.Media)) - }, + // { // "dataTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( // controller => controller.GetById(0)) diff --git a/src/Umbraco.Web/Editors/FromJsonPathAttribute.cs b/src/Umbraco.Web/Editors/FromJsonPathAttribute.cs deleted file mode 100644 index b0a1c6ebaf..0000000000 --- a/src/Umbraco.Web/Editors/FromJsonPathAttribute.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System.Collections.Generic; -using System.Net.Http; -using System.Web.Http; -using System.Web.Http.Controllers; -using System.Web.Http.ModelBinding; -using System.Web.Http.ValueProviders; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Umbraco.Core; - -namespace Umbraco.Web.Editors -{ - /// - /// Used to bind a value from an inner json property - /// - /// - /// An example would be if you had json like: - /// { ids: [1,2,3,4] } - /// - /// And you had an action like: GetByIds(int[] ids, UmbracoEntityTypes type) - /// - /// The ids array will not bind because the object being sent up is an object and not an array so the - /// normal json formatter will not figure this out. - /// - /// This would also let you bind sub levels of the JSON being sent up too if you wanted with any jsonpath - /// - internal class FromJsonPathAttribute : ModelBinderAttribute - { - private readonly string _jsonPath; - private readonly FromUriAttribute _fromUriAttribute = new FromUriAttribute(); - - public FromJsonPathAttribute() - { - } - - public FromJsonPathAttribute(string jsonPath) : base(typeof(JsonPathBinder)) - { - _jsonPath = jsonPath; - } - - public override IEnumerable GetValueProviderFactories(HttpConfiguration configuration) - { - return _fromUriAttribute.GetValueProviderFactories(configuration); - } - - public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter) - { - var config = parameter.Configuration; - //get the default binder, we'll use that if it's a GET or if the body is empty - var underlyingBinder = base.GetModelBinder(config, parameter.ParameterType); - var binder = new JsonPathBinder(underlyingBinder, _jsonPath); - var valueProviderFactories = GetValueProviderFactories(config); - - return new ModelBinderParameterBinding(parameter, binder, valueProviderFactories); - } - - private class JsonPathBinder : IModelBinder - { - private readonly IModelBinder _underlyingBinder; - private readonly string _jsonPath; - - public JsonPathBinder(IModelBinder underlyingBinder, string jsonPath) - { - _underlyingBinder = underlyingBinder; - _jsonPath = jsonPath; - } - - public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) - { - if (actionContext.Request.Method == HttpMethod.Get) - { - return _underlyingBinder.BindModel(actionContext, bindingContext); - } - - var requestContent = new HttpMessageContent(actionContext.Request); - var strJson = requestContent.HttpRequestMessage.Content.ReadAsStringAsync().Result; - - if (strJson.IsNullOrWhiteSpace()) - { - return _underlyingBinder.BindModel(actionContext, bindingContext); - } - - var json = JsonConvert.DeserializeObject(strJson); - - //if no explicit json path then use the model name - var match = json.SelectToken(_jsonPath ?? bindingContext.ModelName); - - if (match == null) - { - return false; - } - - bindingContext.Model = match.ToObject(bindingContext.ModelType); - - return true; - } - } - - - } -} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 5e6098a078..f41460b66b 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -227,7 +227,6 @@ - @@ -303,7 +302,6 @@ -