U4-6837 New content type editor: availableCompositeContentTypes could be moved to service.
Allows for assigning compositions to doc types that are being created. Removes unused getAssignedListViewDataType from contentTypeResource. Removes unused getPropertyTypeScaffold from memberTypeResource. Removes unused performContainerDelete from memberTypeResource - since member types don't support folders. Ensures correct security for the ContentTypeController.GetAllPropertyTypeAliases and ContentTypeController.GetAvailableCompositeContentTypes. Removes unused ContentTypeControllerBase.GetAssignedListViewDataType, this is no longer used because the list view is configured using the DataTypeController.
This commit is contained in:
@@ -7,15 +7,14 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
||||
|
||||
return {
|
||||
|
||||
getAssignedListViewDataType: function (contentTypeId) {
|
||||
|
||||
getAvailableCompositeContentTypes: function (contentTypeId) {
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"contentTypeApiBaseUrl",
|
||||
"GetAssignedListViewDataType",
|
||||
"GetAvailableCompositeContentTypes",
|
||||
[{ contentTypeId: contentTypeId }])),
|
||||
'Failed to retrieve data for content id ' + contentTypeId);
|
||||
'Failed to retrieve data for content type id ' + contentTypeId);
|
||||
},
|
||||
|
||||
|
||||
@@ -34,19 +33,19 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
||||
* $scope.type = type;
|
||||
* });
|
||||
* </pre>
|
||||
* @param {Int} contentId id of the content item to retrive allowed child types for
|
||||
* @param {Int} contentTypeId id of the content item to retrive allowed child types for
|
||||
* @returns {Promise} resourcePromise object.
|
||||
*
|
||||
*/
|
||||
getAllowedTypes: function (contentId) {
|
||||
getAllowedTypes: function (contentTypeId) {
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"contentTypeApiBaseUrl",
|
||||
"GetAllowedChildren",
|
||||
[{ contentId: contentId }])),
|
||||
'Failed to retrieve data for content id ' + contentId);
|
||||
[{ contentId: contentTypeId }])),
|
||||
'Failed to retrieve data for content id ' + contentTypeId);
|
||||
},
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,16 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
||||
|
||||
return {
|
||||
|
||||
getAvailableCompositeContentTypes: function (contentTypeId) {
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"mediaTypeApiBaseUrl",
|
||||
"GetAvailableCompositeMediaTypes",
|
||||
[{ contentTypeId: contentTypeId }])),
|
||||
'Failed to retrieve data for content type id ' + contentTypeId);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.mediaTypeResource#getAllowedTypes
|
||||
|
||||
@@ -7,6 +7,16 @@ function memberTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
||||
|
||||
return {
|
||||
|
||||
getAvailableCompositeContentTypes: function (contentTypeId) {
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"memberTypeApiBaseUrl",
|
||||
"GetAvailableCompositeMemberTypes",
|
||||
[{ contentTypeId: contentTypeId }])),
|
||||
'Failed to retrieve data for content type id ' + contentTypeId);
|
||||
},
|
||||
|
||||
//return all member types
|
||||
getTypes: function () {
|
||||
|
||||
@@ -16,17 +26,7 @@ function memberTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
||||
"memberTypeApiBaseUrl",
|
||||
"GetAllTypes")),
|
||||
'Failed to retrieve data for member types id');
|
||||
},
|
||||
|
||||
getPropertyTypeScaffold : function (id) {
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"memberTypeApiBaseUrl",
|
||||
"GetPropertyTypeScaffold",
|
||||
[{ id: id }])),
|
||||
'Failed to retrieve property type scaffold');
|
||||
},
|
||||
},
|
||||
|
||||
getById: function (id) {
|
||||
|
||||
|
||||
@@ -218,6 +218,13 @@
|
||||
|
||||
function init(contentType) {
|
||||
|
||||
//get available composite types
|
||||
contentTypeResource.getAvailableCompositeContentTypes(contentType.id).then(function (result) {
|
||||
contentType.availableCompositeContentTypes = result;
|
||||
// convert legacy icons
|
||||
convertLegacyIcons(contentType);
|
||||
});
|
||||
|
||||
// set all tab to inactive
|
||||
if (contentType.groups.length !== 0) {
|
||||
angular.forEach(contentType.groups, function (group) {
|
||||
@@ -230,8 +237,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
// convert legacy icons
|
||||
convertLegacyIcons(contentType);
|
||||
|
||||
|
||||
// sort properties after sort order
|
||||
angular.forEach(contentType.groups, function (group) {
|
||||
@@ -244,6 +250,8 @@
|
||||
contentType.allowedTemplates = contentTypeHelper.insertTemplatePlaceholder(contentType.allowedTemplates);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//set a shared state
|
||||
editorState.set(contentType);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @description
|
||||
* The controller for deleting content
|
||||
*/
|
||||
function MemberTypesDeleteController($scope, memberTypeResource, contentTypeResource, treeService, navigationService) {
|
||||
function MemberTypesDeleteController($scope, memberTypeResource, treeService, navigationService) {
|
||||
|
||||
$scope.performDelete = function() {
|
||||
|
||||
@@ -25,23 +25,6 @@ function MemberTypesDeleteController($scope, memberTypeResource, contentTypeReso
|
||||
|
||||
};
|
||||
|
||||
$scope.performContainerDelete = function() {
|
||||
|
||||
//mark it for deletion (used in the UI)
|
||||
$scope.currentNode.loading = true;
|
||||
contentTypeResource.deleteContainerById($scope.currentNode.id).then(function () {
|
||||
$scope.currentNode.loading = false;
|
||||
|
||||
//get the root node before we remove it
|
||||
var rootNode = treeService.getTreeRoot($scope.currentNode);
|
||||
|
||||
//TODO: Need to sync tree, etc...
|
||||
treeService.removeNode($scope.currentNode);
|
||||
navigationService.hideMenu();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
navigationService.hideDialog();
|
||||
};
|
||||
|
||||
@@ -5,33 +5,20 @@
|
||||
Are you sure you want to delete <strong>{{currentNode.name}}</strong> ?
|
||||
</p>
|
||||
|
||||
<ng-switch on="currentNode.nodeType">
|
||||
<div ng-switch-when="container">
|
||||
<p>This action cannot be undone, click ok to delete.</p>
|
||||
<p>
|
||||
<i class="icon-alert red"></i> <strong class="red">All members</strong>
|
||||
using this member type will be deleted permanently, please confirm you want to delete these as well.
|
||||
</p>
|
||||
|
||||
<umb-confirm
|
||||
on-confirm="performContainerDelete"
|
||||
on-cancel="cancel">
|
||||
</umb-confirm>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
<div ng-switch-default>
|
||||
<p>
|
||||
<i class="icon-alert red"></i> <strong class="red">All members</strong>
|
||||
using this member type will be deleted permanently, please confirm you want to delete these as well.
|
||||
</p>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" ng-model="confirmed" />
|
||||
Yes, delete {{currentNode.name}} and all members using this type
|
||||
</label>
|
||||
|
||||
<hr />
|
||||
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" ng-model="confirmed" />
|
||||
Yes, delete {{currentNode.name}} and all members using this type
|
||||
</label>
|
||||
|
||||
<umb-confirm ng-if="confirmed" on-confirm="performDelete" on-cancel="cancel">
|
||||
</umb-confirm>
|
||||
</div>
|
||||
</ng-switch>
|
||||
<umb-confirm ng-if="confirmed" on-confirm="performDelete" on-cancel="cancel">
|
||||
</umb-confirm>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Umbraco.Web.UI.Umbraco.Controls
|
||||
base.OnLoad(e);
|
||||
|
||||
DataTypeControllerUrl = Url.GetUmbracoApiServiceBaseUrl<DataTypeController>(x => x.GetById(0));
|
||||
ContentTypeControllerUrl = Url.GetUmbracoApiServiceBaseUrl<ContentTypeController>(x => x.GetAssignedListViewDataType(0));
|
||||
//ContentTypeControllerUrl = Url.GetUmbracoApiServiceBaseUrl<ContentTypeController>(x => x.GetAssignedListViewDataType(0));
|
||||
}
|
||||
|
||||
protected void dgTabs_PreRender(object sender, EventArgs e)
|
||||
|
||||
@@ -22,7 +22,8 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
//TODO: We'll need to be careful about the security on this controller, when we start implementing
|
||||
// methods to modify content types we'll need to enforce security on the individual methods, we
|
||||
// cannot put security on the whole controller because things like GetAllowedChildren are required for content editing.
|
||||
// cannot put security on the whole controller because things like
|
||||
// GetAllowedChildren, GetPropertyTypeScaffold, GetAllPropertyTypeAliases are required for content editing.
|
||||
|
||||
/// <summary>
|
||||
/// An API controller used for dealing with content types
|
||||
@@ -85,11 +86,24 @@ namespace Umbraco.Web.Editors
|
||||
/// Gets all user defined properties.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[UmbracoTreeAuthorize(
|
||||
Constants.Trees.DocumentTypes, Constants.Trees.Content,
|
||||
Constants.Trees.MediaTypes, Constants.Trees.Media,
|
||||
Constants.Trees.MemberTypes, Constants.Trees.Members)]
|
||||
public IEnumerable<string> GetAllPropertyTypeAliases()
|
||||
{
|
||||
return ApplicationContext.Services.ContentTypeService.GetAllPropertyTypeAliases();
|
||||
}
|
||||
|
||||
public IEnumerable<EntityBasic> GetAvailableCompositeContentTypes(int contentTypeId)
|
||||
{
|
||||
return PerformGetAvailableCompositeContentTypes(contentTypeId, UmbracoObjectTypes.DocumentType);
|
||||
}
|
||||
|
||||
[UmbracoTreeAuthorize(
|
||||
Constants.Trees.DocumentTypes, Constants.Trees.Content,
|
||||
Constants.Trees.MediaTypes, Constants.Trees.Media,
|
||||
Constants.Trees.MemberTypes, Constants.Trees.Members)]
|
||||
public ContentPropertyDisplay GetPropertyTypeScaffold(int id)
|
||||
{
|
||||
var dataTypeDiff = Services.DataTypeService.GetDataTypeDefinitionById(id);
|
||||
|
||||
@@ -46,37 +46,129 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
}
|
||||
|
||||
protected internal DataTypeBasic GetAssignedListViewDataType(int contentTypeId)
|
||||
/// <summary>
|
||||
/// Returns the available composite content types for a given content type
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected IEnumerable<EntityBasic> PerformGetAvailableCompositeContentTypes(int contentTypeId, UmbracoObjectTypes type)
|
||||
{
|
||||
var objectType = Services.EntityService.GetObjectType(contentTypeId);
|
||||
IContentTypeComposition source = null;
|
||||
|
||||
switch (objectType)
|
||||
{
|
||||
case UmbracoObjectTypes.MemberType:
|
||||
var memberType = Services.MemberTypeService.Get(contentTypeId);
|
||||
var dtMember = Services.DataTypeService.GetDataTypeDefinitionByName(Constants.Conventions.DataTypes.ListViewPrefix + memberType.Alias);
|
||||
return dtMember == null
|
||||
? Mapper.Map<IDataTypeDefinition, DataTypeBasic>(
|
||||
Services.DataTypeService.GetDataTypeDefinitionByName(Constants.Conventions.DataTypes.ListViewPrefix + "Member"))
|
||||
: Mapper.Map<IDataTypeDefinition, DataTypeBasic>(dtMember);
|
||||
case UmbracoObjectTypes.MediaType:
|
||||
var mediaType = Services.ContentTypeService.GetMediaType(contentTypeId);
|
||||
var dtMedia = Services.DataTypeService.GetDataTypeDefinitionByName(Constants.Conventions.DataTypes.ListViewPrefix + mediaType.Alias);
|
||||
return dtMedia == null
|
||||
? Mapper.Map<IDataTypeDefinition, DataTypeBasic>(
|
||||
Services.DataTypeService.GetDataTypeDefinitionByName(Constants.Conventions.DataTypes.ListViewPrefix + "Media"))
|
||||
: Mapper.Map<IDataTypeDefinition, DataTypeBasic>(dtMedia);
|
||||
//below is all ported from the old doc type editor and comes with the same weaknesses /insanity / magic
|
||||
|
||||
IContentTypeComposition[] allContentTypes;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case UmbracoObjectTypes.DocumentType:
|
||||
var docType = Services.ContentTypeService.GetContentType(contentTypeId);
|
||||
var dtDoc = Services.DataTypeService.GetDataTypeDefinitionByName(Constants.Conventions.DataTypes.ListViewPrefix + docType.Alias);
|
||||
return dtDoc == null
|
||||
? Mapper.Map<IDataTypeDefinition, DataTypeBasic>(
|
||||
Services.DataTypeService.GetDataTypeDefinitionByName(Constants.Conventions.DataTypes.ListViewPrefix + "Content"))
|
||||
: Mapper.Map<IDataTypeDefinition, DataTypeBasic>(dtDoc);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
if (contentTypeId > 0)
|
||||
{
|
||||
source = Services.ContentTypeService.GetContentType(contentTypeId);
|
||||
if (source == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
|
||||
}
|
||||
allContentTypes = Services.ContentTypeService.GetAllContentTypes().Cast<IContentTypeComposition>().ToArray();
|
||||
break;
|
||||
|
||||
case UmbracoObjectTypes.MediaType:
|
||||
if (contentTypeId > 0)
|
||||
{
|
||||
source = Services.ContentTypeService.GetMediaType(contentTypeId);
|
||||
if (source == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
|
||||
}
|
||||
allContentTypes = Services.ContentTypeService.GetAllMediaTypes().Cast<IContentTypeComposition>().ToArray();
|
||||
break;
|
||||
|
||||
case UmbracoObjectTypes.MemberType:
|
||||
if (contentTypeId > 0)
|
||||
{
|
||||
source = Services.MemberTypeService.Get(contentTypeId);
|
||||
if (source == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
|
||||
}
|
||||
allContentTypes = Services.MemberTypeService.GetAll().Cast<IContentTypeComposition>().ToArray();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("The entity type was not a content type");
|
||||
}
|
||||
|
||||
// note: there are many sanity checks missing here and there ;-((
|
||||
// make sure once and for all
|
||||
//if (allContentTypes.Any(x => x.ParentId > 0 && x.ContentTypeComposition.Any(y => y.Id == x.ParentId) == false))
|
||||
// throw new Exception("A parent does not belong to a composition.");
|
||||
|
||||
// find out if any content type uses this content type
|
||||
var isUsing = allContentTypes.Where(x => x.ContentTypeComposition.Any(y => y.Id == contentTypeId)).ToArray();
|
||||
if (isUsing.Length > 0)
|
||||
{
|
||||
//if already in use a composition, do not allow any composited types
|
||||
return new List<EntityBasic>();
|
||||
}
|
||||
|
||||
// if it is not used then composition is possible
|
||||
// hashset guarantees unicity on Id
|
||||
var list = new HashSet<IContentTypeComposition>(new DelegateEqualityComparer<IContentTypeComposition>(
|
||||
(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);
|
||||
|
||||
// indirect types are those that we use, directly or indirectly
|
||||
var indirectContentTypes = GetIndirect(source).ToArray();
|
||||
foreach (var x in indirectContentTypes)
|
||||
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();
|
||||
|
||||
//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 list
|
||||
.Where(x => x.Id != contentTypeId)
|
||||
.OrderBy(x => x.Name)
|
||||
.Select(Mapper.Map<IContentTypeComposition, EntityBasic>)
|
||||
.Select(x =>
|
||||
{
|
||||
x.Name = TranslateItem(x.Name);
|
||||
return x;
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static IEnumerable<IContentTypeComposition> GetIndirect(IContentTypeComposition ctype)
|
||||
{
|
||||
// hashset guarantees unicity on Id
|
||||
var all = new HashSet<IContentTypeComposition>(new DelegateEqualityComparer<IContentTypeComposition>(
|
||||
(x, y) => x.Id == y.Id,
|
||||
x => x.Id));
|
||||
|
||||
var stack = new Stack<IContentTypeComposition>();
|
||||
|
||||
if (ctype != null)
|
||||
{
|
||||
foreach (var x in ctype.ContentTypeComposition)
|
||||
stack.Push(x);
|
||||
}
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var x = stack.Pop();
|
||||
all.Add(x);
|
||||
foreach (var y in x.ContentTypeComposition)
|
||||
stack.Push(y);
|
||||
}
|
||||
|
||||
return all;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the composition and adds errors to the model state if any are found then throws an error response if there are errors
|
||||
|
||||
@@ -83,6 +83,11 @@ namespace Umbraco.Web.Editors
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
public IEnumerable<EntityBasic> GetAvailableCompositeMediaTypes(int contentTypeId)
|
||||
{
|
||||
return PerformGetAvailableCompositeContentTypes(contentTypeId, UmbracoObjectTypes.MediaType);
|
||||
}
|
||||
|
||||
public ContentTypeCompositionDisplay GetEmpty(int parentId)
|
||||
{
|
||||
var ct = new MediaType(parentId);
|
||||
|
||||
@@ -79,6 +79,11 @@ namespace Umbraco.Web.Editors
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
public IEnumerable<EntityBasic> GetAvailableCompositeMemberTypes(int contentTypeId)
|
||||
{
|
||||
return PerformGetAvailableCompositeContentTypes(contentTypeId, UmbracoObjectTypes.MemberType);
|
||||
}
|
||||
|
||||
public ContentTypeCompositionDisplay GetEmpty()
|
||||
{
|
||||
var ct = new MemberType(-1);
|
||||
|
||||
@@ -18,8 +18,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
//initialize collections so at least their never null
|
||||
Groups = new List<PropertyGroupDisplay>();
|
||||
AllowedContentTypes = new List<int>();
|
||||
CompositeContentTypes = new List<string>();
|
||||
AvailableCompositeContentTypes = new List<EntityBasic>();
|
||||
CompositeContentTypes = new List<string>();
|
||||
Notifications = new List<Notification>();
|
||||
}
|
||||
|
||||
@@ -44,11 +43,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
//Compositions
|
||||
[DataMember(Name = "compositeContentTypes")]
|
||||
public IEnumerable<string> CompositeContentTypes { get; set; }
|
||||
|
||||
[DataMember(Name = "availableCompositeContentTypes")]
|
||||
[ReadOnly(true)]
|
||||
public IEnumerable<EntityBasic> AvailableCompositeContentTypes { get; set; }
|
||||
|
||||
|
||||
[DataMember(Name = "allowAsRoot")]
|
||||
public bool AllowAsRoot { get; set; }
|
||||
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
using AutoMapper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class AvailableCompositeContentTypesResolver : ValueResolver<IContentTypeComposition, IEnumerable<EntityBasic>>
|
||||
{
|
||||
private readonly ApplicationContext _context;
|
||||
|
||||
internal AvailableCompositeContentTypesResolver(ApplicationContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
protected override IEnumerable<EntityBasic> ResolveCore(IContentTypeComposition source)
|
||||
{
|
||||
|
||||
//below is all ported from the old doc type editor and comes with the same weaknesses /insanity / magic
|
||||
|
||||
var s = source;
|
||||
var type = _context.Services.EntityService.GetObjectType(source.Id);
|
||||
var allContentTypes = new IContentTypeComposition[0];
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case UmbracoObjectTypes.DocumentType:
|
||||
allContentTypes = _context.Services.ContentTypeService.GetAllContentTypes().Cast<IContentTypeComposition>().ToArray();
|
||||
break;
|
||||
|
||||
case UmbracoObjectTypes.MediaType:
|
||||
allContentTypes = _context.Services.ContentTypeService.GetAllMediaTypes().Cast<IContentTypeComposition>().ToArray();
|
||||
break;
|
||||
|
||||
case UmbracoObjectTypes.MemberType:
|
||||
allContentTypes = _context.Services.MemberTypeService.GetAll().Cast<IContentTypeComposition>().ToArray();
|
||||
break;
|
||||
}
|
||||
|
||||
// note: there are many sanity checks missing here and there ;-((
|
||||
// make sure once and for all
|
||||
//if (allContentTypes.Any(x => x.ParentId > 0 && x.ContentTypeComposition.Any(y => y.Id == x.ParentId) == false))
|
||||
// throw new Exception("A parent does not belong to a composition.");
|
||||
|
||||
// find out if any content type uses this content type
|
||||
var isUsing = allContentTypes.Where(x => x.ContentTypeComposition.Any(y => y.Id == source.Id)).ToArray();
|
||||
if (isUsing.Length > 0)
|
||||
{
|
||||
//if already in use a composition, do not allow any composited types
|
||||
return new List<EntityBasic>();
|
||||
}
|
||||
|
||||
// if it is not used then composition is possible
|
||||
// hashset guarantees unicity on Id
|
||||
var list = new HashSet<IContentTypeComposition>(new DelegateEqualityComparer<IContentTypeComposition>(
|
||||
(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);
|
||||
|
||||
// indirect types are those that we use, directly or indirectly
|
||||
var indirectContentTypes = GetIndirect(source).ToArray();
|
||||
foreach (var x in indirectContentTypes)
|
||||
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();
|
||||
|
||||
//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 list
|
||||
.Where(x => x.Id != source.Id)
|
||||
.OrderBy(x => x.Name)
|
||||
.Select(Mapper.Map<IContentTypeComposition, EntityBasic>)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
private IEnumerable<IContentTypeComposition> GetIndirect(IContentTypeComposition ctype)
|
||||
{
|
||||
// hashset guarantees unicity on Id
|
||||
var all = new HashSet<IContentTypeComposition>(new DelegateEqualityComparer<IContentTypeComposition>(
|
||||
(x, y) => x.Id == y.Id,
|
||||
x => x.Id));
|
||||
|
||||
var stack = new Stack<IContentTypeComposition>();
|
||||
|
||||
foreach (var x in ctype.ContentTypeComposition)
|
||||
stack.Push(x);
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var x = stack.Pop();
|
||||
all.Add(x);
|
||||
foreach (var y in x.ContentTypeComposition)
|
||||
stack.Push(y);
|
||||
}
|
||||
|
||||
return all;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,6 @@ namespace Umbraco.Web.Models.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());
|
||||
}
|
||||
@@ -77,11 +76,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.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))
|
||||
|
||||
@@ -324,7 +324,6 @@
|
||||
<Compile Include="Security\Identity\GetUserSecondsMiddleWare.cs" />
|
||||
<Compile Include="Models\ContentEditing\PropertyTypeDisplay.cs" />
|
||||
<Compile Include="Models\ContentEditing\PropertyGroupDisplay.cs" />
|
||||
<Compile Include="Models\Mapping\AvailableCompositeContentTypesResolver.cs" />
|
||||
<Compile Include="Media\EmbedProviders\OEmbedJson.cs" />
|
||||
<Compile Include="Media\EmbedProviders\OEmbedResponse.cs" />
|
||||
<Compile Include="Media\EmbedProviders\OEmbedPhoto.cs" />
|
||||
|
||||
Reference in New Issue
Block a user