Wires up the correct saving logic for media types + validation, refactors the logic so it can be reused everywhere.

This commit is contained in:
Shannon
2015-10-15 14:48:50 +02:00
parent 815d34f8a6
commit 33c206d820
9 changed files with 268 additions and 368 deletions

View File

@@ -3,7 +3,7 @@
* @name umbraco.resources.mediaTypeResource
* @description Loads in data for media types
**/
function mediaTypeResource($q, $http, umbRequestHelper) {
function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
return {
@@ -81,12 +81,10 @@ function mediaTypeResource($q, $http, umbRequestHelper) {
save: function (contentType) {
var saveModel = umbDataFormatter.formatContentTypePostData(contentType);
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"mediaTypeApiBaseUrl",
"PostSave"
), contentType),
$http.post(umbRequestHelper.getApiUrl("mediaTypeApiBaseUrl", "PostSave"), saveModel),
'Failed to save data for content type id ' + contentType.id);
},

View File

@@ -154,33 +154,12 @@
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,
//no-op for rebind callback... we don't really need to rebind for content types
//no-op for rebind callback... we don't really need to rebind for content types
rebindCallback: angular.noop
}).then(function (data) {
//success

View File

@@ -9,7 +9,7 @@
(function() {
"use strict";
function MediaTypesEditController($scope, $routeParams, mediaTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter) {
function MediaTypesEditController($scope, $routeParams, mediaTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter, $q) {
var vm = this;
@@ -125,49 +125,47 @@
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);
}
contentEditingHelper.contentEditorPerformSave({
statusMessage: "Saving...",
saveMethod: mediaTypeResource.save,
scope: $scope,
content: vm.contentType,
//no-op for rebind callback... we don't really need to rebind for content types
rebindCallback: angular.noop
}).then(function (data) {
//success
syncTreeNode(vm.contentType, data.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);
mediaTypeResource.save(vm.contentType).then(function(dt){
formHelper.resetForm({ scope: $scope, notifications: dt.notifications });
contentEditingHelper.handleSuccessfulSave({
scope: $scope,
savedContent: dt,
rebindCallback: function() {
}
});
notificationsService.success("Media type saved");
//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

View File

@@ -90,134 +90,33 @@ namespace Umbraco.Web.Editors
: Request.CreateValidationErrorResponse(result.Exception.Message);
}
/// <summary>
/// Validates the composition and adds errors to the model state if any are found then throws an error response if there are errors
/// </summary>
/// <param name="contentTypeSave"></param>
/// <param name="composition"></param>
/// <returns></returns>
private void ValidateComposition(ContentTypeSave contentTypeSave, IContentTypeComposition composition)
{
var validateAttempt = Services.ContentTypeService.ValidateComposition(composition);
if (validateAttempt == false)
{
//if it's not successful then we need to return some model state for the property aliases that
// are duplicated
var propertyAliases = validateAttempt.Result.Distinct();
foreach (var propertyAlias in propertyAliases)
{
//find the property relating to these
var prop = contentTypeSave.Groups.SelectMany(x => x.Properties).Single(x => x.Alias == propertyAlias);
var group = contentTypeSave.Groups.Single(x => x.Properties.Contains(prop));
var propIndex = group.Properties.IndexOf(prop);
var groupIndex = contentTypeSave.Groups.IndexOf(group);
var key = string.Format("Groups[{0}].Properties[{1}].Alias", groupIndex, propIndex);
ModelState.AddModelError(key, "Duplicate property aliases not allowed between compositions");
}
var display = Mapper.Map<ContentTypeDisplay>(composition);
//map the 'save' data on top
display = Mapper.Map(contentTypeSave, display);
display.Errors = ModelState.ToErrorDictionary();
throw new HttpResponseException(Request.CreateValidationErrorResponse(display));
}
}
public ContentTypeDisplay PostSave(ContentTypeSave contentTypeSave)
{
var ctId = Convert.ToInt32(contentTypeSave.Id);
var ctService = Services.ContentTypeService;
if (ModelState.IsValid == false)
{
var ct = ctService.GetContentType(ctId);
//Required data is invalid so we cannot continue
var forDisplay = Mapper.Map<ContentTypeDisplay>(ct);
//map the 'save' data on top
forDisplay = Mapper.Map(contentTypeSave, forDisplay);
forDisplay.Errors = ModelState.ToErrorDictionary();
throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
}
//filter out empty properties
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
ContentTypeDisplay display;
if (ctId > 0)
{
//its an update to an existing
var found = ctService.GetContentType(ctId);
if (found == null)
throw new HttpResponseException(HttpStatusCode.NotFound);
Mapper.Map(contentTypeSave, found);
//NOTE: this throws an error response if it is not valid
ValidateComposition(contentTypeSave, found);
ctService.Save(found);
display = Mapper.Map<ContentTypeDisplay>(found);
}
else
{
//set id to null to ensure its handled as a new type
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
//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 savedCt = PerformPostSave(
contentTypeSave: contentTypeSave,
getContentType: i => Services.ContentTypeService.GetContentType(i),
saveContentType: type => Services.ContentTypeService.Save(type),
beforeCreateNew: ctSave =>
{
var template = Services.FileService.GetTemplate(contentTypeSave.Alias);
if (template == null)
//create a default template if it doesnt exist -but only if default template is == to the content type
//TODO: Is this really what we want? What if we don't want any template assigned at all ?
if (ctSave.DefaultTemplate.IsNullOrWhiteSpace() == false && ctSave.DefaultTemplate == ctSave.Alias)
{
template = new Template(contentTypeSave.Name, contentTypeSave.Alias);
Services.FileService.SaveTemplate(template);
var template = Services.FileService.GetTemplate(ctSave.Alias);
if (template == null)
{
template = new Template(ctSave.Name, ctSave.Alias);
Services.FileService.SaveTemplate(template);
}
//make sure the template alias is set on the default and allowed template so we can map it back
ctSave.DefaultTemplate = template.Alias;
}
});
//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 (contentTypeSave.AllowedContentTypes != null)
{
allowItselfAsChild = contentTypeSave.AllowedContentTypes.Any(x => x == 0);
contentTypeSave.AllowedContentTypes = contentTypeSave.AllowedContentTypes.Where(x => x > 0).ToList();
}
//save as new
var newCt = Mapper.Map<IContentType>(contentTypeSave);
//NOTE: this throws an error response if it is not valid
ValidateComposition(contentTypeSave, newCt);
ctService.Save(newCt);
//we need to save it twice to allow itself under itself.
if (allowItselfAsChild)
{
//NOTE: This will throw if the composition isn't right... but it shouldn't be at this stage
newCt.AddContentType(newCt);
ctService.Save(newCt);
}
display = Mapper.Map<ContentTypeDisplay>(newCt);
}
var display = Mapper.Map<ContentTypeDisplay>(savedCt);
display.AddSuccessNotification(
Services.TextService.Localize("speechBubbles/contentTypeSavedHeader"),

View File

@@ -45,7 +45,7 @@ namespace Umbraco.Web.Editors
{
}
public DataTypeBasic GetAssignedListViewDataType(int contentTypeId)
protected internal DataTypeBasic GetAssignedListViewDataType(int contentTypeId)
{
var objectType = Services.EntityService.GetObjectType(contentTypeId);
@@ -81,12 +81,12 @@ namespace Umbraco.Web.Editors
/// Gets all user defined properties.
/// </summary>
/// <returns></returns>
public IEnumerable<string> GetAllPropertyTypeAliases()
protected IEnumerable<string> GetAllPropertyTypeAliases()
{
return ApplicationContext.Services.ContentTypeService.GetAllPropertyTypeAliases();
}
public ContentPropertyDisplay GetPropertyTypeScaffold(int id)
protected ContentPropertyDisplay GetPropertyTypeScaffold(int id)
{
var dataTypeDiff = Services.DataTypeService.GetDataTypeDefinitionById(id);
@@ -107,7 +107,7 @@ namespace Umbraco.Web.Editors
};
}
public dynamic GetSafeAlias(string value, bool camelCase = true)
protected dynamic GetSafeAlias(string value, bool camelCase = true)
{
var returnValue = (string.IsNullOrWhiteSpace(value)) ? string.Empty : value.ToSafeAlias(camelCase);
dynamic returnObj = new System.Dynamic.ExpandoObject();
@@ -118,9 +118,42 @@ namespace Umbraco.Web.Editors
return returnObj;
}
/// <summary>
/// Validates the composition and adds errors to the model state if any are found then throws an error response if there are errors
/// </summary>
/// <param name="contentTypeSave"></param>
/// <param name="composition"></param>
/// <returns></returns>
protected void ValidateComposition(ContentTypeSave contentTypeSave, IContentTypeComposition composition)
{
var validateAttempt = Services.ContentTypeService.ValidateComposition(composition);
if (validateAttempt == false)
{
//if it's not successful then we need to return some model state for the property aliases that
// are duplicated
var propertyAliases = validateAttempt.Result.Distinct();
foreach (var propertyAlias in propertyAliases)
{
//find the property relating to these
var prop = contentTypeSave.Groups.SelectMany(x => x.Properties).Single(x => x.Alias == propertyAlias);
var group = contentTypeSave.Groups.Single(x => x.Properties.Contains(prop));
var propIndex = group.Properties.IndexOf(prop);
var groupIndex = contentTypeSave.Groups.IndexOf(group);
var key = string.Format("Groups[{0}].Properties[{1}].Alias", groupIndex, propIndex);
ModelState.AddModelError(key, "Duplicate property aliases not allowed between compositions");
}
public string TranslateItem(string text)
var display = Mapper.Map<ContentTypeDisplay>(composition);
//map the 'save' data on top
display = Mapper.Map(contentTypeSave, display);
display.Errors = ModelState.ToErrorDictionary();
throw new HttpResponseException(Request.CreateValidationErrorResponse(display));
}
}
protected string TranslateItem(string text)
{
if (text == null)
{
@@ -134,6 +167,89 @@ namespace Umbraco.Web.Editors
return CultureDictionary[text].IfNullOrWhiteSpace(text);
}
protected TContentType PerformPostSave<TContentType>(
ContentTypeSave contentTypeSave,
Func<int, TContentType> getContentType,
Action<TContentType> saveContentType,
Action<ContentTypeSave> beforeCreateNew = null)
where TContentType : IContentTypeComposition
{
var ctId = Convert.ToInt32(contentTypeSave.Id);
if (ModelState.IsValid == false)
{
var ct = getContentType(ctId);
//Required data is invalid so we cannot continue
var forDisplay = Mapper.Map<ContentTypeCompositionDisplay>(ct);
//map the 'save' data on top
forDisplay = Mapper.Map(contentTypeSave, forDisplay);
forDisplay.Errors = ModelState.ToErrorDictionary();
throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
}
//filter out empty properties
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();
}
if (ctId > 0)
{
//its an update to an existing
var found = getContentType(ctId);
if (found == null)
throw new HttpResponseException(HttpStatusCode.NotFound);
Mapper.Map(contentTypeSave, found);
//NOTE: this throws an error response if it is not valid
ValidateComposition(contentTypeSave, found);
saveContentType(found);
return found;
}
else
{
if (beforeCreateNew != null)
{
beforeCreateNew(contentTypeSave);
}
//set id to null to ensure its handled as a new type
contentTypeSave.Id = null;
contentTypeSave.CreateDate = DateTime.Now;
contentTypeSave.UpdateDate = DateTime.Now;
//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 (contentTypeSave.AllowedContentTypes != null)
{
allowItselfAsChild = contentTypeSave.AllowedContentTypes.Any(x => x == 0);
contentTypeSave.AllowedContentTypes = contentTypeSave.AllowedContentTypes.Where(x => x > 0).ToList();
}
//save as new
var newCt = Mapper.Map<TContentType>(contentTypeSave);
//NOTE: this throws an error response if it is not valid
ValidateComposition(contentTypeSave, newCt);
saveContentType(newCt);
//we need to save it twice to allow itself under itself.
if (allowItselfAsChild)
{
//NOTE: This will throw if the composition isn't right... but it shouldn't be at this stage
newCt.AddContentType(newCt);
saveContentType(newCt);
}
return newCt;
}
}
private ICultureDictionary CultureDictionary
{
get

View File

@@ -14,7 +14,9 @@ using System.Net;
using Umbraco.Core.PropertyEditors;
using System;
using System.Net.Http;
using Umbraco.Web.WebApi;
using ContentType = System.Net.Mime.ContentType;
using Umbraco.Core.Services;
namespace Umbraco.Web.Editors
{
@@ -100,58 +102,20 @@ namespace Umbraco.Web.Editors
.Select(Mapper.Map<IMediaType, ContentTypeBasic>);
}
public ContentTypeCompositionDisplay PostSave(ContentTypeCompositionDisplay contentType)
public ContentTypeCompositionDisplay PostSave(ContentTypeSave contentTypeSave)
{
var savedCt = PerformPostSave(
contentTypeSave: contentTypeSave,
getContentType: i => Services.ContentTypeService.GetMediaType(i),
saveContentType: type => Services.ContentTypeService.Save(type));
var ctService = ApplicationContext.Services.ContentTypeService;
var display = Mapper.Map<ContentTypeCompositionDisplay>(savedCt);
//TODO: warn on content type alias conflicts
//TODO: warn on property alias conflicts
//TODO: Validate the submitted model
//filter out empty properties
contentType.Groups = contentType.Groups.Where(x => x.Name.IsNullOrWhiteSpace() == false).ToList();
foreach (var group in contentType.Groups)
{
group.Properties = group.Properties.Where(x => x.Alias.IsNullOrWhiteSpace() == false).ToList();
}
var ctId = Convert.ToInt32(contentType.Id);
if (ctId > 0)
{
//its an update to an existing
IMediaType found = ctService.GetMediaType(ctId);
if (found == null)
throw new HttpResponseException(HttpStatusCode.NotFound);
Mapper.Map(contentType, 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;
}
else
{
//ensure alias is set
if (string.IsNullOrEmpty(contentType.Alias))
contentType.Alias = contentType.Name.ToSafeAlias();
contentType.Id = null;
//save as new
IMediaType newCt = new MediaType(-1);
Mapper.Map(contentType, 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;
}
display.AddSuccessNotification(
Services.TextService.Localize("speechBubbles/contentTypeSavedHeader"),
string.Empty);
return display;
}

View File

@@ -4,6 +4,7 @@ using System.Web.Security;
using AutoMapper;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Core.Security;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
@@ -105,58 +106,20 @@ namespace Umbraco.Web.Editors
return Enumerable.Empty<ContentTypeBasic>();
}
public ContentTypeCompositionDisplay PostSave(ContentTypeCompositionDisplay contentType)
public ContentTypeCompositionDisplay PostSave(ContentTypeSave contentTypeSave)
{
var savedCt = PerformPostSave(
contentTypeSave: contentTypeSave,
getContentType: i => Services.MemberTypeService.Get(i),
saveContentType: type => Services.MemberTypeService.Save(type));
var ctService = ApplicationContext.Services.MemberTypeService;
var display = Mapper.Map<ContentTypeCompositionDisplay>(savedCt);
//TODO: warn on content type alias conflicts
//TODO: warn on property alias conflicts
//TODO: Validate the submitted model
//filter out empty properties
contentType.Groups = contentType.Groups.Where(x => x.Name.IsNullOrWhiteSpace() == false).ToList();
foreach (var group in contentType.Groups)
{
group.Properties = group.Properties.Where(x => x.Alias.IsNullOrWhiteSpace() == false).ToList();
}
var ctId = Convert.ToInt32(contentType.Id);
if (ctId > 0)
{
//its an update to an existing
IMemberType found = ctService.Get(ctId);
if (found == null)
throw new HttpResponseException(HttpStatusCode.NotFound);
Mapper.Map(contentType, 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;
}
else
{
//ensure alias is set
if (string.IsNullOrEmpty(contentType.Alias))
contentType.Alias = contentType.Name.ToSafeAlias();
contentType.Id = null;
//save as new
IMemberType newCt = new MemberType(-1);
Mapper.Map(contentType, 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;
}
display.AddSuccessNotification(
Services.TextService.Localize("speechBubbles/contentTypeSavedHeader"),
string.Empty);
return display;
}
}
}

View File

@@ -49,7 +49,6 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(type => type.UpdateDate, expression => expression.Ignore())
.ForMember(type => type.HasIdentity, expression => expression.Ignore());
config.CreateMap<ContentTypeSave, IContentType>()
//do the base mapping
.MapBaseContentTypeSaveToEntity(applicationContext)
@@ -64,84 +63,29 @@ namespace Umbraco.Web.Models.Mapping
if (source.DefaultTemplate != null)
dest.SetDefaultTemplate(applicationContext.Services.FileService.GetTemplate(source.DefaultTemplate));
//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.ContentTypeService.GetContentType(a);
if (addCt != null)
dest.AddContentType(addCt);
}
ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, applicationContext);
});
//config.CreateMap<ContentTypeCompositionDisplay, IMemberType>()
// //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<ContentTypeCompositionDisplay, IMediaType>()
// //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.ContentTypeService.GetMediaType(a);
// if (addCt != null)
// dest.AddContentType(addCt);
// }
// });
config.CreateMap<ContentTypeSave, IMediaType>()
//do the base mapping
.MapBaseContentTypeSaveToEntity(applicationContext)
.ConstructUsing((source) => new MediaType(source.ParentId))
.AfterMap((source, dest) =>
{
ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, applicationContext);
});
config.CreateMap<ContentTypeSave, IMemberType>()
//do the base mapping
.MapBaseContentTypeSaveToEntity(applicationContext)
.ConstructUsing((source) => new MemberType(source.ParentId))
.AfterMap((source, dest) =>
{
ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, applicationContext);
});
config.CreateMap<IContentTypeComposition, string>().ConvertUsing(x => x.Alias);
config.CreateMap<IMemberType, ContentTypeCompositionDisplay>()
//map base logic
.MapBaseContentTypeEntityToDisplay(applicationContext, _propertyEditorResolver);
@@ -151,7 +95,6 @@ namespace Umbraco.Web.Models.Mapping
.MapBaseContentTypeEntityToDisplay(applicationContext, _propertyEditorResolver)
.AfterMap((source, dest) =>
{
//default listview
dest.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Media";
@@ -224,15 +167,13 @@ namespace Umbraco.Web.Models.Mapping
#region *** Used for mapping on top of an existing display object from a save object ***
config.CreateMap<ContentTypeSave, ContentTypeCompositionDisplay>()
.MapBaseContentTypeSaveToDisplay();
config.CreateMap<ContentTypeSave, ContentTypeDisplay>()
.ForMember(dto => dto.CreateDate, expression => expression.Ignore())
.ForMember(dto => dto.UpdateDate, expression => expression.Ignore())
.MapBaseContentTypeSaveToDisplay()
.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 templates

View File

@@ -19,6 +19,48 @@ namespace Umbraco.Web.Models.Mapping
/// </remarks>
internal static class ContentTypeModelMapperExtensions
{
public static void AfterMapContentTypeSaveToEntity<TSource, TDestination>(
TSource source, TDestination dest,
ApplicationContext applicationContext)
where TSource : ContentTypeSave
where TDestination : IContentTypeComposition
{
//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.ContentTypeService.GetContentType(a);
if (addCt != null)
dest.AddContentType(addCt);
}
}
public static IMappingExpression<TSource, TDestination> MapBaseContentTypeSaveToDisplay<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mapping)
where TSource : ContentTypeSave
where TDestination : ContentTypeCompositionDisplay
{
return mapping
.ForMember(dto => dto.CreateDate, expression => expression.Ignore())
.ForMember(dto => dto.UpdateDate, 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());
}
public static IMappingExpression<TSource, TDestination> MapBaseContentTypeEntityToDisplay<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mapping, ApplicationContext applicationContext, Lazy<PropertyEditorResolver> propertyEditorResolver)
where TSource : IContentTypeComposition