Merge pull request #3435 from umbraco/temp8-3129

Temp8 3129
This commit is contained in:
Shannon Deminick
2018-10-26 13:22:09 +11:00
committed by GitHub
14 changed files with 1140 additions and 257 deletions

View File

@@ -115,7 +115,7 @@ namespace Umbraco.Core
/// <summary>
/// Determines whether a variation varies by culture and segment.
/// </summary>
public static bool VariesByCultureAndSegment(this ContentVariation variation) => (variation & ContentVariation.CultureAndSegment) > 0;
public static bool VariesByCultureAndSegment(this ContentVariation variation) => (variation & ContentVariation.CultureAndSegment) == ContentVariation.CultureAndSegment;
/// <summary>
/// Validates that a combination of culture and segment is valid for the variation.

View File

@@ -63,20 +63,5 @@ namespace Umbraco.Core.Models
aliases = a;
return hasAnyPropertyVariationChanged;
}
/// <summary>
/// Returns the list of content types the composition is used in
/// </summary>
/// <param name="allContentTypes"></param>
/// <param name="source"></param>
/// <returns></returns>
internal static IEnumerable<IContentTypeComposition> GetWhereCompositionIsUsedInContentTypes(this IContentTypeComposition source,
IContentTypeComposition[] allContentTypes)
{
var sourceId = source != null ? source.Id : 0;
// find which content types are using this composition
return allContentTypes.Where(x => x.ContentTypeComposition.Any(y => y.Id == sourceId)).ToArray();
}
}
}

View File

@@ -114,6 +114,27 @@ namespace Umbraco.Core.Models
}
}
/// <summary>
/// Gets the property types obtained via composition.
/// </summary>
/// <remarks>
/// <para>Gets them raw, ie with their original variation.</para>
/// </remarks>
[IgnoreDataMember]
internal IEnumerable<PropertyType> RawComposedPropertyTypes => GetRawComposedPropertyTypes();
private IEnumerable<PropertyType> GetRawComposedPropertyTypes(bool start = true)
{
var propertyTypes = ContentTypeComposition
.Cast<ContentTypeCompositionBase>()
.SelectMany(x => start ? x.GetRawComposedPropertyTypes(false) : x.CompositionPropertyTypes);
if (!start)
propertyTypes = propertyTypes.Union(PropertyTypes);
return propertyTypes;
}
/// <summary>
/// Adds a content type to the composition.
/// </summary>

View File

@@ -11,13 +11,6 @@ namespace Umbraco.Core.Persistence.Repositories
TItem Get(string alias);
IEnumerable<MoveEventInfo<TItem>> Move(TItem moving, EntityContainer container);
/// <summary>
/// Returns the content types that are direct compositions of the content type
/// </summary>
/// <param name="id">The content type id</param>
/// <returns></returns>
IEnumerable<TItem> GetTypesDirectlyComposedOf(int id);
/// <summary>
/// Derives a unique alias from an existing alias.
/// </summary>

View File

