protected ActionResult <TContentType?> PerformPostSave <TContentTypeDisplay, TContentTypeSave, TPropertyType>( TContentTypeSave contentTypeSave, Func <int, TContentType?> getContentType, Action <TContentType?> saveContentType, Action <TContentTypeSave>?beforeCreateNew = null) where TContentTypeDisplay : ContentTypeCompositionDisplay where TContentTypeSave : ContentTypeSave <TPropertyType> where TPropertyType : PropertyTypeBasic { var ctId = Convert.ToInt32(contentTypeSave.Id); TContentType?ct = ctId > 0 ? getContentType(ctId) : null; if (ctId > 0 && ct == null) { return(NotFound()); } //Validate that there's no other ct with the same alias // it in fact cannot be the same as any content type alias (member, content or media) because // this would interfere with how ModelsBuilder works and also how many of the published caches // works since that is based on aliases. IEnumerable <string> allAliases = ContentTypeService.GetAllContentTypeAliases(); var exists = allAliases.InvariantContains(contentTypeSave.Alias); if (exists && (ctId == 0 || (!ct?.Alias.InvariantEquals(contentTypeSave.Alias) ?? false))) { ModelState.AddModelError("Alias", LocalizedTextService.Localize("editcontenttype", "aliasAlreadyExists")); } // execute the external validators ValidateExternalValidators(ModelState, contentTypeSave); if (ModelState.IsValid == false) { TContentTypeDisplay?err = CreateModelStateValidationEror <TContentTypeSave, TContentTypeDisplay>(ctId, contentTypeSave, ct); return(ValidationProblem(err)); } //filter out empty properties contentTypeSave.Groups = contentTypeSave.Groups.Where(x => x.Name.IsNullOrWhiteSpace() == false).ToList(); foreach (PropertyGroupBasic <TPropertyType> group in contentTypeSave.Groups) { group.Properties = group.Properties.Where(x => x.Alias.IsNullOrWhiteSpace() == false).ToList(); } if (ctId > 0) { //its an update to an existing content type //This mapping will cause a lot of content type validation to occur which we need to deal with try { UmbracoMapper.Map(contentTypeSave, ct); } catch (Exception ex) { TContentTypeDisplay?responseEx = CreateInvalidCompositionResponseException <TContentTypeDisplay, TContentTypeSave, TPropertyType>( ex, contentTypeSave, ct, ctId); if (responseEx != null) { return(ValidationProblem(responseEx)); } } TContentTypeDisplay?exResult = CreateCompositionValidationExceptionIfInvalid <TContentTypeSave, TPropertyType, TContentTypeDisplay>( contentTypeSave, ct); if (exResult != null) { return(ValidationProblem(exResult)); } saveContentType(ct); return(ct); } else { if (beforeCreateNew != null) { beforeCreateNew(contentTypeSave); } //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; var allowIfselfAsChildSortOrder = -1; if (contentTypeSave.AllowedContentTypes != null) { allowIfselfAsChildSortOrder = contentTypeSave.AllowedContentTypes.IndexOf(0); allowItselfAsChild = contentTypeSave.AllowedContentTypes.Any(x => x == 0); contentTypeSave.AllowedContentTypes = contentTypeSave.AllowedContentTypes.Where(x => x > 0).ToList(); } //save as new TContentType?newCt = null; try { //This mapping will cause a lot of content type validation to occur which we need to deal with newCt = UmbracoMapper.Map <TContentType>(contentTypeSave); } catch (Exception ex) { TContentTypeDisplay?responseEx = CreateInvalidCompositionResponseException <TContentTypeDisplay, TContentTypeSave, TPropertyType>( ex, contentTypeSave, ct, ctId); if (responseEx is null) { throw ex; } return(ValidationProblem(responseEx)); } TContentTypeDisplay?exResult = CreateCompositionValidationExceptionIfInvalid <TContentTypeSave, TPropertyType, TContentTypeDisplay>( contentTypeSave, newCt); if (exResult != null) { return(ValidationProblem(exResult)); } //set id to null to ensure its handled as a new type contentTypeSave.Id = null; contentTypeSave.CreateDate = DateTime.Now; contentTypeSave.UpdateDate = DateTime.Now; saveContentType(newCt); //we need to save it twice to allow itself under itself. if (allowItselfAsChild && newCt != null) { newCt.AllowedContentTypes = newCt.AllowedContentTypes?.Union( new[] { new ContentTypeSort(newCt.Id, allowIfselfAsChildSortOrder) } ); saveContentType(newCt); } return(newCt); } }