From 1b21e04dc2fd597eb52041418b46d7d3f28de429 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 6 Oct 2015 17:19:08 +0200 Subject: [PATCH] WIP for model mappers to new content editor save model, udpates to js to use that --- src/Umbraco.Core/Models/PropertyType.cs | 2 + .../Mapping/ContentTypeModelMappingTests.cs | 309 ++++++++---------- .../common/resources/contenttype.resource.js | 6 +- .../services/contenteditinghelper.service.js | 8 +- .../src/common/services/util.service.js | 25 ++ .../src/views/documenttype/edit.controller.js | 100 +++--- .../Editors/ContentTypeController.cs | 86 +++-- .../ContentEditing/ContentItemDisplayBase.cs | 3 + .../Models/ContentEditing/ContentTypeBasic.cs | 14 + .../ContentTypeCompositionDisplay.cs | 31 +- .../ContentEditing/ContentTypeDisplay.cs | 2 + .../Models/ContentEditing/ContentTypeSave.cs | 70 ++++ .../Models/ContentEditing/EntityBasic.cs | 8 +- .../ContentEditing/PropertyGroupBasic.cs | 34 ++ .../ContentEditing/PropertyGroupDisplay.cs | 28 +- .../ContentEditing/PropertyTypeBasic.cs | 41 +++ .../ContentEditing/PropertyTypeDisplay.cs | 47 +-- .../AvailableCompositeContentTypesResolver.cs | 73 ++--- .../Models/Mapping/ContentTypeModelMapper.cs | 293 +++++++---------- .../ContentTypeModelMapperExtensions.cs | 146 +++++++++ .../Models/Mapping/DataTypeModelMapper.cs | 3 + .../Models/Mapping/EntityModelMapper.cs | 24 +- .../Mapping/PropertyTypeGroupResolver.cs | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 175 ++++++++-- 24 files changed, 959 insertions(+), 571 deletions(-) create mode 100644 src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs create mode 100644 src/Umbraco.Web/Models/ContentEditing/PropertyGroupBasic.cs create mode 100644 src/Umbraco.Web/Models/ContentEditing/PropertyTypeBasic.cs create mode 100644 src/Umbraco.Web/Models/Mapping/ContentTypeModelMapperExtensions.cs diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index 239792bf08..9499a7bd7f 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -33,6 +33,8 @@ namespace Umbraco.Core.Models public PropertyType(IDataTypeDefinition dataTypeDefinition) { + if (dataTypeDefinition == null) throw new ArgumentNullException("dataTypeDefinition"); + if(dataTypeDefinition.HasIdentity) _dataTypeDefinitionId = dataTypeDefinition.Id; diff --git a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs index 4048ad5fde..88dc4e8e89 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs @@ -26,12 +26,12 @@ namespace Umbraco.Tests.Models.Mapping public class ContentTypeModelMappingTests : BaseUmbracoConfigurationTest { //Mocks of services that can be setup on a test by test basis to return whatever we want - private Mock _contentTypeService = new Mock(); - private Mock _contentService = new Mock(); - private Mock _dataTypeService = new Mock(); + private readonly Mock _contentTypeService = new Mock(); + private readonly Mock _contentService = new Mock(); + private readonly Mock _dataTypeService = new Mock(); private Mock _propertyEditorResolver; - - private Mock _entityService = new Mock(); + private readonly Mock _entityService = new Mock(); + private readonly Mock _fileService = new Mock(); [SetUp] public void Setup() @@ -47,7 +47,9 @@ namespace Umbraco.Tests.Models.Mapping contentService: _contentService.Object, contentTypeService:_contentTypeService.Object, - dataTypeService:_dataTypeService.Object), + dataTypeService:_dataTypeService.Object, + entityService:_entityService.Object, + fileService: _fileService.Object), nullCacheHelper, new ProfilingLogger(Mock.Of(), Mock.Of())); @@ -67,84 +69,9 @@ namespace Umbraco.Tests.Models.Mapping entityMapper.ConfigureMappings(configuration, appContext); }); } - + [Test] - public void PropertyTypeDisplay_To_PropertyType() - { - // setup the mocks to return the data we want to test against... - - _dataTypeService.Setup(x => x.GetDataTypeDefinitionById(It.IsAny())) - .Returns(Mock.Of( - definition => - definition.Id == 555 - && definition.PropertyEditorAlias == "myPropertyType" - && definition.DatabaseType == DataTypeDatabaseType.Nvarchar)); - - var display = new PropertyTypeDisplay() - { - Id = 1, - Alias = "test", - ContentTypeId = 4, - Description = "testing", - DataTypeId = 555, - - Value = "testsdfasdf", - Inherited = false, - Editor = "blah", - SortOrder = 6, - ContentTypeName = "Hello", - Label = "asdfasdf", - GroupId = 8, - Validation = new PropertyTypeValidation() - { - Mandatory = true, - Pattern = "asdfasdfa" - } - }; - - var result = Mapper.Map(display); - - Assert.AreEqual(1, result.Id); - Assert.AreEqual("test", result.Alias); - Assert.AreEqual("testing", result.Description); - Assert.AreEqual("blah", result.PropertyEditorAlias); - Assert.AreEqual(6, result.SortOrder); - Assert.AreEqual("asdfasdf", result.Name); - } - - [Test] - public void ContentGroupDisplay_To_PropertyGroup() - { - var display = new PropertyGroupDisplay() - { - ContentTypeId = 2, - Id = 1, - Inherited = false, - Name = "test", - ParentGroupId = 4, - ParentTabContentTypeNames = new[] - { - "hello", "world" - }, - SortOrder = 5, - ParentTabContentTypes = new[] - { - 10, 11 - } - }; - - - var result = Mapper.Map(display); - - Assert.AreEqual(1, result.Id); - Assert.AreEqual("test", result.Name); - Assert.AreEqual(4, result.ParentId); - Assert.AreEqual(5, result.SortOrder); - - } - - [Test] - public void ContentTypeDisplay_To_IContentType() + public void ContentTypeSave_To_IContentType() { //Arrange @@ -157,8 +84,14 @@ namespace Umbraco.Tests.Models.Mapping && definition.PropertyEditorAlias == "myPropertyType" && definition.DatabaseType == DataTypeDatabaseType.Nvarchar)); - - var display = CreateSimpleContentTypeDisplay(); + + _fileService.Setup(x => x.GetTemplate(It.IsAny())) + .Returns((string alias) => Mock.Of( + definition => + definition.Id == alias.GetHashCode() && definition.Alias == alias)); + + + var display = CreateContentTypeSave(); //Act @@ -194,10 +127,14 @@ namespace Umbraco.Tests.Models.Mapping } } - Assert.AreEqual(display.AllowedTemplates.Count(), result.AllowedTemplates.Count()); + var allowedTemplateAliases = display.AllowedTemplates + .Concat(new[] {display.DefaultTemplate}) + .Distinct(); + + Assert.AreEqual(allowedTemplateAliases.Count(), result.AllowedTemplates.Count()); for (var i = 0; i < display.AllowedTemplates.Count(); i++) { - Assert.AreEqual(display.AllowedTemplates.ElementAt(i).Id, result.AllowedTemplates.ElementAt(i).Id); + Assert.AreEqual(display.AllowedTemplates.ElementAt(i), result.AllowedTemplates.ElementAt(i).Alias); } Assert.AreEqual(display.AllowedContentTypes.Count(), result.AllowedContentTypes.Count()); @@ -208,7 +145,7 @@ namespace Umbraco.Tests.Models.Mapping } [Test] - public void ContentTypeDisplay_With_Composition_To_IContentType() + public void ContentTypeSave_With_Composition_To_IContentType() { //Arrange @@ -222,7 +159,7 @@ namespace Umbraco.Tests.Models.Mapping && definition.DatabaseType == DataTypeDatabaseType.Nvarchar)); - var display = CreateCompositionContentTypeDisplay(); + var display = CreateCompositionContentTypeSave(); //Act @@ -306,6 +243,91 @@ namespace Umbraco.Tests.Models.Mapping } + [Test] + public void PropertyGroupBasic_To_PropertyGroup() + { + var basic = new PropertyGroupBasic() + { + Id = 222, + Name = "Group 1", + SortOrder = 1, + Properties = new[] + { + new PropertyTypeBasic() + { + Id = 33, + SortOrder = 1, + Alias = "prop1", + Description = "property 1", + DataTypeId = 99, + GroupId = 222, + Label = "Prop 1", + Validation = new PropertyTypeValidation() + { + Mandatory = true, + Pattern = null + } + }, + new PropertyTypeBasic() + { + Id = 34, + SortOrder = 2, + Alias = "prop2", + Description = "property 2", + DataTypeId = 99, + GroupId = 222, + Label = "Prop 2", + Validation = new PropertyTypeValidation() + { + Mandatory = false, + Pattern = null + } + }, + } + }; + + var result = Mapper.Map(basic); + + Assert.AreEqual(basic.Name, result.Name); + Assert.AreEqual(basic.Id, result.Id); + Assert.AreEqual(basic.SortOrder, result.SortOrder); + Assert.AreEqual(basic.Properties.Count(), result.PropertyTypes.Count()); + } + + [Test] + public void PropertyTypeBasic_To_PropertyType() + { + _dataTypeService.Setup(x => x.GetDataTypeDefinitionById(It.IsAny())) + .Returns(new DataTypeDefinition("test")); + + var basic = new PropertyTypeBasic() + { + Id = 33, + SortOrder = 1, + Alias = "prop1", + Description = "property 1", + DataTypeId = 99, + GroupId = 222, + Label = "Prop 1", + Validation = new PropertyTypeValidation() + { + Mandatory = true, + Pattern = "xyz" + } + }; + + var result = Mapper.Map(basic); + + Assert.AreEqual(basic.Id, result.Id); + Assert.AreEqual(basic.SortOrder, result.SortOrder); + Assert.AreEqual(basic.Alias, result.Alias); + Assert.AreEqual(basic.Description, result.Description); + Assert.AreEqual(basic.DataTypeId, result.DataTypeDefinitionId); + Assert.AreEqual(basic.Label, result.Name); + Assert.AreEqual(basic.Validation.Mandatory, result.Mandatory); + Assert.AreEqual(basic.Validation.Pattern, result.ValidationRegExp); + } + [Test] public void IContentTypeComposition_To_ContentTypeDisplay() { @@ -318,6 +340,9 @@ namespace Umbraco.Tests.Models.Mapping _dataTypeService.Setup(x => x.GetPreValuesCollectionByDataTypeId(It.IsAny())) .Returns(new PreValueCollection(new Dictionary())); + _entityService.Setup(x => x.GetObjectType(It.IsAny())) + .Returns(UmbracoObjectTypes.DocumentType); + //return a textbox property editor for any requested editor by alias _propertyEditorResolver.Setup(resolver => resolver.GetByAlias(It.IsAny())) .Returns(new TextboxPropertyEditor()); @@ -403,31 +428,19 @@ namespace Umbraco.Tests.Models.Mapping } - private ContentTypeDisplay CreateSimpleContentTypeDisplay() + private ContentTypeSave CreateContentTypeSave() { - return new ContentTypeDisplay + return new ContentTypeSave { Alias = "test", AllowAsRoot = true, - AllowedTemplates = new List + AllowedTemplates = new [] { - new EntityBasic - { - Id = 555, - Alias = "template1", - Name = "Template1" - }, - new EntityBasic - { - Id = 556, - Alias = "template2", - Name = "Template2" - } + "template1", + "template2" }, - AllowedContentTypes = new [] {666, 667}, - AvailableCompositeContentTypes = new List(), - DefaultTemplate = new EntityBasic(){ Alias = "test" }, + DefaultTemplate = "test", Description = "hello world", Icon = "tree-icon", Id = 1234, @@ -437,18 +450,17 @@ namespace Umbraco.Tests.Models.Mapping ParentId = -1, Thumbnail = "tree-thumb", IsContainer = true, - Groups = new List() + Groups = new [] { - new PropertyGroupDisplay + new PropertyGroupBasic() { Id = 987, Name = "Tab 1", - ParentGroupId = -1, SortOrder = 0, Inherited = false, - Properties = new List + Properties = new [] { - new PropertyTypeDisplay + new PropertyTypeBasic { Alias = "property1", Description = "this is property 1", @@ -459,17 +471,8 @@ namespace Umbraco.Tests.Models.Mapping Mandatory = false, Pattern = "" }, - Editor = "myPropertyType", - Value = "value 1", - //View = ??? - isn't this the same as editor? - Config = new Dictionary - { - {"item1", "value1"}, - {"item2", "value2"} - }, SortOrder = 0, - DataTypeId = 555, - View = "blah" + DataTypeId = 555 } } } @@ -477,30 +480,19 @@ namespace Umbraco.Tests.Models.Mapping }; } - private ContentTypeDisplay CreateCompositionContentTypeDisplay() + private ContentTypeSave CreateCompositionContentTypeSave() { - return new ContentTypeDisplay + return new ContentTypeSave { Alias = "test", AllowAsRoot = true, - AllowedTemplates = new List + AllowedTemplates = new[] { - new EntityBasic - { - Id = 555, - Alias = "template1", - Name = "Template1" - }, - new EntityBasic - { - Id = 556, - Alias = "template2", - Name = "Template2" - } + "template1", + "template2" }, AllowedContentTypes = new[] { 666, 667 }, - AvailableCompositeContentTypes = new List(), - DefaultTemplate = new EntityBasic() { Alias = "test" }, + DefaultTemplate = "test", Description = "hello world", Icon = "tree-icon", Id = 1234, @@ -510,18 +502,17 @@ namespace Umbraco.Tests.Models.Mapping ParentId = -1, Thumbnail = "tree-thumb", IsContainer = true, - Groups = new List() + Groups = new[] { - new PropertyGroupDisplay + new PropertyGroupBasic() { Id = 987, Name = "Tab 1", - ParentGroupId = -1, SortOrder = 0, Inherited = false, - Properties = new List + Properties = new[] { - new PropertyTypeDisplay + new PropertyTypeBasic { Alias = "property1", Description = "this is property 1", @@ -532,30 +523,20 @@ namespace Umbraco.Tests.Models.Mapping Mandatory = false, Pattern = "" }, - Editor = "myPropertyType", - Value = "value 1", - //View = ??? - isn't this the same as editor? - Config = new Dictionary - { - {"item1", "value1"}, - {"item2", "value2"} - }, SortOrder = 0, - DataTypeId = 555, - View = "blah" + DataTypeId = 555 } } }, - new PropertyGroupDisplay + new PropertyGroupBasic() { Id = 894, Name = "Tab 2", - ParentGroupId = -1, SortOrder = 0, Inherited = true, - Properties = new List + Properties = new[] { - new PropertyTypeDisplay + new PropertyTypeBasic { Alias = "parentProperty", Description = "this is a property from the parent", @@ -565,17 +546,9 @@ namespace Umbraco.Tests.Models.Mapping { Mandatory = false, Pattern = "" - }, - Editor = "myPropertyType", - Value = "parent value", - //View = ??? - isn't this the same as editor? - Config = new Dictionary - { - {"item1", "value1"} - }, + }, SortOrder = 0, - DataTypeId = 555, - View = "blah" + DataTypeId = 555 } } diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js index ab3b95bf1c..631a3f6630 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js @@ -3,7 +3,7 @@ * @name umbraco.resources.contentTypeResource * @description Loads in data for content types **/ -function contentTypeResource($q, $http, umbRequestHelper) { +function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { return { @@ -157,8 +157,10 @@ function contentTypeResource($q, $http, umbRequestHelper) { */ save: function (contentType) { + var saveModel = umbDataFormatter.formatContentTypePostData(contentType); + return umbRequestHelper.resourcePromise( - $http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "PostSave"), contentType), + $http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "PostSave"), saveModel), 'Failed to save data for content type id ' + contentType.id); }, diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js index ae4ca3cde8..f1ac6d2900 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js @@ -48,6 +48,9 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica var self = this; + //we will use the default one for content if not specified + var rebindCallback = args.rebindCallback === undefined ? self.reBindChangedProperties : args.rebindCallback; + var deferred = $q.defer(); if (!args.scope.busy && formHelper.submitForm({ scope: args.scope, statusMessage: args.statusMessage })) { @@ -62,7 +65,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica self.handleSuccessfulSave({ scope: args.scope, savedContent: data, - rebindCallback: self.reBindChangedProperties(args.content, data) + rebindCallback: rebindCallback(args.content, data) }); args.scope.busy = false; @@ -72,7 +75,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica self.handleSaveError({ redirectOnFailure: true, err: err, - rebindCallback: self.reBindChangedProperties(args.content, err.data) + rebindCallback: rebindCallback(args.content, err.data) }); //show any notifications if (angular.isArray(err.data.notifications)) { @@ -91,6 +94,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica return deferred.promise; }, + /** Returns the action button definitions based on what permissions the user has. The content.allowedActions parameter contains a list of chars, each represents a button by permission so here we'll build the buttons according to the chars of the user. */ diff --git a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js index 008d0c2183..8b85201b29 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js @@ -487,6 +487,31 @@ angular.module('umbraco.services').factory('umbPropEditorHelper', umbPropEditorH function umbDataFormatter() { return { + formatContentTypePostData: function (displayModel, action) { + + //create the save model from the display model + var saveModel = _.pick(displayModel, + 'compositeContentTypes', 'isContainer', 'allowAsRoot', 'allowedTemplates', 'allowedContentTypes', + 'alias', 'description', 'thumbnail', 'name', 'id', 'icon', 'trashed', + 'key', 'parentId', 'alias', 'path'); + + //TODO: Map these + saveModel.allowedTemplates = _.map(displayModel.allowedTemplates, function (t) { return t.alias; }); + saveModel.defaultTemplate = displayModel.defaultTemplate.alias; + saveModel.groups = _.map(displayModel.groups, function (g) { + + var saveGroup = _.pick(g, 'inherited', 'id', 'sortOrder', 'name'); + var saveProperties = _.map(g.properties, function(p) { + var saveProperty = _.pick(p, 'id', 'alias', 'description', 'validation', 'label', 'sortOrder', 'dataTypeId', 'groupId'); + return saveProperty; + }); + + return saveGroup; + }); + + return saveModel; + }, + /** formats the display model used to display the data type to the model used to save the data type */ formatDataTypePostData: function(displayModel, preValues, action) { var saveModel = { diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js index 16cf0f4d84..edf8e25679 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js @@ -9,7 +9,7 @@ (function() { "use strict"; - function DocumentTypeEditController($scope, $routeParams, contentTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter) { + function DocumentTypeEditController($scope, $routeParams, contentTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter, $q) { var vm = this; @@ -142,54 +142,66 @@ function save() { - // validate form - if (formHelper.submitForm({ scope: $scope })) { + var deferred = $q.defer(); - formHelper.resetForm({ scope: $scope }); + vm.page.saveButtonState = "busy"; - // if form validates - perform save - performSave(); + // reformat allowed content types to array if id's + vm.contentType.allowedContentTypes = contentTypeHelper.createIdArray(vm.contentType.allowedContentTypes); - } + // update placeholder template information on new doc types + if (!$routeParams.notemplate && vm.contentType.id === 0) { + vm.contentType = contentTypeHelper.updateTemplatePlaceholder(vm.contentType); + } + + //contentTypeResource.save(vm.contentType).then(function(dt){ + + // formHelper.resetForm({ scope: $scope, notifications: dt.notifications }); + // contentEditingHelper.handleSuccessfulSave({ + // scope: $scope, + // savedContent: dt, + // rebindCallback: function() { + + // } + // }); + + // notificationsService.success("Document type save"); + // //post save logic here -the saved doctype returns as a new object + // init(dt); + + // syncTreeNode(vm.contentType, dt.path); + + // vm.page.saveButtonState = "success"; + + //}); + + contentEditingHelper.contentEditorPerformSave({ + statusMessage: "Saving...", + saveMethod: contentTypeResource.save, + scope: $scope, + content: vm.contentType + }).then(function (data) { + //success + syncTreeNode(vm.contentType, dt.path); + + vm.page.saveButtonState = "success"; + + deferred.resolve(data); + }, function (err) { + //error + if (err) { + editorState.set($scope.content); + } + + vm.page.saveButtonState = "error"; + + deferred.reject(err); + }); + + return deferred.promise; } - - function performSave() { - - vm.page.saveButtonState = "busy"; - - // reformat allowed content types to array if id's - vm.contentType.allowedContentTypes = contentTypeHelper.createIdArray(vm.contentType.allowedContentTypes); - - // update placeholder template information on new doc types - if (!$routeParams.notemplate && vm.contentType.id === 0) { - vm.contentType = contentTypeHelper.updateTemplatePlaceholder(vm.contentType); - } - - contentTypeResource.save(vm.contentType).then(function(dt){ - - formHelper.resetForm({ scope: $scope, notifications: dt.notifications }); - contentEditingHelper.handleSuccessfulSave({ - scope: $scope, - savedContent: dt, - rebindCallback: function() { - - } - }); - - notificationsService.success("Document type save"); - //post save logic here -the saved doctype returns as a new object - init(dt); - - syncTreeNode(vm.contentType, dt.path); - - vm.page.saveButtonState = "success"; - - }); - - } - - + function init(contentType){ // set all tab to inactive diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index 7c5ca0c5d6..9bd0ba50fd 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -88,81 +88,81 @@ namespace Umbraco.Web.Editors ? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id : Request.CreateValidationErrorResponse(result.Exception.Message); } - - public ContentTypeDisplay PostSave(ContentTypeDisplay contentType) + + public ContentTypeDisplay PostSave(ContentTypeSave contentTypeSave) { + var ctId = Convert.ToInt32(contentTypeSave.Id); + var ctService = Services.ContentTypeService; - //TODO: warn on content type alias conflicts - //TODO: warn on property alias conflicts - - //TODO: Validate the submitted model - - var ctId = Convert.ToInt32(contentType.Id); + if (ModelState.IsValid == false) + { + var ct = ctService.GetContentType(ctId); + //Required data is invalid so we cannot continue + var forDisplay = Mapper.Map(ct); + //map the 'save' data on top + forDisplay = Mapper.Map(contentTypeSave, forDisplay); + forDisplay.Errors = ModelState.ToErrorDictionary(); + throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay)); + } + //TODO: Deal with validation for composition with property and group names/aliases + //filter out empty properties - contentType.Groups = contentType.Groups.Where(x => x.Name.IsNullOrWhiteSpace() == false).ToList(); - foreach (var group in contentType.Groups) + contentTypeSave.Groups = contentTypeSave.Groups.Where(x => x.Name.IsNullOrWhiteSpace() == false).ToList(); + foreach (var group in contentTypeSave.Groups) { group.Properties = group.Properties.Where(x => x.Alias.IsNullOrWhiteSpace() == false).ToList(); } + //TODO: This all needs to be done in a transaction!! + // Which means that all of this logic needs to take place inside the service + if (ctId > 0) { //its an update to an existing - IContentType found = ctService.GetContentType(ctId); + var found = ctService.GetContentType(ctId); if (found == null) throw new HttpResponseException(HttpStatusCode.NotFound); - Mapper.Map(contentType, found); + Mapper.Map(contentTypeSave, found); ctService.Save(found); - - //map the saved item back to the content type (it should now get id etc set) - Mapper.Map(found, contentType); - return contentType; + + return Mapper.Map(found); } else { - //ensure alias is set - if (string.IsNullOrEmpty(contentType.Alias)) - contentType.Alias = contentType.Name.ToSafeAlias(); - //set id to null to ensure its handled as a new type - contentType.Id = null; - contentType.CreateDate = DateTime.Now; - contentType.UpdateDate = DateTime.Now; - - //TODO: This all needs to be done in a transaction!! - // Which means that all of this logic needs to take place inside the service - + contentTypeSave.Id = null; + contentTypeSave.CreateDate = DateTime.Now; + contentTypeSave.UpdateDate = DateTime.Now; + //create a default template if it doesnt exist -but only if default template is == to the content type - if (contentType.DefaultTemplate != null && contentType.DefaultTemplate.Alias == contentType.Alias) + //TODO: Is this really what we want? What if we don't want any template assigned at all ? + if (contentTypeSave.DefaultTemplate.IsNullOrWhiteSpace() == false && contentTypeSave.DefaultTemplate == contentTypeSave.Alias) { - var template = Services.FileService.GetTemplate(contentType.Alias); + var template = Services.FileService.GetTemplate(contentTypeSave.Alias); if (template == null) { - template = new Template(contentType.Name, contentType.Alias); + template = new Template(contentTypeSave.Name, contentTypeSave.Alias); Services.FileService.SaveTemplate(template); } - //make sure the template id is set on the default and allowed template - contentType.DefaultTemplate.Id = template.Id; - var found = contentType.AllowedTemplates.FirstOrDefault(x => x.Alias == contentType.Alias); - if (found != null) - found.Id = template.Id; + //make sure the template alias is set on the default and allowed template so we can map it back + contentTypeSave.DefaultTemplate = template.Alias; } //check if the type is trying to allow type 0 below itself - id zero refers to the currently unsaved type //always filter these 0 types out var allowItselfAsChild = false; - if (contentType.AllowedContentTypes != null) + if (contentTypeSave.AllowedContentTypes != null) { - allowItselfAsChild = contentType.AllowedContentTypes.Any(x => x == 0); - contentType.AllowedContentTypes = contentType.AllowedContentTypes.Where(x => x > 0).ToList(); + allowItselfAsChild = contentTypeSave.AllowedContentTypes.Any(x => x == 0); + contentTypeSave.AllowedContentTypes = contentTypeSave.AllowedContentTypes.Where(x => x > 0).ToList(); } //save as new - var newCt = Mapper.Map(contentType); + var newCt = Mapper.Map(contentTypeSave); ctService.Save(newCt); //we need to save it twice to allow itself under itself. @@ -171,10 +171,8 @@ namespace Umbraco.Web.Editors newCt.AddContentType(newCt); ctService.Save(newCt); } - - //map the saved item back to the content type (it should now get id etc set) - Mapper.Map(newCt, contentType); - return contentType; + + return Mapper.Map(newCt); } } @@ -254,6 +252,6 @@ namespace Umbraco.Web.Editors return basics; } - + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplayBase.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplayBase.cs index 3a13a85f78..87de7b435c 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplayBase.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplayBase.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.ComponentModel; using System.Runtime.Serialization; using Umbraco.Core.Models; @@ -30,6 +31,7 @@ namespace Umbraco.Web.Models.ContentEditing /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. /// [DataMember(Name = "notifications")] + [ReadOnly(true)] public List Notifications { get; private set; } /// @@ -43,6 +45,7 @@ namespace Umbraco.Web.Models.ContentEditing /// NOTE: The ProperCase is important because when we return ModeState normally it will always be proper case. /// [DataMember(Name = "ModelState")] + [ReadOnly(true)] public IDictionary Errors { get; set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeBasic.cs index b12233b1b3..13f8235f69 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeBasic.cs @@ -1,8 +1,10 @@ using System; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; using Umbraco.Core; using Umbraco.Core.IO; +using Umbraco.Core.Models.Validation; namespace Umbraco.Web.Models.ContentEditing { @@ -15,6 +17,14 @@ namespace Umbraco.Web.Models.ContentEditing [DataContract(Name = "contentType", Namespace = "")] public class ContentTypeBasic : EntityBasic { + /// + /// Overridden to apply our own validation attributes since this is not always required for other classes + /// + [Required] + [RegularExpression(@"^([a-zA-Z]\w.*)$", ErrorMessage = "Invalid alias")] + [DataMember(Name = "alias")] + public override string Alias { get; set; } + [DataMember(Name = "updateDate")] public DateTime UpdateDate { get; set; } @@ -31,6 +41,7 @@ namespace Umbraco.Web.Models.ContentEditing /// Returns true if the icon represents a CSS class instead of a file path /// [DataMember(Name = "iconIsClass")] + [ReadOnly(true)] public bool IconIsClass { get @@ -48,6 +59,7 @@ namespace Umbraco.Web.Models.ContentEditing /// Returns the icon file path if the icon is not a class, otherwise returns an empty string /// [DataMember(Name = "iconFilePath")] + [ReadOnly(true)] public string IconFilePath { get @@ -62,6 +74,7 @@ namespace Umbraco.Web.Models.ContentEditing /// Returns true if the icon represents a CSS class instead of a file path /// [DataMember(Name = "thumbnailIsClass")] + [ReadOnly(true)] public bool ThumbnailIsClass { get @@ -79,6 +92,7 @@ namespace Umbraco.Web.Models.ContentEditing /// Returns the icon file path if the icon is not a class, otherwise returns an empty string /// [DataMember(Name = "thumbnailFilePath")] + [ReadOnly(true)] public string ThumbnailFilePath { get diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeCompositionDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeCompositionDisplay.cs index 85be0670ca..16ff968cee 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeCompositionDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeCompositionDisplay.cs @@ -1,9 +1,12 @@ using System; using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; +using Umbraco.Core.Models.Validation; namespace Umbraco.Web.Models.ContentEditing { @@ -17,15 +20,17 @@ namespace Umbraco.Web.Models.ContentEditing AllowedContentTypes = new List(); CompositeContentTypes = new List(); AvailableCompositeContentTypes = new List(); + Notifications = new List(); } - //name, alias, icon, thumb, desc, inherited from basic - + //name, alias, icon, thumb, desc, inherited from basic + //List view [DataMember(Name = "isContainer")] public bool IsContainer { get; set; } [DataMember(Name = "listViewEditorName")] + [ReadOnly(true)] public string ListViewEditorName { get; set; } //Tabs @@ -41,9 +46,31 @@ namespace Umbraco.Web.Models.ContentEditing public IEnumerable CompositeContentTypes { get; set; } [DataMember(Name = "availableCompositeContentTypes")] + [ReadOnly(true)] public IEnumerable AvailableCompositeContentTypes { get; set; } [DataMember(Name = "allowAsRoot")] public bool AllowAsRoot { get; set; } + + /// + /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. + /// + [DataMember(Name = "notifications")] + [ReadOnly(true)] + public List Notifications { get; private set; } + + /// + /// This is used for validation of a content item. + /// + /// + /// A content item can be invalid but still be saved. This occurs when there's property validation errors, we will + /// still save the item but it cannot be published. So we need a way of returning validation errors as well as the + /// updated model. + /// + /// NOTE: The ProperCase is important because when we return ModeState normally it will always be proper case. + /// + [DataMember(Name = "ModelState")] + [ReadOnly(true)] + public IDictionary Errors { get; set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs index 7fb77edc46..e0165450ed 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Runtime.Serialization; using System.Text; @@ -7,6 +8,7 @@ using System.Threading.Tasks; namespace Umbraco.Web.Models.ContentEditing { + [DataContract(Name = "contentType", Namespace = "")] public class ContentTypeDisplay : ContentTypeCompositionDisplay { diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs new file mode 100644 index 0000000000..15cc1d6785 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Runtime.Serialization; +using Umbraco.Core; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "contentType", Namespace = "")] + public class ContentTypeSave : ContentTypeBasic, IValidatableObject + { + public ContentTypeSave() + { + //initialize collections so at least their never null + Groups = new List>(); + AllowedContentTypes = new List(); + CompositeContentTypes = new List(); + } + + //Compositions + [DataMember(Name = "compositeContentTypes")] + public IEnumerable CompositeContentTypes { get; set; } + + [DataMember(Name = "isContainer")] + public bool IsContainer { get; set; } + + [DataMember(Name = "allowAsRoot")] + public bool AllowAsRoot { get; set; } + + /// + /// The list of allowed templates to assign (template alias) + /// + [DataMember(Name = "allowedTemplates")] + public IEnumerable AllowedTemplates { get; set; } + + //Allowed child types + [DataMember(Name = "allowedContentTypes")] + public IEnumerable AllowedContentTypes { get; set; } + + /// + /// The default template to assign (template alias) + /// + [DataMember(Name = "defaultTemplate")] + public string DefaultTemplate { get; set; } + + //Tabs + [DataMember(Name = "groups")] + public IEnumerable> Groups { get; set; } + + /// + /// Custom validation + /// + /// + /// + public IEnumerable Validate(ValidationContext validationContext) + { + if (AllowedTemplates.Any(x => x.IsNullOrWhiteSpace())) + yield return new ValidationResult("Template value cannot be null", new[] {"AllowedTemplates"}); + + if (CompositeContentTypes.Any(x => x.IsNullOrWhiteSpace())) + yield return new ValidationResult("Composite Content Type value cannot be null", new[] { "CompositeContentTypes" }); + + if (Groups.GroupBy(x => x.Name).Any(x => x.Count() > 1)) + yield return new ValidationResult("Duplicate group names not allowed", new[] { "Groups" }); + + if (Groups.SelectMany(x => x.Properties).Where(x => x.Inherited == false).GroupBy(x => x.Alias).Any(x => x.Count() > 1)) + yield return new ValidationResult("Duplicate property aliases not allowed", new[] { "Groups" }); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs b/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs index 520ff677b0..40d884d653 100644 --- a/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.Serialization; @@ -29,6 +30,7 @@ namespace Umbraco.Web.Models.ContentEditing public string Icon { get; set; } [DataMember(Name = "trashed")] + [ReadOnly(true)] public bool Trashed { get; set; } /// @@ -44,8 +46,11 @@ namespace Umbraco.Web.Models.ContentEditing /// /// This will only be populated for some entities like macros /// + /// + /// This is overrideable to specify different validation attributes if required + /// [DataMember(Name = "alias")] - public string Alias { get; set; } + public virtual string Alias { get; set; } /// /// The path of the entity @@ -57,6 +62,7 @@ namespace Umbraco.Web.Models.ContentEditing /// A collection of extra data that is available for this specific entity/entity type /// [DataMember(Name = "metaData")] + [ReadOnly(true)] public IDictionary AdditionalData { get; private set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyGroupBasic.cs b/src/Umbraco.Web/Models/ContentEditing/PropertyGroupBasic.cs new file mode 100644 index 0000000000..d942c433ce --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/PropertyGroupBasic.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "propertyGroup", Namespace = "")] + public class PropertyGroupBasic + where TPropertyType: PropertyTypeBasic + { + public PropertyGroupBasic() + { + Properties = new List(); + } + + //Indicate if this tab was inherited + [DataMember(Name = "inherited")] + public bool Inherited { get; set; } + + //TODO: Required ? + [DataMember(Name = "id")] + public int Id { get; set; } + + [DataMember(Name = "properties")] + public IEnumerable Properties { get; set; } + + [DataMember(Name = "sortOrder")] + public int SortOrder { get; set; } + + [Required] + [DataMember(Name = "name")] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyGroupDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/PropertyGroupDisplay.cs index 091ebc8484..49a3b84261 100644 --- a/src/Umbraco.Web/Models/ContentEditing/PropertyGroupDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/PropertyGroupDisplay.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Runtime.Serialization; using System.Text; @@ -8,7 +9,7 @@ using System.Threading.Tasks; namespace Umbraco.Web.Models.ContentEditing { [DataContract(Name = "propertyGroup", Namespace = "")] - public class PropertyGroupDisplay + public class PropertyGroupDisplay : PropertyGroupBasic { public PropertyGroupDisplay() { @@ -16,33 +17,22 @@ namespace Umbraco.Web.Models.ContentEditing ParentTabContentTypeNames = new List(); ParentTabContentTypes = new List(); } - - [DataMember(Name = "id")] - public int Id { get; set; } - + [DataMember(Name = "parentGroupId")] + [ReadOnly(true)] public int ParentGroupId { get; set; } - - [DataMember(Name = "sortOrder")] - public int SortOrder { get; set; } - - [DataMember(Name = "name")] - public string Name { get; set; } - - [DataMember(Name = "properties")] - public IEnumerable Properties { get; set; } - - //Indicate if this tab was inherited - [DataMember(Name = "inherited")] - public bool Inherited { get; set; } - + + //SD: Seems strange that this is required [DataMember(Name = "contentTypeId")] + [ReadOnly(true)] public int ContentTypeId { get; set; } [DataMember(Name = "parentTabContentTypes")] + [ReadOnly(true)] public IEnumerable ParentTabContentTypes { get; set; } [DataMember(Name = "parentTabContentTypeNames")] + [ReadOnly(true)] public IEnumerable ParentTabContentTypeNames { get; set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeBasic.cs b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeBasic.cs new file mode 100644 index 0000000000..4332a93436 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeBasic.cs @@ -0,0 +1,41 @@ +using System.ComponentModel.DataAnnotations; +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "propertyType")] + public class PropertyTypeBasic + { + //indicates if this property was inherited + [DataMember(Name = "inherited")] + public bool Inherited { get; set; } + + //TODO: Required ? + [DataMember(Name = "id")] + public int Id { get; set; } + + [Required] + [RegularExpression(@"^([a-zA-Z]\w.*)$", ErrorMessage = "Invalid alias")] + [DataMember(Name = "alias")] + public string Alias { get; set; } + + [DataMember(Name = "description")] + public string Description { get; set; } + + [DataMember(Name = "validation")] + public PropertyTypeValidation Validation { get; set; } + + [DataMember(Name = "label")] + public string Label { get; set; } + + [DataMember(Name = "sortOrder")] + public int SortOrder { get; set; } + + [DataMember(Name = "dataTypeId")] + public int DataTypeId { get; set; } + + //SD: Is this really required ? + [DataMember(Name = "groupId")] + public int GroupId { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs index bcf939c76a..a05924ad5c 100644 --- a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Runtime.Serialization; using System.Text; @@ -8,52 +9,28 @@ using System.Threading.Tasks; namespace Umbraco.Web.Models.ContentEditing { [DataContract(Name = "propertyType")] - public class PropertyTypeDisplay - { - [DataMember(Name = "id")] - public int Id { get; set; } - - [DataMember(Name = "alias")] - public string Alias { get; set; } - - [DataMember(Name = "description")] - public string Description { get; set; } - + public class PropertyTypeDisplay : PropertyTypeBasic + { [DataMember(Name = "editor")] + [ReadOnly(true)] public string Editor { get; set; } - - [DataMember(Name = "validation")] - public PropertyTypeValidation Validation { get; set; } - - [DataMember(Name = "label")] - public string Label { get; set; } - + [DataMember(Name = "view")] + [ReadOnly(true)] public string View { get; set; } [DataMember(Name = "config")] + [ReadOnly(true)] public IDictionary Config { get; set; } - - [DataMember(Name = "value")] - public string Value { get; set; } - - [DataMember(Name = "sortOrder")] - public int SortOrder { get; set; } - - //indicates if this property was inherited - [DataMember(Name = "inherited")] - public bool Inherited { get; set; } - - [DataMember(Name = "dataTypeId")] - public int DataTypeId { get; set; } - - [DataMember(Name = "groupId")] - public int GroupId { get; set; } - + + //SD: Seems strange that this is needed [DataMember(Name = "contentTypeId")] + [ReadOnly(true)] public int ContentTypeId { get; set; } + //SD: Seems strange that this is needed [DataMember(Name = "contentTypeName")] + [ReadOnly(true)] public string ContentTypeName { get; set; } } } diff --git a/src/Umbraco.Web/Models/Mapping/AvailableCompositeContentTypesResolver.cs b/src/Umbraco.Web/Models/Mapping/AvailableCompositeContentTypesResolver.cs index dacd2ab0a7..9cc415d27a 100644 --- a/src/Umbraco.Web/Models/Mapping/AvailableCompositeContentTypesResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/AvailableCompositeContentTypesResolver.cs @@ -12,12 +12,11 @@ namespace Umbraco.Web.Models.Mapping { internal class AvailableCompositeContentTypesResolver : ValueResolver> { - private ApplicationContext _context; - private bool _mediaType; - internal AvailableCompositeContentTypesResolver(ApplicationContext context, bool mediaType = false) + private readonly ApplicationContext _context; + + internal AvailableCompositeContentTypesResolver(ApplicationContext context) { _context = context; - _mediaType = mediaType; } protected override IEnumerable ResolveCore(IContentTypeComposition source) @@ -27,19 +26,19 @@ namespace Umbraco.Web.Models.Mapping var s = source; var type = _context.Services.EntityService.GetObjectType(source.Id); - IContentTypeComposition[] allContentTypes = new IContentTypeComposition[0]; + var allContentTypes = new IContentTypeComposition[0]; switch (type) { - case UmbracoObjectTypes.DocumentType: + case UmbracoObjectTypes.DocumentType: allContentTypes = _context.Services.ContentTypeService.GetAllContentTypes().Cast().ToArray(); break; - case UmbracoObjectTypes.MediaType: + case UmbracoObjectTypes.MediaType: allContentTypes = _context.Services.ContentTypeService.GetAllMediaTypes().Cast().ToArray(); break; - case UmbracoObjectTypes.MemberType: + case UmbracoObjectTypes.MemberType: allContentTypes = _context.Services.MemberTypeService.GetAll().Cast().ToArray(); break; } @@ -56,42 +55,40 @@ namespace Umbraco.Web.Models.Mapping //if already in use a composition, do not allow any composited types return new List(); } - else - { - // if it is not used then composition is possible - // hashset guarantees unicity on Id - var list = new HashSet(new DelegateEqualityComparer( - (x, y) => x.Id == y.Id, - x => x.Id)); - // usable types are those that are top-level - var usableContentTypes = allContentTypes - .Where(x => x.ContentTypeComposition.Any() == false).ToArray(); - foreach (var x in usableContentTypes) - list.Add(x); + // if it is not used then composition is possible + // hashset guarantees unicity on Id + var list = new HashSet(new DelegateEqualityComparer( + (x, y) => x.Id == y.Id, + x => x.Id)); - // indirect types are those that we use, directly or indirectly - var indirectContentTypes = GetIndirect(source).ToArray(); - foreach (var x in indirectContentTypes) - list.Add(x); + // usable types are those that are top-level + var usableContentTypes = allContentTypes + .Where(x => x.ContentTypeComposition.Any() == false).ToArray(); + foreach (var x in usableContentTypes) + list.Add(x); - // directContentTypes are those we use directly - // they are already in indirectContentTypes, no need to add to the list - var directContentTypes = source.ContentTypeComposition.ToArray(); + // indirect types are those that we use, directly or indirectly + var indirectContentTypes = GetIndirect(source).ToArray(); + foreach (var x in indirectContentTypes) + list.Add(x); - var enabled = usableContentTypes.Select(x => x.Id) // those we can use - .Except(indirectContentTypes.Select(x => x.Id)) // except those that are indirectly used - .Union(directContentTypes.Select(x => x.Id)) // but those that are directly used - .Where(x => x != source.ParentId) // but not the parent - .Distinct() - .ToArray(); + //// directContentTypes are those we use directly + //// they are already in indirectContentTypes, no need to add to the list + //var directContentTypes = source.ContentTypeComposition.ToArray(); - var wtf = new List(); - foreach (var contentType in list.OrderBy(x => x.Name).Where(x => x.Id != source.Id)) - wtf.Add(Mapper.Map(contentType)); + //var enabled = usableContentTypes.Select(x => x.Id) // those we can use + // .Except(indirectContentTypes.Select(x => x.Id)) // except those that are indirectly used + // .Union(directContentTypes.Select(x => x.Id)) // but those that are directly used + // .Where(x => x != source.ParentId) // but not the parent + // .Distinct() + // .ToArray(); - return wtf; - } + return list + .Where(x => x.Id != source.Id) + .OrderBy(x => x.Name) + .Select(Mapper.Map) + .ToList(); } diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs index 1d7fbd4cb3..74d5c96236 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading; using AutoMapper; @@ -20,7 +19,7 @@ namespace Umbraco.Web.Models.Mapping internal class ContentTypeModelMapper : MapperConfiguration { private readonly Lazy _propertyEditorResolver; - + //default ctor public ContentTypeModelMapper() { @@ -35,102 +34,35 @@ namespace Umbraco.Web.Models.Mapping public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext) { - config.CreateMap() - .Include() - .Include() - .Include() + + config.CreateMap() + .ConstructUsing(basic => new PropertyType(applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(basic.DataTypeId))) + .ForMember(type => type.ValidationRegExp, expression => expression.ResolveUsing(basic => basic.Validation.Pattern)) + .ForMember(type => type.Mandatory, expression => expression.ResolveUsing(basic => basic.Validation.Mandatory)) + .ForMember(type => type.Name, expression => expression.ResolveUsing(basic => basic.Label)) + .ForMember(type => type.DataTypeDefinitionId, expression => expression.ResolveUsing(basic => basic.DataTypeId)) + .ForMember(type => type.DataTypeId, expression => expression.Ignore()) + .ForMember(type => type.PropertyEditorAlias, expression => expression.Ignore()) + .ForMember(type => type.HelpText, expression => expression.Ignore()) + .ForMember(type => type.Key, expression => expression.Ignore()) + .ForMember(type => type.CreateDate, expression => expression.Ignore()) + .ForMember(type => type.UpdateDate, expression => expression.Ignore()) + .ForMember(type => type.HasIdentity, expression => expression.Ignore()); - //only map id if set to something higher then zero - .ForMember(dto => dto.Id, expression => expression.Condition(display => (Convert.ToInt32(display.Id) > 0))) - .ForMember(dto => dto.Id, expression => expression.MapFrom(display => Convert.ToInt32(display.Id))) - - .ForMember(dto => dto.AllowedAsRoot, expression => expression.MapFrom(display => display.AllowAsRoot)) - .ForMember(dto => dto.CreatorId, expression => expression.Ignore()) - .ForMember(dto => dto.Level, expression => expression.Ignore()) - .ForMember(dto => dto.SortOrder, expression => expression.Ignore()) - .ForMember( - dto => dto.AllowedContentTypes, - expression => expression.MapFrom(dto => dto.AllowedContentTypes.Select( (t, i) => new ContentTypeSort(t, i) ))) - - //ignore, we'll do this in after map - .ForMember(dto => dto.PropertyGroups, expression => expression.Ignore()) - - .AfterMap((source, dest) => - { - - var addedProperties = new List(); - - //get all properties from groups that are not generic properties or inhertied (-666 id) - var selfNonGenericGroups = source.Groups.Where(x => x.Inherited == false && x.Id != -666).ToArray(); - - foreach (var groupDisplay in selfNonGenericGroups) - { - //use underlying logic to add the property group which should wire most things up for us - dest.AddPropertyGroup(groupDisplay.Name); - - //now update that group with the values from the display object - Mapper.Map(groupDisplay, dest.PropertyGroups[groupDisplay.Name]); - - foreach (var propertyTypeDisplay in groupDisplay.Properties.Where(x => x.Inherited == false)) - { - //update existing - if(propertyTypeDisplay.Id > 0) - { - var currentPropertyType = dest.PropertyTypes.FirstOrDefault(x => x.Id == propertyTypeDisplay.Id); - Mapper.Map(propertyTypeDisplay, currentPropertyType); - }else - {//add new - var mapped = Mapper.Map(propertyTypeDisplay); - dest.AddPropertyType(mapped, groupDisplay.Name); - } - - addedProperties.Add(propertyTypeDisplay.Alias); - } - } - - //Groups to remove - var groupsToRemove = dest.PropertyGroups.Select(x => x.Name).Except(selfNonGenericGroups.Select(x => x.Name)).ToArray(); - foreach (var toRemove in groupsToRemove) - { - dest.RemovePropertyGroup(toRemove); - } - - //add generic properties - var genericProperties = source.Groups.FirstOrDefault(x => x.Id == -666); - if(genericProperties != null) - { - foreach (var propertyTypeDisplay in genericProperties.Properties.Where(x => x.Inherited == false)) - { - dest.AddPropertyType(Mapper.Map(propertyTypeDisplay)); - addedProperties.Add(propertyTypeDisplay.Alias); - } - } - - //remove deleted types - foreach(var removedType in dest.PropertyTypes - .Where(x => addedProperties.Contains(x.Alias) == false).ToList()) - { - dest.RemovePropertyType(removedType.Alias); - } - - - }); - - config.CreateMap() + config.CreateMap() + //do the base mapping + .MapBaseContentTypeSaveToEntity(applicationContext) .ConstructUsing((source) => new ContentType(source.ParentId)) - .ForMember(dto => dto.Id, expression => expression.Ignore()) - .ForMember(dto => dto.AllowedTemplates, expression => expression.Ignore()) + .ForMember( + dto => dto.AllowedTemplates, + expression => expression.ResolveUsing(basic => basic.AllowedTemplates.Where(x => x != null) + .Select(s => applicationContext.Services.FileService.GetTemplate(s)))) .ForMember(dto => dto.DefaultTemplate, expression => expression.Ignore()) - .AfterMap((source, dest) => { - //sync templates - dest.AllowedTemplates = source.AllowedTemplates.Where(x => x != null).Select(x => Mapper.Map(x)); - if (source.DefaultTemplate != null) - dest.SetDefaultTemplate(Mapper.Map(source.DefaultTemplate)); - + dest.SetDefaultTemplate(applicationContext.Services.FileService.GetTemplate(source.DefaultTemplate)); //sync compositions var current = dest.CompositionAliases().ToArray(); @@ -154,88 +86,97 @@ namespace Umbraco.Web.Models.Mapping } }); - config.CreateMap() + config.CreateMap() + .ForMember(dto => dto.AllowedTemplates, expression => expression.Ignore()) + .ForMember(dto => dto.DefaultTemplate, expression => expression.Ignore()) + .ForMember(dto => dto.ListViewEditorName, expression => expression.Ignore()) + .ForMember(dto => dto.AvailableCompositeContentTypes, expression => expression.Ignore()) + .ForMember(dto => dto.Notifications, expression => expression.Ignore()) + .ForMember(dto => dto.Errors, expression => expression.Ignore()) .AfterMap((source, dest) => - { - - //sync compositions - var current = dest.CompositionAliases().ToArray(); - var proposed = source.CompositeContentTypes; + { + //sync templates + dest.AllowedTemplates = source.AllowedTemplates.Select(Mapper.Map); - var remove = current.Where(x => proposed.Contains(x) == false); - var add = proposed.Where(x => current.Contains(x) == false); + if (source.DefaultTemplate.IsNullOrWhiteSpace() == false) + { + //if the dest is set and it's the same as the source, then don't change + if (dest.DefaultTemplate == null || source.DefaultTemplate != dest.DefaultTemplate.Alias) + { + var template = applicationContext.Services.FileService.GetTemplate(source.DefaultTemplate); + dest.DefaultTemplate = template == null ? null : Mapper.Map(template); + } + } + else + { + dest.DefaultTemplate = null; + } + }); - foreach (var rem in remove) - dest.RemoveContentType(rem); - - foreach (var a in add) - { - //TODO: Remove N+1 lookup - var addCt = applicationContext.Services.MemberTypeService.Get(a); - if (addCt != null) - dest.AddContentType(addCt); - } - }); + //config.CreateMap() + // //do the base mapping + // .MapBaseContentTypeSaveToEntity(applicationContext) + // .AfterMap((source, dest) => + // { + + // //sync compositions + // var current = dest.CompositionAliases().ToArray(); + // var proposed = source.CompositeContentTypes; + + // var remove = current.Where(x => proposed.Contains(x) == false); + // var add = proposed.Where(x => current.Contains(x) == false); + + // foreach (var rem in remove) + // dest.RemoveContentType(rem); + + // foreach (var a in add) + // { + // //TODO: Remove N+1 lookup + // var addCt = applicationContext.Services.MemberTypeService.Get(a); + // if (addCt != null) + // dest.AddContentType(addCt); + // } + // }); - config.CreateMap() - .AfterMap((source, dest) => - { - //sync compositions - var current = dest.CompositionAliases().ToArray(); - var proposed = source.CompositeContentTypes; + //config.CreateMap() + // //do the base mapping + // .MapBaseContentTypeSaveToEntity(applicationContext) + // .AfterMap((source, dest) => + // { + // //sync compositions + // var current = dest.CompositionAliases().ToArray(); + // var proposed = source.CompositeContentTypes; - var remove = current.Where(x => proposed.Contains(x) == false); - var add = proposed.Where(x => current.Contains(x) == false); + // var remove = current.Where(x => proposed.Contains(x) == false); + // var add = proposed.Where(x => current.Contains(x) == false); + + // foreach (var rem in remove) + // dest.RemoveContentType(rem); + + // foreach (var a in add) + // { + // //TODO: Remove N+1 lookup + // var addCt = applicationContext.Services.ContentTypeService.GetMediaType(a); + // if (addCt != null) + // dest.AddContentType(addCt); + // } + // }); - foreach (var rem in remove) - dest.RemoveContentType(rem); - - foreach (var a in add) - { - //TODO: Remove N+1 lookup - var addCt = applicationContext.Services.ContentTypeService.GetMediaType(a); - if (addCt != null) - dest.AddContentType(addCt); - } - }); - config.CreateMap().ConvertUsing(x => x.Alias); - config.CreateMap() - .Include() - .Include() - .Include() + config.CreateMap() + //map base logic + .MapBaseContentTypeEntityToDisplay(applicationContext, _propertyEditorResolver); - .ForMember(display => display.AllowAsRoot, expression => expression.MapFrom(type => type.AllowedAsRoot)) - .ForMember(display => display.ListViewEditorName, expression => expression.Ignore()) - //Ignore because this is not actually used for content types - .ForMember(display => display.Trashed, expression => expression.Ignore()) - - .ForMember( - dto => dto.AllowedContentTypes, - expression => expression.MapFrom(dto => dto.AllowedContentTypes.Select(x => x.Id.Value))) - - .ForMember( - dto => dto.AvailableCompositeContentTypes, - expression => expression.ResolveUsing(new AvailableCompositeContentTypesResolver(applicationContext))) - - .ForMember( - dto => dto.CompositeContentTypes, - expression => expression.MapFrom(dto => dto.ContentTypeComposition)) - - .ForMember( - dto => dto.Groups, - expression => expression.ResolveUsing(new PropertyTypeGroupResolver(applicationContext, _propertyEditorResolver))); - - - config.CreateMap(); config.CreateMap() + //map base logic + .MapBaseContentTypeEntityToDisplay(applicationContext, _propertyEditorResolver) .AfterMap((source, dest) => { - + //default listview dest.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Media"; @@ -248,9 +189,11 @@ namespace Umbraco.Web.Models.Mapping }); config.CreateMap() + //map base logic + .MapBaseContentTypeEntityToDisplay(applicationContext, _propertyEditorResolver) .ForMember(dto => dto.AllowedTemplates, expression => expression.Ignore()) .ForMember(dto => dto.DefaultTemplate, expression => expression.Ignore()) - + .ForMember(display => display.Notifications, expression => expression.Ignore()) .AfterMap((source, dest) => { //sync templates @@ -261,7 +204,7 @@ namespace Umbraco.Web.Models.Mapping //default listview dest.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Content"; - + if (string.IsNullOrEmpty(source.Name) == false) { var name = Constants.Conventions.DataTypes.ListViewPrefix + source.Name; @@ -276,25 +219,28 @@ namespace Umbraco.Web.Models.Mapping config.CreateMap(); - config.CreateMap() + config.CreateMap, PropertyGroup>() .ForMember(dest => dest.Id, expression => expression.Condition(source => source.Id > 0)) .ForMember(g => g.Key, expression => expression.Ignore()) .ForMember(g => g.HasIdentity, expression => expression.Ignore()) .ForMember(dto => dto.CreateDate, expression => expression.Ignore()) .ForMember(dto => dto.UpdateDate, expression => expression.Ignore()) - //only map if a parent is actually set - .ForMember(g => g.ParentId, expression => expression.Condition(display => display.ParentGroupId > 0)) - .ForMember(g => g.ParentId, expression => expression.MapFrom(display => display.ParentGroupId)) + ////only map if a parent is actually set + //.ForMember(g => g.ParentId, expression => expression.Condition(display => display.ParentGroupId > 0)) + //.ForMember(g => g.ParentId, expression => expression.MapFrom(display => display.ParentGroupId)) //ignore these, this is handled with IContentType.AddPropertyType - .ForMember(g => g.PropertyTypes, expression => expression.Ignore()); + .ForMember(g => g.PropertyTypes, expression => expression.MapFrom(display => display.Properties.Select(Mapper.Map))); - config.CreateMap() - - .ConstructUsing((PropertyTypeDisplay propertyTypeDisplay) => + config.CreateMap, PropertyGroupDisplay>() + .ForMember(g => g.Properties, expression => expression.MapFrom(display => display.Properties.Select(Mapper.Map))); + + config.CreateMap() + + .ConstructUsing((PropertyTypeBasic propertyTypeBasic) => { - var dataType = applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(propertyTypeDisplay.DataTypeId); - if (dataType == null) throw new NullReferenceException("No data type found with id " + propertyTypeDisplay.DataTypeId); - return new PropertyType(dataType, propertyTypeDisplay.Alias); + var dataType = applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(propertyTypeBasic.DataTypeId); + if (dataType == null) throw new NullReferenceException("No data type found with id " + propertyTypeBasic.DataTypeId); + return new PropertyType(dataType, propertyTypeBasic.Alias); }) //only map if it is actually set @@ -314,11 +260,12 @@ namespace Umbraco.Web.Models.Mapping .ForMember(type => type.DataTypeId, expression => expression.Ignore()) .ForMember(type => type.Mandatory, expression => expression.MapFrom(display => display.Validation.Mandatory)) .ForMember(type => type.ValidationRegExp, expression => expression.MapFrom(display => display.Validation.Pattern)) - .ForMember(type => type.PropertyEditorAlias, expression => expression.MapFrom(display => display.Editor)) .ForMember(type => type.DataTypeDefinitionId, expression => expression.MapFrom(display => display.DataTypeId)) .ForMember(type => type.Name, expression => expression.MapFrom(display => display.Label)); + + config.CreateMap(); } - + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapperExtensions.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapperExtensions.cs new file mode 100644 index 0000000000..96b286b8fe --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapperExtensions.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// Used as a shared way to do the underlying mapping for content types base classes + /// + /// + /// We used to use 'Include' Automapper inheritance functionality and although this works, the unit test + /// to assert mappings fails which is an Automapper bug. So instead we will use an extension method for the mappings + /// to re-use mappings. + /// + internal static class ContentTypeModelMapperExtensions + { + public static IMappingExpression MapBaseContentTypeEntityToDisplay( + this IMappingExpression mapping, ApplicationContext applicationContext, Lazy propertyEditorResolver) + where TSource : IContentTypeComposition + where TDestination : ContentTypeCompositionDisplay + { + return mapping + .ForMember(display => display.Notifications, expression => expression.Ignore()) + .ForMember(display => display.Errors, expression => expression.Ignore()) + .ForMember(display => display.AllowAsRoot, expression => expression.MapFrom(type => type.AllowedAsRoot)) + .ForMember(display => display.ListViewEditorName, expression => expression.Ignore()) + //Ignore because this is not actually used for content types + .ForMember(display => display.Trashed, expression => expression.Ignore()) + + .ForMember( + dto => dto.AllowedContentTypes, + expression => expression.MapFrom(dto => dto.AllowedContentTypes.Select(x => x.Id.Value))) + + .ForMember( + dto => dto.AvailableCompositeContentTypes, + expression => expression.ResolveUsing(new AvailableCompositeContentTypesResolver(applicationContext))) + + .ForMember( + dto => dto.CompositeContentTypes, + expression => expression.MapFrom(dto => dto.ContentTypeComposition)) + + .ForMember( + dto => dto.Groups, + expression => expression.ResolveUsing(new PropertyTypeGroupResolver(applicationContext, propertyEditorResolver))); + } + + /// + /// Display -> Entity class base mapping logic + /// + /// + /// + /// + /// + /// + public static IMappingExpression MapBaseContentTypeSaveToEntity( + this IMappingExpression mapping, ApplicationContext applicationContext) + //where TSource : ContentTypeCompositionDisplay + where TSource : ContentTypeSave + where TDestination : IContentTypeComposition + { + return mapping + //only map id if set to something higher then zero + .ForMember(dto => dto.Id, expression => expression.Condition(display => (Convert.ToInt32(display.Id) > 0))) + .ForMember(dto => dto.Id, expression => expression.MapFrom(display => Convert.ToInt32(display.Id))) + + .ForMember(dto => dto.AllowedAsRoot, expression => expression.MapFrom(display => display.AllowAsRoot)) + .ForMember(dto => dto.CreatorId, expression => expression.Ignore()) + .ForMember(dto => dto.Level, expression => expression.Ignore()) + .ForMember(dto => dto.SortOrder, expression => expression.Ignore()) + //ignore, we'll do this in after map + .ForMember(dto => dto.PropertyGroups, expression => expression.Ignore()) + + .ForMember( + dto => dto.AllowedContentTypes, + expression => expression.MapFrom(dto => dto.AllowedContentTypes.Select((t, i) => new ContentTypeSort(t, i)))) + + .AfterMap((source, dest) => + { + + var addedProperties = new List(); + + //get all properties from groups that are not generic properties or inhertied (-666 id) + var selfNonGenericGroups = source.Groups.Where(x => x.Inherited == false && x.Id != -666).ToArray(); + + foreach (var group in selfNonGenericGroups) + { + //use underlying logic to add the property group which should wire most things up for us + dest.AddPropertyGroup(group.Name); + + //now update that group with the values from the display object + Mapper.Map(group, dest.PropertyGroups[group.Name]); + + foreach (var propType in group.Properties.Where(x => x.Inherited == false)) + { + //update existing + if (propType.Id > 0) + { + var currentPropertyType = dest.PropertyTypes.FirstOrDefault(x => x.Id == propType.Id); + Mapper.Map(propType, currentPropertyType); + } + else + { + //add new + var mapped = Mapper.Map(propType); + dest.AddPropertyType(mapped, group.Name); + } + + addedProperties.Add(propType.Alias); + } + } + + //Groups to remove + var groupsToRemove = dest.PropertyGroups.Select(x => x.Name).Except(selfNonGenericGroups.Select(x => x.Name)).ToArray(); + foreach (var toRemove in groupsToRemove) + { + dest.RemovePropertyGroup(toRemove); + } + + //add generic properties + var genericProperties = source.Groups.FirstOrDefault(x => x.Id == -666); + if (genericProperties != null) + { + foreach (var propertyTypeBasic in genericProperties.Properties.Where(x => x.Inherited == false)) + { + dest.AddPropertyType(Mapper.Map(propertyTypeBasic)); + addedProperties.Add(propertyTypeBasic.Alias); + } + } + + //remove deleted types + foreach (var removedType in dest.PropertyTypes + .Where(x => addedProperties.Contains(x.Alias) == false).ToList()) + { + dest.RemovePropertyType(removedType.Alias); + } + + + }); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs index 0b3327a553..c451e1a9bd 100644 --- a/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs @@ -35,6 +35,7 @@ namespace Umbraco.Web.Models.Mapping }; config.CreateMap() + .ForMember(x => x.HasPrevalues, expression => expression.Ignore()) .ForMember(x => x.IsSystemDataType, expression => expression.Ignore()) .ForMember(x => x.Id, expression => expression.Ignore()) .ForMember(x => x.Trashed, expression => expression.Ignore()) @@ -44,6 +45,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(x => x.AdditionalData, expression => expression.Ignore()); config.CreateMap() + .ForMember(x => x.HasPrevalues, expression => expression.Ignore()) .ForMember(x => x.Icon, expression => expression.Ignore()) .ForMember(x => x.Alias, expression => expression.Ignore()) .ForMember(x => x.Group, expression => expression.Ignore()) @@ -65,6 +67,7 @@ namespace Umbraco.Web.Models.Mapping new PreValueDisplayResolver(lazyDataTypeService))) .ForMember(display => display.SelectedEditor, expression => expression.MapFrom( definition => definition.PropertyEditorAlias.IsNullOrWhiteSpace() ? null : definition.PropertyEditorAlias)) + .ForMember(x => x.HasPrevalues, expression => expression.Ignore()) .ForMember(x => x.Notifications, expression => expression.Ignore()) .ForMember(x => x.Icon, expression => expression.Ignore()) .ForMember(x => x.Alias, expression => expression.Ignore()) diff --git a/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs b/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs index 44925b5a7f..5610a70008 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs @@ -52,18 +52,18 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dto => dto.Trashed, expression => expression.Ignore()) .ForMember(x => x.AdditionalData, expression => expression.Ignore()); - config.CreateMap() - .ConstructUsing(basic => new Template(basic.Name, basic.Alias) - { - Id = Convert.ToInt32(basic.Id), - Key = basic.Key - }) - .ForMember(t => t.Path, expression => expression.Ignore()) - .ForMember(t => t.Id, expression => expression.MapFrom(template => Convert.ToInt32(template.Id))) - .ForMember(x => x.VirtualPath, expression => expression.Ignore()) - .ForMember(x => x.CreateDate, expression => expression.Ignore()) - .ForMember(x => x.UpdateDate, expression => expression.Ignore()) - .ForMember(x => x.Content, expression => expression.Ignore()); + //config.CreateMap() + // .ConstructUsing(basic => new Template(basic.Name, basic.Alias) + // { + // Id = Convert.ToInt32(basic.Id), + // Key = basic.Key + // }) + // .ForMember(t => t.Path, expression => expression.Ignore()) + // .ForMember(t => t.Id, expression => expression.MapFrom(template => Convert.ToInt32(template.Id))) + // .ForMember(x => x.VirtualPath, expression => expression.Ignore()) + // .ForMember(x => x.CreateDate, expression => expression.Ignore()) + // .ForMember(x => x.UpdateDate, expression => expression.Ignore()) + // .ForMember(x => x.Content, expression => expression.Ignore()); config.CreateMap() .ForMember(x => x.Id, expression => expression.MapFrom(entity => new Lazy(() => Convert.ToInt32(entity.Id)))) diff --git a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs index ae300a824e..2802fe2965 100644 --- a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs @@ -176,7 +176,7 @@ namespace Umbraco.Web.Models.Mapping Label = p.Name, View = editor.ValueEditor.View, Config = editor.PreValueEditor.ConvertDbToEditor(editor.DefaultPreValues, preVals) , - Value = "", + //Value = "", ContentTypeId = contentType.Id, ContentTypeName = contentType.Name, GroupId = groupId, diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 3dba2dba9d..1f1f5585c7 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -304,7 +304,11 @@ + + + + @@ -608,7 +612,9 @@ ASPXCodeBehind - + + ASPXCodeBehind + ASPXCodeBehind @@ -622,8 +628,12 @@ ASPXCodeBehind - - + + ASPXCodeBehind + + + ASPXCodeBehind + @@ -829,14 +839,30 @@ ASPXCodeBehind - - - - - - - - + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + AssignDomain2.aspx ASPXCodeBehind @@ -844,10 +870,16 @@ AssignDomain2.aspx - + + ASPXCodeBehind + - - + + ASPXCodeBehind + + + ASPXCodeBehind + @@ -983,37 +1015,57 @@ ASPXCodeBehind - + + ASPXCodeBehind + ASPXCodeBehind - + + ASPXCodeBehind + ASPXCodeBehind - + + ASPXCodeBehind + ASPXCodeBehind - + + ASPXCodeBehind + ASPXCodeBehind - + + ASPXCodeBehind + ASPXCodeBehind - - - + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + ASPXCodeBehind - - + + ASPXCodeBehind + + + ASPXCodeBehind + ASPXCodeBehind @@ -1024,9 +1076,15 @@ ASPXCodeBehind - - - + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + @@ -1055,7 +1113,9 @@ - + + ASPXCodeBehind + @@ -1144,24 +1204,28 @@ delete.aspx + ASPXCodeBehind delete.aspx editContent.aspx + ASPXCodeBehind editContent.aspx preview.aspx + ASPXCodeBehind preview.aspx publish.aspx + ASPXCodeBehind publish.aspx @@ -1204,7 +1268,9 @@ ProgressBar.ascx - + + ASPXCodeBehind + Component @@ -1235,12 +1301,14 @@ FeedProxy.aspx + ASPXCodeBehind FeedProxy.aspx EditRelationType.aspx + ASPXCodeBehind EditRelationType.aspx @@ -1261,6 +1329,7 @@ Preview.aspx + ASPXCodeBehind Preview.aspx @@ -1277,42 +1346,49 @@ xsltVisualize.aspx + ASPXCodeBehind xsltVisualize.aspx insertMasterpageContent.aspx + ASPXCodeBehind insertMasterpageContent.aspx insertMasterpagePlaceholder.aspx + ASPXCodeBehind insertMasterpagePlaceholder.aspx mediaPicker.aspx + ASPXCodeBehind mediaPicker.aspx republish.aspx + ASPXCodeBehind republish.aspx search.aspx + ASPXCodeBehind search.aspx SendPublish.aspx + ASPXCodeBehind SendPublish.aspx @@ -1329,6 +1405,7 @@ media.ascx + ASPXCodeBehind media.ascx @@ -1359,30 +1436,35 @@ assemblyBrowser.aspx + ASPXCodeBehind assemblyBrowser.aspx autoDoc.aspx + ASPXCodeBehind autoDoc.aspx BrowseRepository.aspx + ASPXCodeBehind BrowseRepository.aspx editPackage.aspx + ASPXCodeBehind editPackage.aspx installedPackage.aspx + ASPXCodeBehind installedPackage.aspx @@ -1414,90 +1496,106 @@ xsltInsertValueOf.aspx + ASPXCodeBehind xsltInsertValueOf.aspx about.aspx + ASPXCodeBehind about.aspx exportDocumenttype.aspx + ASPXCodeBehind imageViewer.aspx + ASPXCodeBehind imageViewer.aspx importDocumenttype.aspx + ASPXCodeBehind insertMacro.aspx + ASPXCodeBehind insertMacro.aspx insertTable.aspx + ASPXCodeBehind insertTable.aspx notifications.aspx + ASPXCodeBehind notifications.aspx RegexWs.aspx + ASPXCodeBehind RegexWs.aspx rollBack.aspx + ASPXCodeBehind rollBack.aspx sendToTranslation.aspx + ASPXCodeBehind sendToTranslation.aspx uploadImage.aspx + ASPXCodeBehind uploadImage.aspx viewAuditTrail.aspx + ASPXCodeBehind viewAuditTrail.aspx language.aspx + ASPXCodeBehind language.aspx EditMemberGroup.aspx + ASPXCodeBehind EditMemberGroup.aspx EditMemberType.aspx + ASPXCodeBehind EditMemberType.aspx @@ -1511,6 +1609,7 @@ ViewMembers.aspx + ASPXCodeBehind ViewMembers.aspx @@ -1524,30 +1623,35 @@ InsertAnchor.aspx + ASPXCodeBehind InsertAnchor.aspx insertChar.aspx + ASPXCodeBehind insertChar.aspx insertImage.aspx + ASPXCodeBehind insertImage.aspx insertLink.aspx + ASPXCodeBehind insertLink.aspx insertMacro.aspx + ASPXCodeBehind insertMacro.aspx @@ -1567,6 +1671,7 @@ DictionaryItemList.aspx + ASPXCodeBehind DictionaryItemList.aspx @@ -1580,18 +1685,21 @@ editLanguage.aspx + ASPXCodeBehind editLanguage.aspx EditMediaType.aspx + ASPXCodeBehind EditMediaType.aspx editScript.aspx + ASPXCodeBehind @@ -1646,23 +1754,28 @@ details.aspx + ASPXCodeBehind details.aspx preview.aspx + ASPXCodeBehind preview.aspx xml.aspx + ASPXCodeBehind xml.aspx - + + ASPXCodeBehind + @@ -1705,6 +1818,7 @@ EditUserType.aspx + ASPXCodeBehind EditUserType.aspx @@ -1718,6 +1832,7 @@ PermissionEditor.aspx + ASPXCodeBehind PermissionEditor.aspx