@@ -67,7 +67,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return ContentTypeQueryMapper.GetContentTypes(Database, SqlSyntax, IsPublishing, this, _templateRepository);
}
protected override IEnumerable<IContentType> PerformGetAll(params Guid[] ids)
{
// use the underlying GetAll which will force cache all content types

View File

@@ -119,7 +119,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected void PersistNewBaseContentType(IContentTypeComposition entity)
{
var dto = ContentTypeFactory.BuildContentTypeDto(entity);
//Cannot add a duplicate content type type
@@ -234,7 +233,6 @@ AND umbracoNode.nodeObjectType = @objectType",
protected void PersistUpdatedBaseContentType(IContentTypeComposition entity)
{
var dto = ContentTypeFactory.BuildContentTypeDto(entity);
// ensure the alias is not used already
@@ -270,8 +268,8 @@ AND umbracoNode.id <> @id",
// 1. Find content based on the current ContentType: entity.Id
// 2. Find all PropertyTypes on the ContentType that was removed - tracked id (key)
// 3. Remove properties based on property types from the removed content type where the content ids correspond to those found in step one
var compositionBase = entity as ContentTypeCompositionBase;
if (compositionBase != null && compositionBase.RemovedContentTypeKeyTracker != null &&
if (entity is ContentTypeCompositionBase compositionBase &&
compositionBase.RemovedContentTypeKeyTracker != null &&
compositionBase.RemovedContentTypeKeyTracker.Any())
{
//TODO: Could we do the below with bulk SQL statements instead of looking everything up and then manipulating?
@@ -314,7 +312,7 @@ AND umbracoNode.id <> @id",
}
}
// delete the allowed content type entries before re-inserting the collectino of allowed content types
// delete the allowed content type entries before re-inserting the collection of allowed content types
Database.Delete<ContentTypeAllowedContentTypeDto>("WHERE Id = @Id", new { entity.Id });
foreach (var allowedContentType in entity.AllowedContentTypes)
{
@@ -409,40 +407,34 @@ AND umbracoNode.id <> @id",
}
//check if the content type variation has been changed
var ctVariationChanging = entity.IsPropertyDirty("Variations");
if (ctVariationChanging)
var contentTypeVariationDirty = entity.IsPropertyDirty("Variations");
var oldContentTypeVariation = (ContentVariation) dtoPk.Variations;
var newContentTypeVariation = entity.Variations;
var contentTypeVariationChanging = contentTypeVariationDirty && oldContentTypeVariation != newContentTypeVariation;
if (contentTypeVariationChanging)
{
//we've already looked up the previous version of the content type so we know it's previous variation state
MoveVariantData(entity, (ContentVariation)dtoPk.Variations, entity.Variations);
MoveContentTypeVariantData(entity, oldContentTypeVariation, newContentTypeVariation);
Clear301Redirects(entity);
ClearScheduledPublishing(entity);
}
}
//track any content type/property types that are changing variation which will require content updates
var propertyTypeVariationChanges = new Dictionary<int, ContentVariation>();
// collect property types that have a dirty variation
List<PropertyType> propertyTypeVariationDirty = null;
// insert or update properties
// all of them, no-group and in-groups
// note: this only deals with *local* property types, we're dealing w/compositions later below
foreach (var propertyType in entity.PropertyTypes)
{
//if the content type variation isn't changing track if any property type is changing
if (!ctVariationChanging)
if (contentTypeVariationChanging)
{
if (propertyType.IsPropertyDirty("Variations"))
// content type is changing
switch (newContentTypeVariation)
{
propertyTypeVariationChanges[propertyType.Id] = propertyType.Variations;
}
}
else
{
switch(entity.Variations)
{
case ContentVariation.Nothing:
//if the content type is changing to Nothing, then all property type's must change to nothing
case ContentVariation.Nothing: // changing to Nothing
// all property types must change to Nothing
propertyType.Variations = ContentVariation.Nothing;
break;
case ContentVariation.Culture:
//we don't need to modify the property type in this case
case ContentVariation.Culture: // changing to Culture
// all property types can remain Nothing
break;
case ContentVariation.CultureAndSegment:
case ContentVariation.Segment:
@@ -451,15 +443,65 @@ AND umbracoNode.id <> @id",
}
}
var groupId = propertyType.PropertyGroupId?.Value ?? default(int);
// then, track each property individually
if (propertyType.IsPropertyDirty("Variations"))
{
// allocate the list only when needed
if (propertyTypeVariationDirty == null)
propertyTypeVariationDirty = new List<PropertyType>();
propertyTypeVariationDirty.Add(propertyType);
}
}
// figure out dirty property types that have actually changed
// before we insert or update properties, so we can read the old variations
var propertyTypeVariationChanges = propertyTypeVariationDirty != null
? GetPropertyVariationChanges(propertyTypeVariationDirty)
: null;
// deal with composition property types
// add changes for property types obtained via composition, which change due
// to this content type variations change
if (contentTypeVariationChanging)
{
// must use RawComposedPropertyTypes here: only those types that are obtained
// via composition, with their original variations (ie not filtered by this
// content type variations - we need this true value to make decisions.
foreach (var propertyType in ((ContentTypeCompositionBase) entity).RawComposedPropertyTypes)
{
if (propertyType.VariesBySegment() || newContentTypeVariation.VariesBySegment())
throw new NotSupportedException(); // TODO: support this
if (propertyType.Variations == ContentVariation.Culture)
{
if (propertyTypeVariationChanges == null)
propertyTypeVariationChanges = new Dictionary<int, (ContentVariation, ContentVariation)>();
// if content type moves to Culture, property type becomes Culture here again
// if content type moves to Nothing, property type becomes Nothing here
if (newContentTypeVariation == ContentVariation.Culture)
propertyTypeVariationChanges[propertyType.Id] = (ContentVariation.Nothing, ContentVariation.Culture);
else if (newContentTypeVariation == ContentVariation.Nothing)
propertyTypeVariationChanges[propertyType.Id] = (ContentVariation.Culture, ContentVariation.Nothing);
}
}
}
// insert or update properties
// all of them, no-group and in-groups
foreach (var propertyType in entity.PropertyTypes)
{
// if the Id of the DataType is not set, we resolve it from the db by its PropertyEditorAlias
if (propertyType.DataTypeId == 0 || propertyType.DataTypeId == default(int))
if (propertyType.DataTypeId == 0 || propertyType.DataTypeId == default)
AssignDataTypeFromPropertyEditor(propertyType);
// validate the alias
ValidateAlias(propertyType);
// insert or update property
var groupId = propertyType.PropertyGroupId?.Value ?? default;
var propertyTypeDto = PropertyGroupFactory.BuildPropertyTypeDto(groupId, propertyType, entity.Id);
var typeId = propertyType.HasIdentity
? Database.Update(propertyTypeDto)
@@ -470,31 +512,22 @@ AND umbracoNode.id <> @id",
typeId = propertyType.Id;
// not an orphan anymore
if (orphanPropertyTypeIds != null)
orphanPropertyTypeIds.Remove(typeId);
orphanPropertyTypeIds?.Remove(typeId);
}
//check if any property types were changing variation
if (propertyTypeVariationChanges.Count > 0)
{
var changes = new Dictionary<int, (ContentVariation, ContentVariation)>();
// must restrict property data changes to impacted content types - if changing a composing
// type, some composed types (those that do not vary) are not impacted and should be left
// unchanged
//
// getting 'all' from the cache policy is prone to race conditions - fast but dangerous
//var all = ((FullDataSetRepositoryCachePolicy<TEntity, int>)CachePolicy).GetAllCached(PerformGetAll);
var all = PerformGetAll();
//now get the current property type variations for the changed ones so that we know which variation they
//are going from and to
var from = Database.Dictionary<int, byte>(Sql()
.Select<PropertyTypeDto>(x => x.Id, x => x.Variations)
.From<PropertyTypeDto>()
.WhereIn<PropertyTypeDto>(x => x.Id, propertyTypeVariationChanges.Keys));
foreach (var f in from)
{
changes[f.Key] = (propertyTypeVariationChanges[f.Key], (ContentVariation)f.Value);
}
//perform the move
MoveVariantData(changes);
}
var impacted = GetImpactedContentTypes(entity, all);
// if some property types have actually changed, move their variant data
if (propertyTypeVariationChanges != null)
MovePropertyTypeVariantData(propertyTypeVariationChanges, impacted);
// deal with orphan properties: those that were in a deleted tab,
// and have not been re-mapped to another tab or to 'generic properties'
@@ -503,6 +536,77 @@ AND umbracoNode.id <> @id",
DeletePropertyType(entity.Id, id);
}
private IEnumerable<IContentTypeComposition> GetImpactedContentTypes(IContentTypeComposition contentType, IEnumerable<IContentTypeComposition> all)
{
var impact = new List<IContentTypeComposition>();
var set = new List<IContentTypeComposition> { contentType };
var tree = new Dictionary<int, List<IContentTypeComposition>>();
foreach (var x in all)
foreach (var y in x.ContentTypeComposition)
{
if (!tree.TryGetValue(y.Id, out var list))
list = tree[y.Id] = new List<IContentTypeComposition>();
list.Add(x);
}
var nset = new List<IContentTypeComposition>();
do
{
impact.AddRange(set);
foreach (var x in set)
{
if (!tree.TryGetValue(x.Id, out var list)) continue;
nset.AddRange(list.Where(y => y.VariesByCulture()));
}
set = nset;
nset = new List<IContentTypeComposition>();
} while (set.Count > 0);
return impact;
}
// gets property types that have actually changed, and the corresponding changes
// returns null if no property type has actually changed
private Dictionary<int, (ContentVariation FromVariation, ContentVariation ToVariation)> GetPropertyVariationChanges(IEnumerable<PropertyType> propertyTypes)
{
var propertyTypesL = propertyTypes.ToList();
// select the current variations (before the change) from database
var selectCurrentVariations = Sql()
.Select<PropertyTypeDto>(x => x.Id, x => x.Variations)
.From<PropertyTypeDto>()
.WhereIn<PropertyTypeDto>(x => x.Id, propertyTypesL.Select(x => x.Id));
var oldVariations = Database.Dictionary<int, byte>(selectCurrentVariations);
// build a dictionary of actual changes
Dictionary<int, (ContentVariation, ContentVariation)> changes = null;
foreach (var propertyType in propertyTypesL)
{
// new property type, ignore
if (!oldVariations.TryGetValue(propertyType.Id, out var oldVariationB))
continue;
var oldVariation = (ContentVariation) oldVariationB; // NPoco cannot fetch directly
// only those property types that *actually* changed
var newVariation = propertyType.Variations;
if (oldVariation == newVariation)
continue;
// allocate the dictionary only when needed
if (changes == null)
changes = new Dictionary<int, (ContentVariation, ContentVariation)>();
changes[propertyType.Id] = (oldVariation, newVariation);
}
return changes;
}
/// <summary>
/// Clear any redirects associated with content for a content type
/// </summary>
@@ -529,28 +633,39 @@ AND umbracoNode.id <> @id",
}
/// <summary>
/// Moves variant data for property type changes
/// Gets the default language identifier.
/// </summary>
/// <param name="propertyTypeChanges"></param>
private void MoveVariantData(IDictionary<int, (ContentVariation, ContentVariation)> propertyTypeChanges)
private int GetDefaultLanguageId()
{
var defaultLangId = Database.First<int>(Sql().Select<LanguageDto>(x => x.Id).From<LanguageDto>().Where<LanguageDto>(x => x.IsDefault));
var selectDefaultLanguageId = Sql()
.Select<LanguageDto>(x => x.Id)
.From<LanguageDto>()
.Where<LanguageDto>(x => x.IsDefault);
return Database.First<int>(selectDefaultLanguageId);
}
/// <summary>
/// Moves variant data for property type variation changes.
/// </summary>
private void MovePropertyTypeVariantData(IDictionary<int, (ContentVariation FromVariation, ContentVariation ToVariation)> propertyTypeChanges, IEnumerable<IContentTypeComposition> impacted)
{
var defaultLanguageId = GetDefaultLanguageId();
var impactedL = impacted.Select(x => x.Id).ToList();
//Group by the "To" variation so we can bulk update in the correct batches
foreach(var g in propertyTypeChanges.GroupBy(x => x.Value.Item2))
foreach(var grouping in propertyTypeChanges.GroupBy(x => x.Value.ToVariation))
{
var propertyTypeIds = g.Select(s => s.Key).ToList();
var propertyTypeIds = grouping.Select(x => x.Key).ToList();
var toVariation = grouping.Key;
//the ContentVariation that the data is moving "To"
var toVariantType = g.Key;
switch(toVariantType)
switch (toVariation)
{
case ContentVariation.Culture:
MovePropertyDataToVariantCulture(defaultLangId, propertyTypeIds: propertyTypeIds);
CopyPropertyData(null, defaultLanguageId, propertyTypeIds, impactedL);
break;
case ContentVariation.Nothing:
MovePropertyDataToVariantNothing(defaultLangId, propertyTypeIds: propertyTypeIds);
CopyPropertyData(defaultLanguageId, null, propertyTypeIds, impactedL);
break;
case ContentVariation.CultureAndSegment:
case ContentVariation.Segment:
@@ -561,24 +676,17 @@ AND umbracoNode.id <> @id",
}
/// <summary>
/// Moves variant data for a content type variation change
/// Moves variant data for a content type variation change.
/// </summary>
/// <param name="contentType"></param>
/// <param name="from"></param>
/// <param name="to"></param>
private void MoveVariantData(IContentTypeComposition contentType, ContentVariation from, ContentVariation to)
private void MoveContentTypeVariantData(IContentTypeComposition contentType, ContentVariation fromVariation, ContentVariation toVariation)
{
var defaultLangId = Database.First<int>(Sql().Select<LanguageDto>(x => x.Id).From<LanguageDto>().Where<LanguageDto>(x => x.IsDefault));
var defaultLanguageId = GetDefaultLanguageId();
var sqlPropertyTypeIds = Sql().Select<PropertyTypeDto>(x => x.Id).From<PropertyTypeDto>().Where<PropertyTypeDto>(x => x.ContentTypeId == contentType.Id);
switch (to)
switch (toVariation)
{
case ContentVariation.Culture:
//move the property data
MovePropertyDataToVariantCulture(defaultLangId, sqlPropertyTypeIds: sqlPropertyTypeIds);
//now we need to move the names
//move the names
//first clear out any existing names that might already exists under the default lang
//there's 2x tables to update
@@ -588,10 +696,11 @@ AND umbracoNode.id <> @id",
.InnerJoin<ContentVersionDto>().On<ContentVersionDto, ContentVersionCultureVariationDto>(x => x.Id, x => x.VersionId)
.InnerJoin<ContentDto>().On<ContentDto, ContentVersionDto>(x => x.NodeId, x => x.NodeId)
.Where<ContentDto>(x => x.ContentTypeId == contentType.Id)
.Where<ContentVersionCultureVariationDto>(x => x.LanguageId == defaultLangId);
.Where<ContentVersionCultureVariationDto>(x => x.LanguageId == defaultLanguageId);
var sqlDelete = Sql()
.Delete<ContentVersionCultureVariationDto>()
.WhereIn<ContentVersionCultureVariationDto>(x => x.Id, sqlSelect);
Database.Execute(sqlDelete);
//clear out the documentCultureVariation table
@@ -599,10 +708,11 @@ AND umbracoNode.id <> @id",
.From<DocumentCultureVariationDto>()
.InnerJoin<ContentDto>().On<ContentDto, DocumentCultureVariationDto>(x => x.NodeId, x => x.NodeId)
.Where<ContentDto>(x => x.ContentTypeId == contentType.Id)
.Where<DocumentCultureVariationDto>(x => x.LanguageId == defaultLangId);
.Where<DocumentCultureVariationDto>(x => x.LanguageId == defaultLanguageId);
sqlDelete = Sql()
.Delete<DocumentCultureVariationDto>()
.WhereIn<DocumentCultureVariationDto>(x => x.Id, sqlSelect);
Database.Execute(sqlDelete);
//now we need to insert names into these 2 tables based on the invariant data
@@ -610,32 +720,31 @@ AND umbracoNode.id <> @id",
//insert rows into the versionCultureVariationDto table based on the data from contentVersionDto for the default lang
var cols = Sql().Columns<ContentVersionCultureVariationDto>(x => x.VersionId, x => x.Name, x => x.UpdateUserId, x => x.UpdateDate, x => x.LanguageId);
sqlSelect = Sql().Select<ContentVersionDto>(x => x.Id, x => x.Text, x => x.UserId, x => x.VersionDate)
.Append($", {defaultLangId}") //default language ID
.Append($", {defaultLanguageId}") //default language ID
.From<ContentVersionDto>()
.InnerJoin<ContentDto>().On<ContentDto, ContentVersionDto>(x => x.NodeId, x => x.NodeId)
.Where<ContentDto>(x => x.ContentTypeId == contentType.Id);
var sqlInsert = Sql($"INSERT INTO {ContentVersionCultureVariationDto.TableName} ({cols})").Append(sqlSelect);
Database.Execute(sqlInsert);
//insert rows into the documentCultureVariation table
cols = Sql().Columns<DocumentCultureVariationDto>(x => x.NodeId, x => x.Edited, x => x.Published, x => x.Name, x => x.Available, x => x.LanguageId);
sqlSelect = Sql().Select<DocumentDto>(x => x.NodeId, x => x.Edited, x => x.Published)
.AndSelect<NodeDto>(x => x.Text)
.Append($", 1, {defaultLangId}") //make Available + default language ID
.Append($", 1, {defaultLanguageId}") //make Available + default language ID
.From<DocumentDto>()
.InnerJoin<NodeDto>().On<NodeDto, DocumentDto>(x => x.NodeId, x => x.NodeId)
.InnerJoin<ContentDto>().On<ContentDto, NodeDto>(x => x.NodeId, x => x.NodeId)
.Where<ContentDto>(x => x.ContentTypeId == contentType.Id);
sqlInsert = Sql($"INSERT INTO {DocumentCultureVariationDto.TableName} ({cols})").Append(sqlSelect);
Database.Execute(sqlInsert);
break;
case ContentVariation.Nothing:
//move the property data
MovePropertyDataToVariantNothing(defaultLangId, sqlPropertyTypeIds: sqlPropertyTypeIds);
//we dont need to move the names! this is because we always keep the invariant names with the name of the default language.
//we don't need to move the names! this is because we always keep the invariant names with the name of the default language.
//however, if we were to move names, we could do this: BUT this doesn't work with SQLCE, for that we'd have to update row by row :(
// if we want these SQL statements back, look into GIT history
@@ -649,73 +758,102 @@ AND umbracoNode.id <> @id",
}
/// <summary>
/// This will move all property data from variant to invariant
/// Copies property data from one language to another.
/// </summary>
/// <param name="defaultLangId"></param>
/// <param name="propertyTypeIds">Optional list of property type ids of the properties to be updated</param>
/// <param name="sqlPropertyTypeIds">Optional SQL statement used for the sub-query to select the properties type ids for the properties to be updated</param>
private void MovePropertyDataToVariantNothing(int defaultLangId, IReadOnlyCollection<int> propertyTypeIds = null, Sql<ISqlContext> sqlPropertyTypeIds = null)
/// <param name="sourceLanguageId">The source language (can be null ie invariant).</param>
/// <param name="targetLanguageId">The target language (can be null ie invariant)</param>
/// <param name="propertyTypeIds">The property type identifiers.</param>
/// <param name="contentTypeIds">The content type identifiers.</param>
private void CopyPropertyData(int? sourceLanguageId, int? targetLanguageId, IReadOnlyCollection<int> propertyTypeIds, IReadOnlyCollection<int> contentTypeIds = null)
{
//first clear out any existing property data that might already exists under the default lang
// fixme - should we batch then?
var whereInArgsCount = propertyTypeIds.Count + (contentTypeIds?.Count ?? 0);
if (whereInArgsCount > 2000)
throw new NotSupportedException("Too many property/content types.");
//first clear out any existing property data that might already exists under the target language
var sqlDelete = Sql()
.Delete<PropertyDataDto>()
.Where<PropertyDataDto>(x => x.LanguageId == null);
if (sqlPropertyTypeIds != null)
sqlDelete.WhereIn<PropertyDataDto>(x => x.PropertyTypeId, sqlPropertyTypeIds);
if (propertyTypeIds != null)
sqlDelete.WhereIn<PropertyDataDto>(x => x.PropertyTypeId, propertyTypeIds);
.Delete<PropertyDataDto>();
// not ok for SqlCe (no JOIN in DELETE)
//if (contentTypeIds != null)
// sqlDelete
// .From<PropertyDataDto>()
// .InnerJoin<ContentVersionDto>().On<PropertyDataDto, ContentVersionDto>((pdata, cversion) => pdata.VersionId == cversion.Id)
// .InnerJoin<ContentDto>().On<ContentVersionDto, ContentDto>((cversion, c) => cversion.NodeId == c.NodeId);
Sql<ISqlContext> inSql = null;
if (contentTypeIds != null)
{
inSql = Sql()
.Select<ContentVersionDto>(x => x.Id)
.From<ContentVersionDto>()
.InnerJoin<ContentDto>().On<ContentVersionDto, ContentDto>((cversion, c) => cversion.NodeId == c.NodeId)
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIds);
sqlDelete.WhereIn<PropertyDataDto>(x => x.VersionId, inSql);
}
// NPoco cannot turn the clause into IS NULL with a nullable parameter - deal with it
if (targetLanguageId == null)
sqlDelete.Where<PropertyDataDto>(x => x.LanguageId == null);
else
sqlDelete.Where<PropertyDataDto>(x => x.LanguageId == targetLanguageId);
sqlDelete
.WhereIn<PropertyDataDto>(x => x.PropertyTypeId, propertyTypeIds);
// see note above, not ok for SqlCe
//if (contentTypeIds != null)
// sqlDelete
// .WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIds);
Database.Execute(sqlDelete);
//now insert all property data into the default language that exists under the invariant lang
//now insert all property data into the target language that exists under the source language
var targetLanguageIdS = targetLanguageId.HasValue ? targetLanguageId.ToString() : "NULL";
var cols = Sql().Columns<PropertyDataDto>(x => x.VersionId, x => x.PropertyTypeId, x => x.Segment, x => x.IntegerValue, x => x.DecimalValue, x => x.DateValue, x => x.VarcharValue, x => x.TextValue, x => x.LanguageId);
var sqlSelectData = Sql().Select<PropertyDataDto>(x => x.VersionId, x => x.PropertyTypeId, x => x.Segment, x => x.IntegerValue, x => x.DecimalValue, x => x.DateValue, x => x.VarcharValue, x => x.TextValue)
.Append(", NULL") //null language ID
.From<PropertyDataDto>()
.Where<PropertyDataDto>(x => x.LanguageId == defaultLangId);
if (sqlPropertyTypeIds != null)
sqlSelectData.WhereIn<PropertyDataDto>(x => x.PropertyTypeId, sqlPropertyTypeIds);
if (propertyTypeIds != null)
sqlSelectData.WhereIn<PropertyDataDto>(x => x.PropertyTypeId, propertyTypeIds);
.Append(", " + targetLanguageIdS) //default language ID
.From<PropertyDataDto>();
if (contentTypeIds != null)
sqlSelectData
.InnerJoin<ContentVersionDto>().On<PropertyDataDto, ContentVersionDto>((pdata, cversion) => pdata.VersionId == cversion.Id)
.InnerJoin<ContentDto>().On<ContentVersionDto, ContentDto>((cversion, c) => cversion.NodeId == c.NodeId);
// NPoco cannot turn the clause into IS NULL with a nullable parameter - deal with it
if (sourceLanguageId == null)
sqlSelectData.Where<PropertyDataDto>(x => x.LanguageId == null);
else
sqlSelectData.Where<PropertyDataDto>(x => x.LanguageId == sourceLanguageId);
sqlSelectData
.WhereIn<PropertyDataDto>(x => x.PropertyTypeId, propertyTypeIds);
if (contentTypeIds != null)
sqlSelectData
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIds);
var sqlInsert = Sql($"INSERT INTO {PropertyDataDto.TableName} ({cols})").Append(sqlSelectData);
Database.Execute(sqlInsert);
}
/// <summary>
/// This will move all property data from invariant to variant
/// </summary>
/// <param name="defaultLangId"></param>
/// <param name="propertyTypeIds">Optional list of property type ids of the properties to be updated</param>
/// <param name="sqlPropertyTypeIds">Optional SQL statement used for the sub-query to select the properties type ids for the properties to be updated</param>
private void MovePropertyDataToVariantCulture(int defaultLangId, IReadOnlyCollection<int> propertyTypeIds = null, Sql<ISqlContext> sqlPropertyTypeIds = null)
{
//first clear out any existing property data that might already exists under the default lang
var sqlDelete = Sql()
.Delete<PropertyDataDto>()
.Where<PropertyDataDto>(x => x.LanguageId == defaultLangId);
if (sqlPropertyTypeIds != null)
sqlDelete.WhereIn<PropertyDataDto>(x => x.PropertyTypeId, sqlPropertyTypeIds);
if (propertyTypeIds != null)
sqlDelete.WhereIn<PropertyDataDto>(x => x.PropertyTypeId, propertyTypeIds);
// when copying from Culture, keep the original values around in case we want to go back
// when copying from Nothing, kill the original values, we don't want them around
if (sourceLanguageId == null)
{
sqlDelete = Sql()
.Delete<PropertyDataDto>();
Database.Execute(sqlDelete);
if (contentTypeIds != null)
sqlDelete.WhereIn<PropertyDataDto>(x => x.VersionId, inSql);
//now insert all property data into the default language that exists under the invariant lang
var cols = Sql().Columns<PropertyDataDto>(x => x.VersionId, x => x.PropertyTypeId, x => x.Segment, x => x.IntegerValue, x => x.DecimalValue, x => x.DateValue, x => x.VarcharValue, x => x.TextValue, x => x.LanguageId);
var sqlSelectData = Sql().Select<PropertyDataDto>(x => x.VersionId, x => x.PropertyTypeId, x => x.Segment, x => x.IntegerValue, x => x.DecimalValue, x => x.DateValue, x => x.VarcharValue, x => x.TextValue)
.Append($", {defaultLangId}") //default language ID
.From<PropertyDataDto>()
.Where<PropertyDataDto>(x => x.LanguageId == null);
if (sqlPropertyTypeIds != null)
sqlSelectData.WhereIn<PropertyDataDto>(x => x.PropertyTypeId, sqlPropertyTypeIds);
if (propertyTypeIds != null)
sqlSelectData.WhereIn<PropertyDataDto>(x => x.PropertyTypeId, propertyTypeIds);
var sqlInsert = Sql($"INSERT INTO {PropertyDataDto.TableName} ({cols})").Append(sqlSelectData);
sqlDelete
.Where<PropertyDataDto>(x => x.LanguageId == null)
.WhereIn<PropertyDataDto>(x => x.PropertyTypeId, propertyTypeIds);
Database.Execute(sqlInsert);
Database.Execute(sqlDelete);
}
}
private void DeletePropertyType(int contentTypeId, int propertyTypeId)
@@ -851,24 +989,6 @@ AND umbracoNode.id <> @id",
}
}
/// <inheritdoc />
public IEnumerable<TEntity> GetTypesDirectlyComposedOf(int id)
{
//fixme - this will probably be more efficient to simply load all content types and do the calculation, see GetWhereCompositionIsUsedInContentTypes
var sql = Sql()
.SelectAll()
.From<NodeDto>()
.InnerJoin<ContentType2ContentTypeDto>()
.On<NodeDto, ContentType2ContentTypeDto>(left => left.NodeId, right => right.ChildId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
.Where<ContentType2ContentTypeDto>(x => x.ParentId == id);
var dtos = Database.Fetch<NodeDto>(sql);
return dtos.Any()
? GetMany(dtos.DistinctBy(x => x.NodeId).Select(x => x.NodeId).ToArray())
: Enumerable.Empty<TEntity>();
}
internal static class ContentTypeQueryMapper
{
public class AssociatedTemplate

View File

@@ -30,12 +30,12 @@ namespace Umbraco.Core.Services
string[] filterPropertyTypes = null)
{
filterContentTypes = filterContentTypes == null
? new string[] { }
: filterContentTypes.Where(x => x.IsNullOrWhiteSpace() == false).ToArray();
? Array.Empty<string>()
: filterContentTypes.Where(x => !x.IsNullOrWhiteSpace()).ToArray();
filterPropertyTypes = filterPropertyTypes == null
? new string[] {}
: filterPropertyTypes.Where(x => x.IsNullOrWhiteSpace() == false).ToArray();
? Array.Empty<string>()
: filterPropertyTypes.Where(x => !x.IsNullOrWhiteSpace()).ToArray();
//create the full list of property types to use as the filter
//this is the combination of all property type aliases found in the content types passed in for the filter
@@ -47,7 +47,7 @@ namespace Umbraco.Core.Services
.Union(filterPropertyTypes)
.ToArray();
var sourceId = source != null ? source.Id : 0;
var sourceId = source?.Id ?? 0;
// find out if any content type uses this content type
var isUsing = allContentTypes.Where(x => x.ContentTypeComposition.Any(y => y.Id == sourceId)).ToArray();
@@ -161,6 +161,5 @@ namespace Umbraco.Core.Services
return all;
}
}
}

