Merge branch 'temp-U4-6837' into 7.4.0

Conflicts:
	src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx.cs
This commit is contained in:
Claus
2015-12-16 12:32:16 +01:00
14 changed files with 198 additions and 224 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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; }

View File

@@ -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;
}
}
}

View File

@@ -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))

View File

@@ -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" />