diff --git a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs index c171b76762..8f30e88a5e 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs @@ -90,9 +90,12 @@ namespace Umbraco.Tests.Models.Mapping var content = MockedContent.CreateSimpleContent(contentType); FixUsers(content); - var result = Mapper.Map(content); + var result = Mapper.Map(content); - AssertContentItem(result, content); + foreach (var p in content.Properties) + { + AssertProperty(result, p); + } } [Test] @@ -102,9 +105,12 @@ namespace Umbraco.Tests.Models.Mapping var content = MockedMedia.CreateMediaImage(contentType, -1); FixUsers(content); - var result = Mapper.Map(content); + var result = Mapper.Map(content); - AssertContentItem(result, content); + foreach (var p in content.Properties) + { + AssertProperty(result, p); + } } [Test] @@ -288,7 +294,7 @@ namespace Umbraco.Tests.Models.Mapping Assert.AreEqual(pDto.Value, p.GetValue().ToString()); } - private void AssertProperty(ContentItemBasic result, Property p) + private void AssertProperty(IContentProperties result, Property p) { AssertBasicProperty(result, p); diff --git a/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs b/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs index 43d6eb888d..180d9746a3 100644 --- a/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs +++ b/src/Umbraco.Web/Editors/Binders/ContentItemBinder.cs @@ -52,17 +52,28 @@ namespace Umbraco.Web.Editors.Binders model.PersistedContent = ContentControllerBase.IsCreatingAction(model.Action) ? CreateNew(model) : GetExisting(model); - //TODO: Implement this! - ////create the dto from the persisted model - //if (model.PersistedContent != null) - //{ - // model.ContentDto = MapFromPersisted(model); - //} - //if (model.ContentDto != null) - //{ - // //now map all of the saved values to the dto - // _modelBinderHelper.MapPropertyValuesFromSaved(model, model.ContentDto); - //} + //create the dto from the persisted model + if (model.PersistedContent != null) + { + foreach (var variant in model.Variants) + { + if (variant.Culture.IsNullOrWhiteSpace()) + { + //map the property dto collection (no culture is passed to the mapping context so it will be invariant) + variant.PropertyCollectionDto = Mapper.Map(model.PersistedContent); + } + else + { + //map the property dto collection with the culture of the current variant + variant.PropertyCollectionDto = Mapper.Map( + model.PersistedContent, + options => options.Items[ResolutionContextExtensions.CultureKey] = variant.Culture); + } + + //now map all of the saved values to the dto + _modelBinderHelper.MapPropertyValuesFromSaved(variant, variant.PropertyCollectionDto); + } + } return true; } @@ -84,17 +95,6 @@ namespace Umbraco.Web.Editors.Binders model.ParentId, contentType); } - - private static ContentItemDto MapFromPersisted(ContentItemSave model) - { - return MapFromPersisted(model.PersistedContent); - } - - internal static ContentItemDto MapFromPersisted(IContent content) - { - return Mapper.Map(content); - } - } } diff --git a/src/Umbraco.Web/Editors/Binders/ContentModelBinderHelper.cs b/src/Umbraco.Web/Editors/Binders/ContentModelBinderHelper.cs index 16f7c70b0b..284b4b0c05 100644 --- a/src/Umbraco.Web/Editors/Binders/ContentModelBinderHelper.cs +++ b/src/Umbraco.Web/Editors/Binders/ContentModelBinderHelper.cs @@ -56,7 +56,7 @@ namespace Umbraco.Web.Editors.Binders /// /// /// - public void MapPropertyValuesFromSaved(IContentProperties saveModel, ContentItemDto dto) + public void MapPropertyValuesFromSaved(IContentProperties saveModel, ContentPropertyCollectionDto dto) { //NOTE: Don't convert this to linq, this is much quicker foreach (var p in saveModel.Properties) diff --git a/src/Umbraco.Web/Editors/Binders/MediaItemBinder.cs b/src/Umbraco.Web/Editors/Binders/MediaItemBinder.cs index 718850c07a..bbba9d8122 100644 --- a/src/Umbraco.Web/Editors/Binders/MediaItemBinder.cs +++ b/src/Umbraco.Web/Editors/Binders/MediaItemBinder.cs @@ -46,12 +46,9 @@ namespace Umbraco.Web.Editors.Binders //create the dto from the persisted model if (model.PersistedContent != null) { - model.ContentDto = MapFromPersisted(model); - } - if (model.ContentDto != null) - { + model.PropertyCollectionDto = Mapper.Map(model.PersistedContent); //now map all of the saved values to the dto - _modelBinderHelper.MapPropertyValuesFromSaved(model, model.ContentDto); + _modelBinderHelper.MapPropertyValuesFromSaved(model, model.PropertyCollectionDto); } model.Name = model.Name.Trim(); @@ -74,9 +71,5 @@ namespace Umbraco.Web.Editors.Binders return new Core.Models.Media(model.Name, model.ParentId, mediaType); } - private ContentItemDto MapFromPersisted(MediaItemSave model) - { - return Mapper.Map(model.PersistedContent); - } } } diff --git a/src/Umbraco.Web/Editors/Binders/MemberBinder.cs b/src/Umbraco.Web/Editors/Binders/MemberBinder.cs index 7b48b7a4ff..f50122bc9b 100644 --- a/src/Umbraco.Web/Editors/Binders/MemberBinder.cs +++ b/src/Umbraco.Web/Editors/Binders/MemberBinder.cs @@ -50,12 +50,9 @@ namespace Umbraco.Web.Editors.Binders //create the dto from the persisted model if (model.PersistedContent != null) { - model.ContentDto = MapFromPersisted(model); - } - if (model.ContentDto != null) - { + model.PropertyCollectionDto = Mapper.Map(model.PersistedContent); //now map all of the saved values to the dto - _modelBinderHelper.MapPropertyValuesFromSaved(model, model.ContentDto); + _modelBinderHelper.MapPropertyValuesFromSaved(model, model.PropertyCollectionDto); } model.Name = model.Name.Trim(); @@ -202,15 +199,6 @@ namespace Umbraco.Web.Editors.Binders } } } - - private ContentItemDto MapFromPersisted(MemberSave model) - { - //need to explicitly cast since it's an explicit implementation - var saveModel = (IContentSave)model; - - return Mapper.Map(saveModel.PersistedContent); - } - } } diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 2e41c01296..8eb29887a0 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -591,7 +591,7 @@ namespace Umbraco.Web.Editors /// /// [FileUploadCleanupFilter] - [ContentPostValidate] + [ContentSaveValidation] public ContentItemDisplay PostSaveBlueprint([ModelBinder(typeof(ContentItemBinder))] ContentItemSave contentItem) { var contentItemDisplay = PostSaveInternal(contentItem, @@ -613,7 +613,7 @@ namespace Umbraco.Web.Editors /// /// [FileUploadCleanupFilter] - [ContentPostValidate] + [ContentSaveValidation] [OutgoingEditorModelEvent] public ContentItemDisplay PostSave([ModelBinder(typeof(ContentItemBinder))] ContentItemSave contentItem) { diff --git a/src/Umbraco.Web/Editors/ContentControllerBase.cs b/src/Umbraco.Web/Editors/ContentControllerBase.cs index 931ef46dcd..1ea1372d81 100644 --- a/src/Umbraco.Web/Editors/ContentControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentControllerBase.cs @@ -47,7 +47,7 @@ namespace Umbraco.Web.Editors /// internal void MapPropertyValues( TSaved contentItem, - ContentItemDto dto, + ContentPropertyCollectionDto dto, Func getPropertyValue, Action savePropertyValue) where TPersisted : IContentBase diff --git a/src/Umbraco.Web/Editors/Filters/ContentItemValidationHelper.cs b/src/Umbraco.Web/Editors/Filters/ContentItemValidationHelper.cs index d75c4c8a63..dc9beae392 100644 --- a/src/Umbraco.Web/Editors/Filters/ContentItemValidationHelper.cs +++ b/src/Umbraco.Web/Editors/Filters/ContentItemValidationHelper.cs @@ -43,38 +43,21 @@ namespace Umbraco.Web.Editors.Filters { } - ///// - ///// Validates the content item and updates the Response and ModelState accordingly - ///// - ///// - ///// - //public void ValidateItem(HttpActionContext actionContext, string argumentName) + //public void ValidateItem(HttpActionContext actionContext, TModelSave model, IContentProperties modelWithProperties, ContentPropertyCollectionDto dto) //{ - // if (!(actionContext.ActionArguments[argumentName] is TModelSave model)) - // { - // actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "No " + typeof(TModelSave) + " found in request"); - // return; - // } - - // ValidateItem(actionContext, model); - + // //now do each validation step + // if (ValidateExistingContent(model, actionContext) == false) return; + // if (ValidateProperties(model, modelWithProperties, actionContext) == false) return; + // if (ValidatePropertyData(model, modelWithProperties, dto, actionContext.ModelState) == false) return; //} - public void ValidateItem(HttpActionContext actionContext, TModelSave model, IContentProperties modelWithProperties, ContentItemDto dto) - { - //now do each validation step - if (ValidateExistingContent(model, actionContext) == false) return; - if (ValidateProperties(model, modelWithProperties, actionContext) == false) return; - if (ValidatePropertyData(model, modelWithProperties, dto, actionContext.ModelState) == false) return; - } - /// /// Ensure the content exists /// /// /// /// - protected virtual bool ValidateExistingContent(TModelSave postedItem, HttpActionContext actionContext) + public virtual bool ValidateExistingContent(TModelSave postedItem, HttpActionContext actionContext) { var persistedContent = postedItem.PersistedContent; if (persistedContent == null) @@ -93,7 +76,7 @@ namespace Umbraco.Web.Editors.Filters /// /// /// - protected virtual bool ValidateProperties(TModelSave model, IContentProperties modelWithProperties, HttpActionContext actionContext) + public virtual bool ValidateProperties(TModelSave model, IContentProperties modelWithProperties, HttpActionContext actionContext) { var persistedContent = model.PersistedContent; return ValidateProperties(modelWithProperties.Properties.ToList(), persistedContent.Properties.ToList(), actionContext); @@ -138,7 +121,7 @@ namespace Umbraco.Web.Editors.Filters public virtual bool ValidatePropertyData( TModelSave model, IContentProperties modelWithProperties, - ContentItemDto dto, + ContentPropertyCollectionDto dto, ModelStateDictionary modelState) { var properties = modelWithProperties.Properties.ToList(); diff --git a/src/Umbraco.Web/Editors/Filters/ContentPostValidateAttribute.cs b/src/Umbraco.Web/Editors/Filters/ContentSaveValidationAttribute.cs similarity index 65% rename from src/Umbraco.Web/Editors/Filters/ContentPostValidateAttribute.cs rename to src/Umbraco.Web/Editors/Filters/ContentSaveValidationAttribute.cs index c044ad0b5c..7bb803ffa8 100644 --- a/src/Umbraco.Web/Editors/Filters/ContentPostValidateAttribute.cs +++ b/src/Umbraco.Web/Editors/Filters/ContentSaveValidationAttribute.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; 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; @@ -14,47 +15,55 @@ using Umbraco.Web._Legacy.Actions; namespace Umbraco.Web.Editors.Filters { /// - /// Checks if the user has access to post a content item based on whether it's being created or saved. + /// Validates the incoming model along with if the user is allowed to perform the operation /// - internal sealed class ContentPostValidateAttribute : ActionFilterAttribute + internal sealed class ContentSaveValidationAttribute : ActionFilterAttribute { - private readonly IContentService _contentService; - private readonly WebSecurity _security; - private readonly IUserService _userService; - private readonly IEntityService _entityService; - - public ContentPostValidateAttribute() + public ContentSaveValidationAttribute(): this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.ContentService, Current.Services.UserService, Current.Services.EntityService, UmbracoContext.Current.Security) { } - // fixme wtf is this? - public ContentPostValidateAttribute(IContentService contentService, IUserService userService, IEntityService entityService, WebSecurity security) + public ContentSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, IContentService contentService, IUserService userService, IEntityService entityService, WebSecurity security) { + _logger = logger; + _umbracoContextAccessor = umbracoContextAccessor; _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); _userService = userService ?? throw new ArgumentNullException(nameof(userService)); _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); _security = security ?? throw new ArgumentNullException(nameof(security)); } - // fixme all these should be injected properties - - private IContentService ContentService - => _contentService ?? Current.Services.ContentService; - - private WebSecurity Security - => _security ?? UmbracoContext.Current.Security; - - private IUserService UserService - => _userService ?? Current.Services.UserService; - - private IEntityService EntityService - => _entityService ?? Current.Services.EntityService; - - public override bool AllowMultiple - => true; + private readonly ILogger _logger; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IContentService _contentService; + private readonly WebSecurity _security; + private readonly IUserService _userService; + private readonly IEntityService _entityService; public override void OnActionExecuting(HttpActionContext actionContext) { - var contentItem = (ContentItemSave)actionContext.ActionArguments["contentItem"]; + var model = (ContentItemSave)actionContext.ActionArguments["contentItem"]; + var contentItemValidator = new ContentItemValidationHelper(_logger, _umbracoContextAccessor); + + if (ValidateUserAccess(model, actionContext)) + { + //now do each validation step + if (contentItemValidator.ValidateExistingContent(model, actionContext) == false) return; + //validate for each variant + foreach (var variant in model.Variants) + { + if (contentItemValidator.ValidateProperties(model, variant, actionContext) == false) return; + if (contentItemValidator.ValidatePropertyData(model, variant, variant.PropertyCollectionDto, actionContext.ModelState) == false) return; + } + } + } + + /// + /// Checks if the user has access to post a content item based on whether it's being created or saved. + /// + /// + /// + private bool ValidateUserAccess(ContentItemSave contentItem, HttpActionContext actionContext) + { //We now need to validate that the user is allowed to be doing what they are doing. //Based on the action we need to check different permissions. @@ -87,7 +96,7 @@ namespace Umbraco.Web.Editors.Filters if (contentItem.ParentId != Constants.System.Root) { - contentToCheck = ContentService.GetById(contentItem.ParentId); + contentToCheck = _contentService.GetById(contentItem.ParentId); contentIdToCheck = contentToCheck.Id; } else @@ -102,7 +111,7 @@ namespace Umbraco.Web.Editors.Filters permissionToCheck.Add(ActionToPublish.Instance.Letter); if (contentItem.ParentId != Constants.System.Root) { - contentToCheck = ContentService.GetById(contentItem.ParentId); + contentToCheck = _contentService.GetById(contentItem.ParentId); contentIdToCheck = contentToCheck.Id; } else @@ -119,7 +128,7 @@ namespace Umbraco.Web.Editors.Filters if (contentItem.ParentId != Constants.System.Root) { - contentToCheck = ContentService.GetById(contentItem.ParentId); + contentToCheck = _contentService.GetById(contentItem.ParentId); contentIdToCheck = contentToCheck.Id; } else @@ -133,12 +142,15 @@ namespace Umbraco.Web.Editors.Filters if (ContentController.CheckPermissions( actionContext.Request.Properties, - Security.CurrentUser, - UserService, ContentService, EntityService, + _security.CurrentUser, + _userService, _contentService, _entityService, contentIdToCheck, permissionToCheck.ToArray(), contentToCheck) == false) { actionContext.Response = actionContext.Request.CreateUserNoAccessResponse(); + return false; } + + return true; } } } diff --git a/src/Umbraco.Web/Editors/Filters/MediaItemSaveValidationAttribute.cs b/src/Umbraco.Web/Editors/Filters/MediaItemSaveValidationAttribute.cs index 7f2901e69e..4bb2e72b0c 100644 --- a/src/Umbraco.Web/Editors/Filters/MediaItemSaveValidationAttribute.cs +++ b/src/Umbraco.Web/Editors/Filters/MediaItemSaveValidationAttribute.cs @@ -42,7 +42,12 @@ namespace Umbraco.Web.Editors.Filters var contentItemValidator = new ContentItemValidationHelper(_logger, _umbracoContextAccessor); if (ValidateUserAccess(model, actionContext)) - contentItemValidator.ValidateItem(actionContext, model, model, model.ContentDto); + { + //now do each validation step + if (!contentItemValidator.ValidateExistingContent(model, actionContext)) return; + if (!contentItemValidator.ValidateProperties(model, model, actionContext)) return; + if (!contentItemValidator.ValidatePropertyData(model, model, model.PropertyCollectionDto, actionContext.ModelState)) return; + } } /// diff --git a/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs b/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs index 930e8319f6..7333a39536 100644 --- a/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs +++ b/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs @@ -32,7 +32,10 @@ namespace Umbraco.Web.Editors.Filters { var model = (MemberSave)actionContext.ActionArguments["contentItem"]; var contentItemValidator = new MemberValidationHelper(_logger, _umbracoContextAccessor); - contentItemValidator.ValidateItem(actionContext, model, model, model.ContentDto); + //now do each validation step + if (!contentItemValidator.ValidateExistingContent(model, actionContext)) return; + if (!contentItemValidator.ValidateProperties(model, model, actionContext)) return; + if (!contentItemValidator.ValidatePropertyData(model, model, model.PropertyCollectionDto, actionContext.ModelState)) return; } } } diff --git a/src/Umbraco.Web/Editors/Filters/MemberValidationHelper.cs b/src/Umbraco.Web/Editors/Filters/MemberValidationHelper.cs index 5fa5e955b6..74fcac60ea 100644 --- a/src/Umbraco.Web/Editors/Filters/MemberValidationHelper.cs +++ b/src/Umbraco.Web/Editors/Filters/MemberValidationHelper.cs @@ -56,7 +56,7 @@ namespace Umbraco.Web.Editors.Filters /// /// /// - public override bool ValidatePropertyData(MemberSave model, IContentProperties modelWithProperties, ContentItemDto dto, ModelStateDictionary modelState) + public override bool ValidatePropertyData(MemberSave model, IContentProperties modelWithProperties, ContentPropertyCollectionDto dto, ModelStateDictionary modelState) { if (model.Username.IsNullOrWhiteSpace()) { @@ -103,7 +103,7 @@ namespace Umbraco.Web.Editors.Filters /// /// /// - protected override bool ValidateProperties(MemberSave model, IContentProperties modelWithProperties, HttpActionContext actionContext) + public override bool ValidateProperties(MemberSave model, IContentProperties modelWithProperties, HttpActionContext actionContext) { var propertiesToValidate = model.Properties.ToList(); var defaultProps = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 9da9107534..4a7dae9ed0 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -452,7 +452,7 @@ namespace Umbraco.Web.Editors MapPropertyValues( contentItem, - contentItem.ContentDto, + contentItem.PropertyCollectionDto, (save, property) => property.GetValue(), //get prop val (save, property, v) => property.SetValue(v)); //set prop val diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index 39e081bc64..448da27290 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -397,7 +397,7 @@ namespace Umbraco.Web.Editors //use the base method to map the rest of the properties base.MapPropertyValues( contentItem, - contentItem.ContentDto, + contentItem.PropertyCollectionDto, (save, property) => property.GetValue(), //get prop val (save, property, v) => property.SetValue(v)); //set prop val } diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentBaseSave.cs b/src/Umbraco.Web/Models/ContentEditing/ContentBaseSave.cs index a9fefbc62e..8a840a60c3 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentBaseSave.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentBaseSave.cs @@ -41,7 +41,7 @@ namespace Umbraco.Web.Models.ContentEditing } /// - /// The DTO object used to gather all required content data including data type information etc... for use with validation - used during inbound model binding + /// The property DTO object is used to gather all required property data including data type information etc... for use with validation - used during inbound model binding /// /// /// We basically use this object to hydrate all required data from the database into one object so we can validate everything we need @@ -49,7 +49,7 @@ namespace Umbraco.Web.Models.ContentEditing /// This is not used for outgoing model information. /// [IgnoreDataMember] - internal ContentItemDto ContentDto { get; set; } + internal ContentPropertyCollectionDto PropertyCollectionDto { get; set; } #endregion diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs index db788c9d95..427dadb2c9 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Linq; using System.Runtime.Serialization; using Newtonsoft.Json; using Umbraco.Core.Models; @@ -72,7 +73,7 @@ namespace Umbraco.Web.Models.ContentEditing public ContentItemBasic() { //ensure its not null - _properties = new List(); + _properties = Enumerable.Empty(); } private IEnumerable _properties; diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs index 7ce6dd9000..b63fa38ed6 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs @@ -181,7 +181,7 @@ namespace Umbraco.Web.Models.ContentEditing /// This is not used for outgoing model information. /// [IgnoreDataMember] - internal ContentItemDto ContentDto { get; set; } + internal ContentPropertyCollectionDto ContentDto { get; set; } /// /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemDto.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemDto.cs deleted file mode 100644 index 2d039dddd6..0000000000 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemDto.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Newtonsoft.Json; -using Umbraco.Core.Models; - -namespace Umbraco.Web.Models.ContentEditing -{ - /// - /// Represents a content item from the database including all of the required data that we need to work with such as data type data - /// - internal class ContentItemDto: ContentItemBasic - { - } -} diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemSave.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemSave.cs index 98ccd47d5a..0c5f0b8c19 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemSave.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemSave.cs @@ -69,10 +69,6 @@ namespace Umbraco.Web.Models.ContentEditing set => ((IContentSave)this).PersistedContent = value; } - ////Non explicit internal getter so we don't need to explicitly cast in our own code - //[IgnoreDataMember] - //internal ContentItemDto ContentDto { get;set; } - #endregion } diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyCollectionDto.cs b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyCollectionDto.cs new file mode 100644 index 0000000000..dc6a92cc93 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyCollectionDto.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Umbraco.Core.Models; + +namespace Umbraco.Web.Models.ContentEditing +{ + /// + /// Used to map property values when saving content/media/members + /// + /// + /// This is only used during mapping operations, it is not used for angular purposes + /// + internal class ContentPropertyCollectionDto : IContentProperties + { + public ContentPropertyCollectionDto() + { + Properties = Enumerable.Empty(); + } + + public IEnumerable Properties { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentVariantSave.cs b/src/Umbraco.Web/Models/ContentEditing/ContentVariantSave.cs index 72a59f05cc..dc50129b37 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentVariantSave.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentVariantSave.cs @@ -32,6 +32,15 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "publish")] public bool Publish { get; set; } - + /// + /// The property DTO object is used to gather all required property data including data type information etc... for use with validation - used during inbound model binding + /// + /// + /// We basically use this object to hydrate all required data from the database into one object so we can validate everything we need + /// instead of having to look up all the data individually. + /// This is not used for outgoing model information. + /// + [IgnoreDataMember] + internal ContentPropertyCollectionDto PropertyCollectionDto { get; set; } } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs index bd7de4176e..cea151f6ca 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs @@ -42,7 +42,7 @@ namespace Umbraco.Web.Models.Mapping { //We need to set the culture in the mapping context since this is needed to ensure that the correct property values //are resolved during the mapping - context.Items[ContextMapper.CultureKey] = x.IsoCode; + context.Items[ResolutionContextExtensions.CultureKey] = x.IsoCode; return context.Mapper.Map(source, null, context); }).ToList(); diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs index 3f6d9461d1..8786ab61ac 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs @@ -86,15 +86,9 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Alias, opt => opt.Ignore()) .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); - //FROM IContent TO ContentItemDto - CreateMap() - .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => - Udi.Create(src.Blueprint ? Constants.UdiEntityType.DocumentBlueprint : Constants.UdiEntityType.Document, src.Key))) - .ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => contentOwnerResolver.Resolve(src))) - .ForMember(dest => dest.Updater, opt => opt.Ignore()) - .ForMember(dest => dest.Icon, opt => opt.Ignore()) - .ForMember(dest => dest.Alias, opt => opt.Ignore()) - .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); + //FROM IContent TO ContentPropertyCollectionDto + //NOTE: the property mapping for cultures relies on a culture being set in the mapping context + CreateMap(); } } } diff --git a/src/Umbraco.Web/Models/Mapping/ContextMapper.cs b/src/Umbraco.Web/Models/Mapping/ContextMapper.cs deleted file mode 100644 index 6897a8ae62..0000000000 --- a/src/Umbraco.Web/Models/Mapping/ContextMapper.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using AutoMapper; -using ClientDependency.Core; - -namespace Umbraco.Web.Models.Mapping -{ - /// - /// Extends AutoMapper's class to handle Umbraco's context and other contextual data - /// - internal static class ContextMapper - { - //public const string UmbracoContextKey = "ContextMapper.UmbracoContext"; - public const string CultureKey = "ContextMapper.Culture"; - - public static TDestination Map(TSource obj, IDictionary contextVals) - => Mapper.Map(obj, opt => - { - //set other supplied context vals - if (contextVals != null) - { - foreach (var contextVal in contextVals) - { - opt.Items[contextVal.Key] = contextVal.Value; - } - } - }); - - public static TDestination Map(TSource obj, object contextVals) - => Mapper.Map(obj, opt => - { - //set other supplied context vals - if (contextVals != null) - { - foreach (var contextVal in contextVals.ToDictionary()) - { - opt.Items[contextVal.Key] = contextVal.Value; - } - } - }); - - /// - /// Returns the language Id in the mapping context if one is found - /// - /// - /// - public static string GetCulture(this ResolutionContext resolutionContext) - { - if (!resolutionContext.Options.Items.TryGetValue(CultureKey, out var obj)) return null; - - if (obj is string s) - return s; - - return null; - } - } -} - diff --git a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs index 6d81957a24..caa9f29e70 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs @@ -69,15 +69,15 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); //FROM IMedia TO ContentItemDto - CreateMap() - .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(Constants.UdiEntityType.Media, src.Key))) - .ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => mediaOwnerResolver.Resolve(src))) - .ForMember(dest => dest.Published, opt => opt.Ignore()) - .ForMember(dest => dest.Edited, opt => opt.Ignore()) - .ForMember(dest => dest.Updater, opt => opt.Ignore()) - .ForMember(dest => dest.Icon, opt => opt.Ignore()) - .ForMember(dest => dest.Alias, opt => opt.Ignore()) - .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); + CreateMap(); + //.ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(Constants.UdiEntityType.Media, src.Key))) + //.ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => mediaOwnerResolver.Resolve(src))) + //.ForMember(dest => dest.Published, opt => opt.Ignore()) + //.ForMember(dest => dest.Edited, opt => opt.Ignore()) + //.ForMember(dest => dest.Updater, opt => opt.Ignore()) + //.ForMember(dest => dest.Icon, opt => opt.Ignore()) + //.ForMember(dest => dest.Alias, opt => opt.Ignore()) + //.ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); } } } diff --git a/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs index a54baa8124..22d499f3d6 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberMapperProfile.cs @@ -123,14 +123,14 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.ContentTypeAlias, opt => opt.Ignore()); //FROM IMember TO ContentItemDto - CreateMap() - .ForMember(dest => dest.Udi, opt => opt.MapFrom(content => Udi.Create(Constants.UdiEntityType.Member, content.Key))) - .ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => memberOwnerResolver.Resolve(src))) - .ForMember(dest => dest.Published, opt => opt.Ignore()) - .ForMember(dest => dest.Edited, opt => opt.Ignore()) - .ForMember(dest => dest.Updater, opt => opt.Ignore()) - .ForMember(dest => dest.Icon, opt => opt.Ignore()) - .ForMember(dest => dest.Alias, opt => opt.Ignore()) + CreateMap() + //.ForMember(dest => dest.Udi, opt => opt.MapFrom(content => Udi.Create(Constants.UdiEntityType.Member, content.Key))) + //.ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => memberOwnerResolver.Resolve(src))) + //.ForMember(dest => dest.Published, opt => opt.Ignore()) + //.ForMember(dest => dest.Edited, opt => opt.Ignore()) + //.ForMember(dest => dest.Updater, opt => opt.Ignore()) + //.ForMember(dest => dest.Icon, opt => opt.Ignore()) + //.ForMember(dest => dest.Alias, opt => opt.Ignore()) //do no map the custom member properties (currently anyways, they were never there in 6.x) .ForMember(dest => dest.Properties, opt => opt.ResolveUsing(src => memberDtoPropertiesResolver.Resolve(src))); diff --git a/src/Umbraco.Web/Models/Mapping/ResolutionContextExtensions.cs b/src/Umbraco.Web/Models/Mapping/ResolutionContextExtensions.cs new file mode 100644 index 0000000000..27f00ce91c --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/ResolutionContextExtensions.cs @@ -0,0 +1,28 @@ +using AutoMapper; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// Extension methods for AutoMapper's + /// + internal static class ResolutionContextExtensions + { + public const string CultureKey = "ContextMapper.Culture"; + + /// + /// Returns the language Id in the mapping context if one is found + /// + /// + /// + public static string GetCulture(this ResolutionContext resolutionContext) + { + if (!resolutionContext.Options.Items.TryGetValue(CultureKey, out var obj)) return null; + + if (obj is string s) + return s; + + return null; + } + } +} + diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 47868dd48e..8c00d18e9a 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -250,7 +250,7 @@ - + @@ -616,7 +616,7 @@ - + @@ -930,7 +930,7 @@ - +