View File

@@ -344,37 +344,18 @@ namespace Umbraco.Core.Services.Implement
}
}
public IEnumerable<TItem> GetComposedOf(int id, IEnumerable<TItem> all)
{
return all.Where(x => x.ContentTypeComposition.Any(y => y.Id == id));
}
public IEnumerable<TItem> GetComposedOf(int id)
{
//fixme: this is essentially the same as ContentTypeServiceExtensions.GetWhereCompositionIsUsedInContentTypes which loads
// all content types to figure this out, this instead makes quite a few queries so should be replaced
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
scope.ReadLock(ReadLockIds);
// hash set handles duplicates
var composed = new HashSet<TItem>(new DelegateEqualityComparer<TItem>(
(x, y) => x.Id == y.Id,
x => x.Id.GetHashCode()));
var ids = new Stack<int>();
ids.Push(id);
while (ids.Count > 0)
{
var i = ids.Pop();
var result = Repository.GetTypesDirectlyComposedOf(i).ToArray();
foreach (var c in result)
{
composed.Add(c);
ids.Push(c.Id);
}
}
return composed.ToArray();
}
// GetAll is cheap, repository has a full dataset cache policy
// fixme - still, because it uses the cache, race conditions!
var allContentTypes = GetAll(Array.Empty<int>());
return GetComposedOf(id, allContentTypes);
}
public int Count()

View File

@@ -1,10 +1,8 @@
using System.Runtime.Remoting;
using NUnit.Framework;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using NPoco;
using Umbraco.Core;
using Umbraco.Core.Events;
using Umbraco.Core.Exceptions;
@@ -12,10 +10,9 @@ using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
using Umbraco.Tests.Testing;
using Umbraco.Core.Components;
using Umbraco.Tests.Scoping;
namespace Umbraco.Tests.Services
{

View File

@@ -0,0 +1,773 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using LightInject;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
using Umbraco.Core.Sync;
using Umbraco.Tests.Testing;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.PublishedCache.NuCache;
using Umbraco.Web.PublishedCache.NuCache.DataSource;
using Umbraco.Web.Routing;
namespace Umbraco.Tests.Services
{
[TestFixture]
[Apartment(ApartmentState.STA)]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true, WithApplication = true)]
public class ContentTypeServiceVariantsTests : TestWithSomeContentBase
{
protected override void Compose()
{
base.Compose();
// pfew - see note in ScopedNuCacheTests?
Container.RegisterSingleton<IServerMessenger, LocalServerMessenger>();
Container.RegisterSingleton(f => Mock.Of<IServerRegistrar>());
Container.RegisterCollectionBuilder<CacheRefresherCollectionBuilder>()
.Add(f => f.TryGetInstance<TypeLoader>().GetCacheRefreshers());
}
protected override IPublishedSnapshotService CreatePublishedSnapshotService()
{
var options = new PublishedSnapshotService.Options { IgnoreLocalDb = true };
var publishedSnapshotAccessor = new UmbracoContextPublishedSnapshotAccessor(Umbraco.Web.Composing.Current.UmbracoContextAccessor);
var runtimeStateMock = new Mock<IRuntimeState>();
runtimeStateMock.Setup(x => x.Level).Returns(() => RuntimeLevel.Run);
var contentTypeFactory = new PublishedContentTypeFactory(Mock.Of<IPublishedModelFactory>(), new PropertyValueConverterCollection(Array.Empty<IPropertyValueConverter>()), Mock.Of<IDataTypeService>());
//var documentRepository = Mock.Of<IDocumentRepository>();
var documentRepository = Container.GetInstance<IDocumentRepository>();
var mediaRepository = Mock.Of<IMediaRepository>();
var memberRepository = Mock.Of<IMemberRepository>();
return new PublishedSnapshotService(
options,
null,
runtimeStateMock.Object,
ServiceContext,
contentTypeFactory,
null,
publishedSnapshotAccessor,
Mock.Of<IVariationContextAccessor>(),
Logger,
ScopeProvider,
documentRepository, mediaRepository, memberRepository,
DefaultCultureAccessor,
new DatabaseDataSource(),
Container.GetInstance<IGlobalSettings>(), new SiteDomainHelper());
}
public class LocalServerMessenger : ServerMessengerBase
{
public LocalServerMessenger()
: base(false)
{ }
protected override void DeliverRemote(ICacheRefresher refresher, MessageType messageType, IEnumerable<object> ids = null, string json = null)
{
throw new NotImplementedException();
}
}
private void AssertJsonStartsWith(int id, string expected)
{
var json = GetJson(id).Replace('"', '\'');
var pos = json.IndexOf("'cultureData':", StringComparison.InvariantCultureIgnoreCase);
json = json.Substring(0, pos + "'cultureData':".Length);
Assert.AreEqual(expected, json);
}
private string GetJson(int id)
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
var selectJson = SqlContext.Sql().Select<ContentNuDto>().From<ContentNuDto>().Where<ContentNuDto>(x => x.NodeId == id && !x.Published);
var dto = scope.Database.Fetch<ContentNuDto>(selectJson).FirstOrDefault();
Assert.IsNotNull(dto);
var json = dto.Data;
return json;
}
}
[Test]
public void Change_Variations_SimpleContentType_VariantToInvariantAndBack()
{
// one simple content type, variant, with both variant and invariant properties
// can change it to invariant and back
var languageEn = new Language("en") { IsDefault = true };
ServiceContext.LocalizationService.Save(languageEn);
var languageFr = new Language("fr");
ServiceContext.LocalizationService.Save(languageFr);
var contentType = new ContentType(-1)
{
Alias = "contentType",
Name = "contentType",
Variations = ContentVariation.Culture
};
var properties = new PropertyTypeCollection(true)
{
new PropertyType("value1", ValueStorageType.Ntext)
{
Alias = "value1",
DataTypeId = -88,
Variations = ContentVariation.Culture
},
new PropertyType("value2", ValueStorageType.Ntext)
{
Alias = "value2",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
ServiceContext.ContentTypeService.Save(contentType);
var document = (IContent) new Content("document", -1, contentType);
document.SetCultureName("doc1en", "en");
document.SetCultureName("doc1fr", "fr");
document.SetValue("value1", "v1en", "en");
document.SetValue("value1", "v1fr", "fr");
document.SetValue("value2", "v2");
ServiceContext.ContentService.Save(document);
document = ServiceContext.ContentService.GetById(document.Id);
Assert.AreEqual("doc1en", document.Name);
Assert.AreEqual("doc1en", document.GetCultureName("en"));
Assert.AreEqual("doc1fr", document.GetCultureName("fr"));
Assert.AreEqual("v1en", document.GetValue("value1", "en"));
Assert.AreEqual("v1fr", document.GetValue("value1", "fr"));
Assert.AreEqual("v2", document.GetValue("value2"));
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value1':[{'culture':'en','seg':'','val':'v1en'},{'culture':'fr','seg':'','val':'v1fr'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':");
// switch content type to Nothing
contentType.Variations = ContentVariation.Nothing;
ServiceContext.ContentTypeService.Save(contentType);
document = ServiceContext.ContentService.GetById(document.Id);
Assert.AreEqual("doc1en", document.Name);
Assert.IsNull(document.GetCultureName("en"));
Assert.IsNull(document.GetCultureName("fr"));
Assert.IsNull(document.GetValue("value1", "en"));
Assert.IsNull(document.GetValue("value1", "fr"));
Assert.AreEqual("v1en", document.GetValue("value1"));
Assert.AreEqual("v2", document.GetValue("value2"));
Assert.IsFalse(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture());
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value1':[{'culture':'','seg':'','val':'v1en'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':");
// switch content back to Culture
contentType.Variations = ContentVariation.Culture;
ServiceContext.ContentTypeService.Save(contentType);
document = ServiceContext.ContentService.GetById(document.Id);
Assert.AreEqual("doc1en", document.Name);
Assert.AreEqual("doc1en", document.GetCultureName("en"));
Assert.AreEqual("doc1fr", document.GetCultureName("fr"));
Assert.IsNull(document.GetValue("value1", "en"));
Assert.IsNull(document.GetValue("value1", "fr"));
Assert.AreEqual("v1en", document.GetValue("value1"));
Assert.AreEqual("v2", document.GetValue("value2"));
Assert.IsFalse(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture());
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value1':[{'culture':'','seg':'','val':'v1en'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':");
// switch property back to Culture
contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = ContentVariation.Culture;
ServiceContext.ContentTypeService.Save(contentType);
document = ServiceContext.ContentService.GetById(document.Id);
Assert.AreEqual("doc1en", document.Name);
Assert.AreEqual("doc1en", document.GetCultureName("en"));
Assert.AreEqual("doc1fr", document.GetCultureName("fr"));
Assert.AreEqual("v1en", document.GetValue("value1", "en"));
Assert.AreEqual("v1fr", document.GetValue("value1", "fr"));
Assert.AreEqual("v2", document.GetValue("value2"));
Assert.IsTrue(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture());
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value1':[{'culture':'en','seg':'','val':'v1en'},{'culture':'fr','seg':'','val':'v1fr'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':");
}
[Test]
public void Change_Variations_SimpleContentType_InvariantToVariantAndBack()
{
// one simple content type, invariant
// can change it to variant and back
// can then switch one property to variant
var languageEn = new Language("en") { IsDefault = true };
ServiceContext.LocalizationService.Save(languageEn);
var languageFr = new Language("fr");
ServiceContext.LocalizationService.Save(languageFr);
var contentType = new ContentType(-1)
{
Alias = "contentType",
Name = "contentType",
Variations = ContentVariation.Nothing
};
var properties = new PropertyTypeCollection(true)
{
new PropertyType("value1", ValueStorageType.Ntext)
{
Alias = "value1",
DataTypeId = -88,
Variations = ContentVariation.Nothing
},
new PropertyType("value2", ValueStorageType.Ntext)
{
Alias = "value2",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
ServiceContext.ContentTypeService.Save(contentType);
var document = (IContent) new Content("document", -1, contentType);
document.Name = "doc1";
document.SetValue("value1", "v1");
document.SetValue("value2", "v2");
ServiceContext.ContentService.Save(document);
document = ServiceContext.ContentService.GetById(document.Id);
Assert.AreEqual("doc1", document.Name);
Assert.IsNull(document.GetCultureName("en"));
Assert.IsNull(document.GetCultureName("fr"));
Assert.IsNull(document.GetValue("value1", "en"));
Assert.IsNull(document.GetValue("value1", "fr"));
Assert.AreEqual("v1", document.GetValue("value1"));
Assert.AreEqual("v2", document.GetValue("value2"));
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value1':[{'culture':'','seg':'','val':'v1'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':");
// switch content type to Culture
contentType.Variations = ContentVariation.Culture;
ServiceContext.ContentTypeService.Save(contentType);
document = ServiceContext.ContentService.GetById(document.Id);
Assert.AreEqual("doc1", document.GetCultureName("en"));
Assert.IsNull(document.GetCultureName("fr"));
Assert.IsNull(document.GetValue("value1", "en"));
Assert.IsNull(document.GetValue("value1", "fr"));
Assert.AreEqual("v1", document.GetValue("value1"));
Assert.AreEqual("v2", document.GetValue("value2"));
Assert.IsFalse(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture());
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value1':[{'culture':'','seg':'','val':'v1'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':");
// switch property to Culture
contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = ContentVariation.Culture;
ServiceContext.ContentTypeService.Save(contentType);
document = ServiceContext.ContentService.GetById(document.Id);
Assert.AreEqual("doc1", document.GetCultureName("en"));
Assert.IsNull(document.GetCultureName("fr"));
Assert.AreEqual("v1", document.GetValue("value1", "en"));
Assert.IsNull(document.GetValue("value1", "fr"));
Assert.AreEqual("v2", document.GetValue("value2"));
Assert.IsTrue(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture());
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value1':[{'culture':'en','seg':'','val':'v1'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':");
// switch content back to Nothing
contentType.Variations = ContentVariation.Nothing;
ServiceContext.ContentTypeService.Save(contentType);
document = ServiceContext.ContentService.GetById(document.Id);
Assert.AreEqual("doc1", document.Name);
Assert.IsNull(document.GetCultureName("en"));
Assert.IsNull(document.GetCultureName("fr"));
Assert.IsNull(document.GetValue("value1", "en"));
Assert.IsNull(document.GetValue("value1", "fr"));
Assert.AreEqual("v1", document.GetValue("value1"));
Assert.AreEqual("v2", document.GetValue("value2"));
Assert.IsFalse(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture());
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value1':[{'culture':'','seg':'','val':'v1'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':");
}
[Test]
public void Change_Variations_SimpleContentType_VariantPropertyToInvariantAndBack()
{
// one simple content type, variant, with both variant and invariant properties
// can change an invariant property to variant and back
var languageEn = new Language("en") { IsDefault = true };
ServiceContext.LocalizationService.Save(languageEn);
var languageFr = new Language("fr");
ServiceContext.LocalizationService.Save(languageFr);
var contentType = new ContentType(-1)
{
Alias = "contentType",
Name = "contentType",
Variations = ContentVariation.Culture
};
var properties = new PropertyTypeCollection(true)
{
new PropertyType("value1", ValueStorageType.Ntext)
{
Alias = "value1",
DataTypeId = -88,
Variations = ContentVariation.Culture
},
new PropertyType("value2", ValueStorageType.Ntext)
{
Alias = "value2",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
ServiceContext.ContentTypeService.Save(contentType);
var document = (IContent)new Content("document", -1, contentType);
document.SetCultureName("doc1en", "en");
document.SetCultureName("doc1fr", "fr");
document.SetValue("value1", "v1en", "en");
document.SetValue("value1", "v1fr", "fr");
document.SetValue("value2", "v2");
ServiceContext.ContentService.Save(document);
document = ServiceContext.ContentService.GetById(document.Id);
Assert.AreEqual("doc1en", document.Name);
Assert.AreEqual("doc1en", document.GetCultureName("en"));
Assert.AreEqual("doc1fr", document.GetCultureName("fr"));
Assert.AreEqual("v1en", document.GetValue("value1", "en"));
Assert.AreEqual("v1fr", document.GetValue("value1", "fr"));
Assert.AreEqual("v2", document.GetValue("value2"));
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value1':[{'culture':'en','seg':'','val':'v1en'},{'culture':'fr','seg':'','val':'v1fr'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':");
// switch property type to Nothing
contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = ContentVariation.Nothing;
ServiceContext.ContentTypeService.Save(contentType);
document = ServiceContext.ContentService.GetById(document.Id);
Assert.AreEqual("doc1en", document.Name);
Assert.AreEqual("doc1en", document.GetCultureName("en"));
Assert.AreEqual("doc1fr", document.GetCultureName("fr"));
Assert.IsNull(document.GetValue("value1", "en"));
Assert.IsNull(document.GetValue("value1", "fr"));
Assert.AreEqual("v1en", document.GetValue("value1"));
Assert.AreEqual("v2", document.GetValue("value2"));
Assert.IsFalse(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture());
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value1':[{'culture':'','seg':'','val':'v1en'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':");
// switch property back to Culture
contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = ContentVariation.Culture;
ServiceContext.ContentTypeService.Save(contentType);
document = ServiceContext.ContentService.GetById(document.Id);
Assert.AreEqual("doc1en", document.Name);
Assert.AreEqual("doc1en", document.GetCultureName("en"));
Assert.AreEqual("doc1fr", document.GetCultureName("fr"));
Assert.AreEqual("v1en", document.GetValue("value1", "en"));
Assert.AreEqual("v1fr", document.GetValue("value1", "fr"));
Assert.AreEqual("v2", document.GetValue("value2"));
Assert.IsTrue(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture());
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value1':[{'culture':'en','seg':'','val':'v1en'},{'culture':'fr','seg':'','val':'v1fr'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':");
// switch other property to Culture
contentType.PropertyTypes.First(x => x.Alias == "value2").Variations = ContentVariation.Culture;
ServiceContext.ContentTypeService.Save(contentType);
document = ServiceContext.ContentService.GetById(document.Id);
Assert.AreEqual("doc1en", document.Name);
Assert.AreEqual("doc1en", document.GetCultureName("en"));
Assert.AreEqual("doc1fr", document.GetCultureName("fr"));
Assert.AreEqual("v1en", document.GetValue("value1", "en"));
Assert.AreEqual("v1fr", document.GetValue("value1", "fr"));
Assert.AreEqual("v2", document.GetValue("value2", "en"));
Assert.IsNull(document.GetValue("value2", "fr"));
Assert.IsNull(document.GetValue("value2"));
Assert.IsTrue(document.ContentType.PropertyTypes.First(x => x.Alias == "value2").VariesByCulture());
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value1':[{'culture':'en','seg':'','val':'v1en'},{'culture':'fr','seg':'','val':'v1fr'}],'value2':[{'culture':'en','seg':'','val':'v2'}]},'cultureData':");
}
[Test]
public void Change_Variations_ComposedContentType_1()
{
// one composing content type, variant, with both variant and invariant properties
// one composed content type, variant, with both variant and invariant properties
// can change the composing content type to invariant and back
// can change the composed content type to invariant and back
var languageEn = new Language("en") { IsDefault = true };
ServiceContext.LocalizationService.Save(languageEn);
var languageFr = new Language("fr");
ServiceContext.LocalizationService.Save(languageFr);
var composing = new ContentType(-1)
{
Alias = "composing",
Name = "composing",
Variations = ContentVariation.Culture
};
var properties1 = new PropertyTypeCollection(true)
{
new PropertyType("value11", ValueStorageType.Ntext)
{
Alias = "value11",
DataTypeId = -88,
Variations = ContentVariation.Culture
},
new PropertyType("value12", ValueStorageType.Ntext)
{
Alias = "value12",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
composing.PropertyGroups.Add(new PropertyGroup(properties1) { Name = "Content" });
ServiceContext.ContentTypeService.Save(composing);
var composed = new ContentType(-1)
{
Alias = "composed",
Name = "composed",
Variations = ContentVariation.Culture
};
var properties2 = new PropertyTypeCollection(true)
{
new PropertyType("value21", ValueStorageType.Ntext)
{
Alias = "value21",
DataTypeId = -88,
Variations = ContentVariation.Culture
},
new PropertyType("value22", ValueStorageType.Ntext)
{
Alias = "value22",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
composed.PropertyGroups.Add(new PropertyGroup(properties2) { Name = "Content" });
composed.AddContentType(composing);
ServiceContext.ContentTypeService.Save(composed);
var document = (IContent) new Content("document", -1, composed);
document.SetCultureName("doc1en", "en");
document.SetCultureName("doc1fr", "fr");
document.SetValue("value11", "v11en", "en");
document.SetValue("value11", "v11fr", "fr");
document.SetValue("value12", "v12");
document.SetValue("value21", "v21en", "en");
document.SetValue("value21", "v21fr", "fr");
document.SetValue("value22", "v22");
ServiceContext.ContentService.Save(document);
// both value11 and value21 are variant
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value11':[{'culture':'en','seg':'','val':'v11en'},{'culture':'fr','seg':'','val':'v11fr'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value21':[{'culture':'en','seg':'','val':'v21en'},{'culture':'fr','seg':'','val':'v21fr'}],'value22':[{'culture':'','seg':'','val':'v22'}]},'cultureData':");
composed.Variations = ContentVariation.Nothing;
ServiceContext.ContentTypeService.Save(composed);
// both value11 and value21 are invariant
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value11':[{'culture':'','seg':'','val':'v11en'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value21':[{'culture':'','seg':'','val':'v21en'}],'value22':[{'culture':'','seg':'','val':'v22'}]},'cultureData':");
composed.Variations = ContentVariation.Culture;
ServiceContext.ContentTypeService.Save(composed);
// value11 is variant again, but value21 is still invariant
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value11':[{'culture':'en','seg':'','val':'v11en'},{'culture':'fr','seg':'','val':'v11fr'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value21':[{'culture':'','seg':'','val':'v21en'}],'value22':[{'culture':'','seg':'','val':'v22'}]},'cultureData':");
composed.PropertyTypes.First(x => x.Alias == "value21").Variations = ContentVariation.Culture;
ServiceContext.ContentTypeService.Save(composed);
// we can make it variant again
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value11':[{'culture':'en','seg':'','val':'v11en'},{'culture':'fr','seg':'','val':'v11fr'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value21':[{'culture':'en','seg':'','val':'v21en'},{'culture':'fr','seg':'','val':'v21fr'}],'value22':[{'culture':'','seg':'','val':'v22'}]},'cultureData':");
composing.Variations = ContentVariation.Nothing;
ServiceContext.ContentTypeService.Save(composing);
// value11 is invariant
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value11':[{'culture':'','seg':'','val':'v11en'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value21':[{'culture':'en','seg':'','val':'v21en'},{'culture':'fr','seg':'','val':'v21fr'}],'value22':[{'culture':'','seg':'','val':'v22'}]},'cultureData':");
composing.Variations = ContentVariation.Culture;
ServiceContext.ContentTypeService.Save(composing);
// value11 is still invariant
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value11':[{'culture':'','seg':'','val':'v11en'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value21':[{'culture':'en','seg':'','val':'v21en'},{'culture':'fr','seg':'','val':'v21fr'}],'value22':[{'culture':'','seg':'','val':'v22'}]},'cultureData':");
composing.PropertyTypes.First(x => x.Alias == "value11").Variations = ContentVariation.Culture;
ServiceContext.ContentTypeService.Save(composing);
// we can make it variant again
Console.WriteLine(GetJson(document.Id));
AssertJsonStartsWith(document.Id,
"{'properties':{'value11':[{'culture':'en','seg':'','val':'v11en'},{'culture':'fr','seg':'','val':'v11fr'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value21':[{'culture':'en','seg':'','val':'v21en'},{'culture':'fr','seg':'','val':'v21fr'}],'value22':[{'culture':'','seg':'','val':'v22'}]},'cultureData':");
}
[Test]
public void Change_Variations_ComposedContentType_2()
{
// one composing content type, variant, with both variant and invariant properties
// one composed content type, variant, with both variant and invariant properties
// one composed content type, invariant
// can change the composing content type to invariant and back
// can change the variant composed content type to invariant and back
var languageEn = new Language("en") { IsDefault = true };
ServiceContext.LocalizationService.Save(languageEn);
var languageFr = new Language("fr");
ServiceContext.LocalizationService.Save(languageFr);
var composing = new ContentType(-1)
{
Alias = "composing",
Name = "composing",
Variations = ContentVariation.Culture
};
var properties1 = new PropertyTypeCollection(true)
{
new PropertyType("value11", ValueStorageType.Ntext)
{
Alias = "value11",
DataTypeId = -88,
Variations = ContentVariation.Culture
},
new PropertyType("value12", ValueStorageType.Ntext)
{
Alias = "value12",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
composing.PropertyGroups.Add(new PropertyGroup(properties1) { Name = "Content" });
ServiceContext.ContentTypeService.Save(composing);
var composed1 = new ContentType(-1)
{
Alias = "composed1",
Name = "composed1",
Variations = ContentVariation.Culture
};
var properties2 = new PropertyTypeCollection(true)
{
new PropertyType("value21", ValueStorageType.Ntext)
{
Alias = "value21",
DataTypeId = -88,
Variations = ContentVariation.Culture
},
new PropertyType("value22", ValueStorageType.Ntext)
{
Alias = "value22",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
composed1.PropertyGroups.Add(new PropertyGroup(properties2) { Name = "Content" });
composed1.AddContentType(composing);
ServiceContext.ContentTypeService.Save(composed1);
var composed2 = new ContentType(-1)
{
Alias = "composed2",
Name = "composed2",
Variations = ContentVariation.Nothing
};
var properties3 = new PropertyTypeCollection(true)
{
new PropertyType("value31", ValueStorageType.Ntext)
{
Alias = "value31",
DataTypeId = -88,
Variations = ContentVariation.Nothing
},
new PropertyType("value32", ValueStorageType.Ntext)
{
Alias = "value32",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
composed2.PropertyGroups.Add(new PropertyGroup(properties3) { Name = "Content" });
composed2.AddContentType(composing);
ServiceContext.ContentTypeService.Save(composed2);
var document1 = (IContent) new Content ("document1", -1, composed1);
document1.SetCultureName("doc1en", "en");
document1.SetCultureName("doc1fr", "fr");
document1.SetValue("value11", "v11en", "en");
document1.SetValue("value11", "v11fr", "fr");
document1.SetValue("value12", "v12");
document1.SetValue("value21", "v21en", "en");
document1.SetValue("value21", "v21fr", "fr");
document1.SetValue("value22", "v22");
ServiceContext.ContentService.Save(document1);
var document2 = (IContent)new Content("document2", -1, composed2);
document2.Name = "doc2";
document2.SetValue("value11", "v11");
document2.SetValue("value12", "v12");
document2.SetValue("value31", "v31");
document2.SetValue("value32", "v32");
ServiceContext.ContentService.Save(document2);
// both value11 and value21 are variant
Console.WriteLine(GetJson(document1.Id));
AssertJsonStartsWith(document1.Id,
"{'properties':{'value11':[{'culture':'en','seg':'','val':'v11en'},{'culture':'fr','seg':'','val':'v11fr'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value21':[{'culture':'en','seg':'','val':'v21en'},{'culture':'fr','seg':'','val':'v21fr'}],'value22':[{'culture':'','seg':'','val':'v22'}]},'cultureData':");
Console.WriteLine(GetJson(document2.Id));
AssertJsonStartsWith(document2.Id,
"{'properties':{'value11':[{'culture':'','seg':'','val':'v11'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value31':[{'culture':'','seg':'','val':'v31'}],'value32':[{'culture':'','seg':'','val':'v32'}]},'cultureData':");
composed1.Variations = ContentVariation.Nothing;
ServiceContext.ContentTypeService.Save(composed1);
// both value11 and value21 are invariant
Console.WriteLine(GetJson(document1.Id));
AssertJsonStartsWith(document1.Id,
"{'properties':{'value11':[{'culture':'','seg':'','val':'v11en'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value21':[{'culture':'','seg':'','val':'v21en'}],'value22':[{'culture':'','seg':'','val':'v22'}]},'cultureData':");
Console.WriteLine(GetJson(document2.Id));
AssertJsonStartsWith(document2.Id,
"{'properties':{'value11':[{'culture':'','seg':'','val':'v11'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value31':[{'culture':'','seg':'','val':'v31'}],'value32':[{'culture':'','seg':'','val':'v32'}]},'cultureData':");
composed1.Variations = ContentVariation.Culture;
ServiceContext.ContentTypeService.Save(composed1);
// value11 is variant again, but value21 is still invariant
Console.WriteLine(GetJson(document1.Id));
AssertJsonStartsWith(document1.Id,
"{'properties':{'value11':[{'culture':'en','seg':'','val':'v11en'},{'culture':'fr','seg':'','val':'v11fr'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value21':[{'culture':'','seg':'','val':'v21en'}],'value22':[{'culture':'','seg':'','val':'v22'}]},'cultureData':");
Console.WriteLine(GetJson(document2.Id));
AssertJsonStartsWith(document2.Id,
"{'properties':{'value11':[{'culture':'','seg':'','val':'v11'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value31':[{'culture':'','seg':'','val':'v31'}],'value32':[{'culture':'','seg':'','val':'v32'}]},'cultureData':");
composed1.PropertyTypes.First(x => x.Alias == "value21").Variations = ContentVariation.Culture;
ServiceContext.ContentTypeService.Save(composed1);
// we can make it variant again
Console.WriteLine(GetJson(document1.Id));
AssertJsonStartsWith(document1.Id,
"{'properties':{'value11':[{'culture':'en','seg':'','val':'v11en'},{'culture':'fr','seg':'','val':'v11fr'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value21':[{'culture':'en','seg':'','val':'v21en'},{'culture':'fr','seg':'','val':'v21fr'}],'value22':[{'culture':'','seg':'','val':'v22'}]},'cultureData':");
Console.WriteLine(GetJson(document2.Id));
AssertJsonStartsWith(document2.Id,
"{'properties':{'value11':[{'culture':'','seg':'','val':'v11'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value31':[{'culture':'','seg':'','val':'v31'}],'value32':[{'culture':'','seg':'','val':'v32'}]},'cultureData':");
composing.Variations = ContentVariation.Nothing;
ServiceContext.ContentTypeService.Save(composing);
// value11 is invariant
Console.WriteLine(GetJson(document1.Id));
AssertJsonStartsWith(document1.Id,
"{'properties':{'value11':[{'culture':'','seg':'','val':'v11en'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value21':[{'culture':'en','seg':'','val':'v21en'},{'culture':'fr','seg':'','val':'v21fr'}],'value22':[{'culture':'','seg':'','val':'v22'}]},'cultureData':");
Console.WriteLine(GetJson(document2.Id));
AssertJsonStartsWith(document2.Id,
"{'properties':{'value11':[{'culture':'','seg':'','val':'v11'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value31':[{'culture':'','seg':'','val':'v31'}],'value32':[{'culture':'','seg':'','val':'v32'}]},'cultureData':");
composing.Variations = ContentVariation.Culture;
ServiceContext.ContentTypeService.Save(composing);
// value11 is still invariant
Console.WriteLine(GetJson(document1.Id));
AssertJsonStartsWith(document1.Id,
"{'properties':{'value11':[{'culture':'','seg':'','val':'v11en'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value21':[{'culture':'en','seg':'','val':'v21en'},{'culture':'fr','seg':'','val':'v21fr'}],'value22':[{'culture':'','seg':'','val':'v22'}]},'cultureData':");
Console.WriteLine(GetJson(document2.Id));
AssertJsonStartsWith(document2.Id,
"{'properties':{'value11':[{'culture':'','seg':'','val':'v11'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value31':[{'culture':'','seg':'','val':'v31'}],'value32':[{'culture':'','seg':'','val':'v32'}]},'cultureData':");
composing.PropertyTypes.First(x => x.Alias == "value11").Variations = ContentVariation.Culture;
ServiceContext.ContentTypeService.Save(composing);
// we can make it variant again
Console.WriteLine(GetJson(document1.Id));
AssertJsonStartsWith(document1.Id,
"{'properties':{'value11':[{'culture':'en','seg':'','val':'v11en'},{'culture':'fr','seg':'','val':'v11fr'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value21':[{'culture':'en','seg':'','val':'v21en'},{'culture':'fr','seg':'','val':'v21fr'}],'value22':[{'culture':'','seg':'','val':'v22'}]},'cultureData':");
Console.WriteLine(GetJson(document2.Id));
AssertJsonStartsWith(document2.Id,
"{'properties':{'value11':[{'culture':'','seg':'','val':'v11'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value31':[{'culture':'','seg':'','val':'v31'}],'value32':[{'culture':'','seg':'','val':'v32'}]},'cultureData':");
}
}
}

View File

@@ -129,6 +129,7 @@
<Compile Include="PublishedContent\PublishedContentSnapshotTestBase.cs" />
<Compile Include="PublishedContent\SolidPublishedSnapshot.cs" />
<Compile Include="PublishedContent\NuCacheTests.cs" />
<Compile Include="Services\ContentTypeServiceVariantsTests.cs" />
<Compile Include="Testing\Objects\TestDataSource.cs" />
<Compile Include="Published\PublishedSnapshotTestObjects.cs" />
<Compile Include="Published\ModelTypeTests.cs" />

View File

@@ -348,8 +348,6 @@
<Content Include="Umbraco\Developer\Packages\directoryBrowser.aspx" />
<Content Include="Umbraco\Developer\Packages\editPackage.aspx" />
<Content Include="Umbraco\Dialogs\protectPage.aspx" />
<Content Include="Umbraco\Dialogs\rollBack.aspx" />
<Content Include="Umbraco\Dialogs\sendToTranslation.aspx" />
<Content Include="Umbraco\Config\Create\UI.xml" />
<Content Include="Umbraco\Config\Lang\en.xml">
<SubType>Designer</SubType>

View File

@@ -119,66 +119,74 @@ namespace Umbraco.Web.Editors
/// <param name="type">Type of content Type, eg documentType or mediaType</param>
/// <param name="contentTypeId">Id of composition content type</param>
/// <returns></returns>
protected IEnumerable<EntityBasic> PerformGetWhereCompositionIsUsedInContentTypes(int contentTypeId,
UmbracoObjectTypes type)
protected IEnumerable<EntityBasic> PerformGetWhereCompositionIsUsedInContentTypes(int contentTypeId, UmbracoObjectTypes type)
{
IContentTypeComposition source = null;
var id = 0;
//below is all ported from the old doc type editor and comes with the same weaknesses /insanity / magic
if (contentTypeId > 0)
{
IContentTypeComposition source;
IContentTypeComposition[] allContentTypes;
switch (type)
{
case UmbracoObjectTypes.DocumentType:
source = Services.ContentTypeService.Get(contentTypeId);
break;
case UmbracoObjectTypes.MediaType:
source = Services.ContentTypeService.Get(contentTypeId);
break;
case UmbracoObjectTypes.MemberType:
source = Services.MemberTypeService.Get(contentTypeId);
break;
default:
throw new ArgumentOutOfRangeException(nameof(type));
}
if (source == null)
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
id = source.Id;
}
IEnumerable<IContentTypeComposition> composedOf;
switch (type)
{
case UmbracoObjectTypes.DocumentType:
if (contentTypeId > 0)
{
source = Services.ContentTypeService.Get(contentTypeId);
if (source == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
allContentTypes = Services.ContentTypeService.GetAll().Cast<IContentTypeComposition>().ToArray();
composedOf = Services.ContentTypeService.GetComposedOf(id);
break;
case UmbracoObjectTypes.MediaType:
if (contentTypeId > 0)
{
source = Services.ContentTypeService.Get(contentTypeId);
if (source == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
allContentTypes = Services.ContentTypeService.GetAll().Cast<IContentTypeComposition>().ToArray();
composedOf = Services.MediaTypeService.GetComposedOf(id);
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();
composedOf = Services.MemberTypeService.GetComposedOf(id);
break;
default:
throw new ArgumentOutOfRangeException("The entity type was not a content type");
throw new ArgumentOutOfRangeException(nameof(type));
}
var contentTypesWhereCompositionIsUsed = source.GetWhereCompositionIsUsedInContentTypes(allContentTypes);
return contentTypesWhereCompositionIsUsed
.Select(x => Mapper.Map<IContentTypeComposition, EntityBasic>(x))
.Select(x =>
{
//translate the name
x.Name = TranslateItem(x.Name);
EntityBasic TranslateName(EntityBasic e)
{
e.Name = TranslateItem(e.Name);
return e;
}
return x;
})
return composedOf
.Select(Mapper.Map<IContentTypeComposition, EntityBasic>)
.Select(TranslateName)
.ToList();
}
protected string TranslateItem(string text)
{
if (text == null)
{
return null;
}
if (text.StartsWith("#") == false)
return text;

View File

@@ -1160,6 +1160,10 @@ namespace Umbraco.Web.PublishedCache.NuCache
var pdatas = new List<PropertyData>();
foreach (var pvalue in prop.Values)
{
// sanitize - properties should be ok but ... never knows
if (!prop.PropertyType.SupportsVariation(pvalue.Culture, pvalue.Segment))
continue;
// note: at service level, invariant is 'null', but here invariant becomes 'string.Empty'
var value = published ? pvalue.PublishedValue : pvalue.EditedValue;
if (value != null)
@@ -1191,15 +1195,19 @@ namespace Umbraco.Web.PublishedCache.NuCache
var cultureData = new Dictionary<string, CultureVariation>();
var names = content is IContent document
// sanitize - names should be ok but ... never knows
if (content.GetContentType().VariesByCulture())
{
var infos = content is IContent document
? (published
? document.PublishCultureInfos
: document.CultureInfos)
: content.CultureInfos;
foreach (var (culture, name) in names)
{
cultureData[culture] = new CultureVariation { Name = name.Name, Date = content.GetUpdateDate(culture) ?? DateTime.MinValue };
foreach (var (culture, info) in infos)
{
cultureData[culture] = new CultureVariation { Name = info.Name, Date = content.GetUpdateDate(culture) ?? DateTime.MinValue };
}
}
//the dictionary that will be serialized