Handle tags when variations change
This commit is contained in:
@@ -595,7 +595,6 @@ namespace Umbraco.Core
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to serialize the value to an XmlString using ToXmlString
|
||||
/// </summary>
|
||||
@@ -788,5 +787,20 @@ namespace Umbraco.Core
|
||||
|
||||
return BoolConvertCache[type] = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether two nullable values are equal, substituting a fallback value for nulls.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The nullable type.</typeparam>
|
||||
/// <param name="value">The value to compare.</param>
|
||||
/// <param name="other">The value to compare to.</param>
|
||||
/// <param name="fallbackValue">The value to use when any value is null.</param>
|
||||
/// <remarks>Do not use outside of Sql expressions.</remarks>
|
||||
// see usage in ExpressionVisitorBase
|
||||
public static bool NEquals<T>(this T? value, T? other, T fallbackValue)
|
||||
where T : struct
|
||||
{
|
||||
return (value ?? fallbackValue).Equals(other ?? fallbackValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,13 @@ using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Dtos
|
||||
{
|
||||
[TableName(Constants.DatabaseSchema.Tables.TagRelationship)]
|
||||
[TableName(TableName)]
|
||||
[PrimaryKey("nodeId", AutoIncrement = false)]
|
||||
[ExplicitColumns]
|
||||
internal class TagRelationshipDto
|
||||
{
|
||||
public const string TableName = Constants.DatabaseSchema.Tables.TagRelationship;
|
||||
|
||||
[Column("nodeId")]
|
||||
[PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsTagRelationship", OnColumns = "nodeId, propertyTypeId, tagId")]
|
||||
[ForeignKey(typeof(ContentDto), Name = "FK_cmsTagRelationship_cmsContent", Column = "nodeId")]
|
||||
|
||||
@@ -653,6 +653,23 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
else
|
||||
throw new NotSupportedException("Expression is not a proper lambda.");
|
||||
|
||||
// c# 'x == null' becomes sql 'x IS NULL' which is fine
|
||||
// c# 'x == y' becomes sql 'x = @0' which is fine - unless they are nullable types,
|
||||
// because sql 'x = NULL' is always false and the 'IS NULL' syntax is required,
|
||||
// so for comparing nullable types, we use x.NEquals(y, fb) where fb is a fallback
|
||||
// value which will be used when values are null - turning the comparison into
|
||||
// sql 'COALESCE(x,fb) = COALESCE(y,fb)' - of course, fb must be a value outside
|
||||
// of x and y range - and if that is not possible, then a manual comparison need
|
||||
// to be written
|
||||
//TODO support NEquals with 0 parameters, using the full syntax below
|
||||
case "NEquals":
|
||||
var compareTo = Visit(m.Arguments[1]);
|
||||
var fallback = Visit(m.Arguments[2]);
|
||||
// that would work without a fallback value but is more cumbersome
|
||||
//return Visited ? string.Empty : $"((({compareTo} is null) AND ({visitedMethodObject} is null)) OR (({compareTo} is not null) AND ({visitedMethodObject} = {compareTo})))";
|
||||
// use a fallback value
|
||||
return Visited ? string.Empty : $"(COALESCE({visitedMethodObject},{fallback}) = COALESCE({compareTo},{fallback}))";
|
||||
|
||||
default:
|
||||
|
||||
throw new ArgumentOutOfRangeException("No logic supported for " + m.Method.Name);
|
||||
|
||||
@@ -190,8 +190,8 @@ AND umbracoNode.nodeObjectType = @objectType",
|
||||
SortOrder = allowedContentType.SortOrder
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//Insert Tabs
|
||||
foreach (var propertyGroup in entity.PropertyGroups)
|
||||
{
|
||||
@@ -620,7 +620,7 @@ AND umbracoNode.id <> @id",
|
||||
var sqlDelete = Sql()
|
||||
.Delete<RedirectUrlDto>()
|
||||
.WhereIn((System.Linq.Expressions.Expression<Func<RedirectUrlDto, object>>)(x => x.ContentKey), sqlSelect);
|
||||
|
||||
|
||||
Database.Execute(sqlDelete);
|
||||
}
|
||||
|
||||
@@ -663,9 +663,11 @@ AND umbracoNode.id <> @id",
|
||||
{
|
||||
case ContentVariation.Culture:
|
||||
CopyPropertyData(null, defaultLanguageId, propertyTypeIds, impactedL);
|
||||
CopyTagData(null, defaultLanguageId, propertyTypeIds, impactedL);
|
||||
break;
|
||||
case ContentVariation.Nothing:
|
||||
CopyPropertyData(defaultLanguageId, null, propertyTypeIds, impactedL);
|
||||
CopyTagData(defaultLanguageId, null, propertyTypeIds, impactedL);
|
||||
break;
|
||||
case ContentVariation.CultureAndSegment:
|
||||
case ContentVariation.Segment:
|
||||
@@ -690,7 +692,7 @@ AND umbracoNode.id <> @id",
|
||||
//first clear out any existing names that might already exists under the default lang
|
||||
//there's 2x tables to update
|
||||
|
||||
//clear out the versionCultureVariation table
|
||||
//clear out the versionCultureVariation table
|
||||
var sqlSelect = Sql().Select<ContentVersionCultureVariationDto>(x => x.Id)
|
||||
.From<ContentVersionCultureVariationDto>()
|
||||
.InnerJoin<ContentVersionDto>().On<ContentVersionDto, ContentVersionCultureVariationDto>(x => x.Id, x => x.VersionId)
|
||||
@@ -757,6 +759,129 @@ AND umbracoNode.id <> @id",
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
private void CopyTagData(int? sourceLanguageId, int? targetLanguageId, IReadOnlyCollection<int> propertyTypeIds, IReadOnlyCollection<int> contentTypeIds = null)
|
||||
{
|
||||
// note: important to use NEquals for nullable types, cannot directly compare language identifiers
|
||||
|
||||
// fixme - should we batch then?
|
||||
var whereInArgsCount = propertyTypeIds.Count + (contentTypeIds?.Count ?? 0);
|
||||
if (whereInArgsCount > 2000)
|
||||
throw new NotSupportedException("Too many property/content types.");
|
||||
|
||||
// delete existing relations (for target language)
|
||||
// do *not* delete existing tags
|
||||
|
||||
var sqlTagToDelete = Sql()
|
||||
.Select<TagDto>(x => x.Id)
|
||||
.From<TagDto>()
|
||||
.InnerJoin<TagRelationshipDto>().On<TagDto, TagRelationshipDto>((tag, rel) => tag.Id == rel.TagId);
|
||||
|
||||
if (contentTypeIds != null)
|
||||
sqlTagToDelete
|
||||
.InnerJoin<ContentDto>().On<TagRelationshipDto, ContentDto>((rel, content) => rel.NodeId == content.NodeId)
|
||||
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIds);
|
||||
|
||||
sqlTagToDelete
|
||||
.WhereIn<TagRelationshipDto>(x => x.PropertyTypeId, propertyTypeIds)
|
||||
.Where<TagDto>(x => x.LanguageId.NEquals(targetLanguageId, -1));
|
||||
|
||||
var sqlDeleteRel = Sql()
|
||||
.Delete<TagRelationshipDto>()
|
||||
.WhereIn<TagRelationshipDto>(x => x.TagId, sqlTagToDelete);
|
||||
|
||||
sqlDeleteRel.WriteToConsole();
|
||||
Database.Execute(sqlDeleteRel);
|
||||
|
||||
// do *not* delete the tags - they could be used by other content types / property types
|
||||
/*
|
||||
var sqlDeleteTag = Sql()
|
||||
.Delete<TagDto>()
|
||||
.WhereIn<TagDto>(x => x.Id, sqlTagToDelete);
|
||||
Database.Execute(sqlDeleteTag);
|
||||
*/
|
||||
|
||||
// copy tags from source language to target language
|
||||
|
||||
var targetLanguageIdS = targetLanguageId.HasValue ? targetLanguageId.ToString() : "NULL";
|
||||
var sqlSelect = Sql()
|
||||
.Select<TagDto>(x => x.Text, x => x.Group)
|
||||
.Append(", " + targetLanguageIdS)
|
||||
.From<TagDto>();
|
||||
|
||||
sqlSelect
|
||||
.InnerJoin<TagRelationshipDto>().On<TagDto, TagRelationshipDto>((tag, rel) => tag.Id == rel.TagId)
|
||||
.LeftJoin<TagDto>("xtags").On<TagDto, TagDto>((tag, xtag) => tag.Text == xtag.Text && tag.Group == xtag.Group && tag.LanguageId.NEquals(targetLanguageId, -1), aliasRight: "xtags");
|
||||
|
||||
if (contentTypeIds != null)
|
||||
sqlSelect
|
||||
.InnerJoin<ContentDto>().On<TagRelationshipDto, ContentDto>((rel, content) => rel.NodeId == content.NodeId);
|
||||
|
||||
sqlSelect
|
||||
.WhereIn<TagRelationshipDto>(x => x.PropertyTypeId, propertyTypeIds)
|
||||
.WhereNull<TagDto>(x => x.Id, "xtags"); // ie, not exists
|
||||
|
||||
if (contentTypeIds != null)
|
||||
sqlSelect
|
||||
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIds);
|
||||
|
||||
sqlSelect.Where<TagDto>(x => x.LanguageId.NEquals(sourceLanguageId, -1));
|
||||
|
||||
var cols = Sql().Columns<TagDto>(x => x.Text, x => x.Group, x => x.LanguageId);
|
||||
var sqlInsertTag = Sql($"INSERT INTO {TagDto.TableName} ({cols})").Append(sqlSelect);
|
||||
|
||||
sqlInsertTag.WriteToConsole();
|
||||
Database.Execute(sqlInsertTag);
|
||||
|
||||
// create relations to new tags
|
||||
|
||||
var sqlFoo = Sql()
|
||||
.Select<TagRelationshipDto>(x => x.NodeId, x => x.PropertyTypeId)
|
||||
.AndSelect<TagDto>("otag", x => x.Id)
|
||||
.From<TagRelationshipDto>()
|
||||
.InnerJoin<TagDto>().On<TagRelationshipDto, TagDto>((rel, tag) => rel.TagId == tag.Id)
|
||||
.InnerJoin<TagDto>("otag").On<TagDto, TagDto>((tag, otag) => tag.Text == otag.Text && tag.Group == otag.Group && otag.LanguageId.NEquals(targetLanguageId, -1), aliasRight: "otag")
|
||||
.Where<TagDto>(x => x.LanguageId.NEquals(sourceLanguageId, -1));
|
||||
|
||||
var cols2 = Sql().Columns<TagRelationshipDto>(x => x.NodeId, x => x.PropertyTypeId, x => x.TagId);
|
||||
var sqlInsertRel = Sql($"INSERT INTO {TagRelationshipDto.TableName} ({cols2})").Append(sqlFoo);
|
||||
|
||||
sqlInsertRel.WriteToConsole();
|
||||
Database.Execute(sqlInsertRel);
|
||||
|
||||
// delete original relations - *not* the tags - all of them
|
||||
// cannot really "go back" with relations, would have to do it with property values
|
||||
|
||||
sqlTagToDelete = Sql()
|
||||
.Select<TagDto>(x => x.Id)
|
||||
.From<TagDto>()
|
||||
.InnerJoin<TagRelationshipDto>().On<TagDto, TagRelationshipDto>((tag, rel) => tag.Id == rel.TagId);
|
||||
|
||||
if (contentTypeIds != null)
|
||||
sqlTagToDelete
|
||||
.InnerJoin<ContentDto>().On<TagRelationshipDto, ContentDto>((rel, content) => rel.NodeId == content.NodeId)
|
||||
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIds);
|
||||
|
||||
sqlTagToDelete
|
||||
.WhereIn<TagRelationshipDto>(x => x.PropertyTypeId, propertyTypeIds)
|
||||
.Where<TagDto>(x => !x.LanguageId.NEquals(targetLanguageId, -1));
|
||||
|
||||
sqlDeleteRel = Sql()
|
||||
.Delete<TagRelationshipDto>()
|
||||
.WhereIn<TagRelationshipDto>(x => x.TagId, sqlTagToDelete);
|
||||
|
||||
sqlDeleteRel.WriteToConsole();
|
||||
Database.Execute(sqlDeleteRel);
|
||||
|
||||
// no
|
||||
/*
|
||||
var sqlDeleteTag = Sql()
|
||||
.Delete<TagDto>()
|
||||
.WhereIn<TagDto>(x => x.Id, sqlTagToDelete);
|
||||
Database.Execute(sqlDeleteTag);
|
||||
*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies property data from one language to another.
|
||||
/// </summary>
|
||||
@@ -766,6 +891,8 @@ AND umbracoNode.id <> @id",
|
||||
/// <param name="contentTypeIds">The content type identifiers.</param>
|
||||
private void CopyPropertyData(int? sourceLanguageId, int? targetLanguageId, IReadOnlyCollection<int> propertyTypeIds, IReadOnlyCollection<int> contentTypeIds = null)
|
||||
{
|
||||
// note: important to use NEquals for nullable types, cannot directly compare language identifiers
|
||||
//
|
||||
// fixme - should we batch then?
|
||||
var whereInArgsCount = propertyTypeIds.Count + (contentTypeIds?.Count ?? 0);
|
||||
if (whereInArgsCount > 2000)
|
||||
@@ -793,11 +920,7 @@ AND umbracoNode.id <> @id",
|
||||
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.Where<PropertyDataDto>(x => x.LanguageId.NEquals(targetLanguageId, -1));
|
||||
|
||||
sqlDelete
|
||||
.WhereIn<PropertyDataDto>(x => x.PropertyTypeId, propertyTypeIds);
|
||||
@@ -821,11 +944,7 @@ AND umbracoNode.id <> @id",
|
||||
.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.Where<PropertyDataDto>(x => x.LanguageId.NEquals(sourceLanguageId, -1));
|
||||
|
||||
sqlSelectData
|
||||
.WhereIn<PropertyDataDto>(x => x.PropertyTypeId, propertyTypeIds);
|
||||
|
||||
@@ -67,6 +67,24 @@ namespace Umbraco.Core.Persistence
|
||||
/// <inheritdoc />
|
||||
public ISqlContext SqlContext { get; }
|
||||
|
||||
#region Temp
|
||||
|
||||
// work around NPoco issue https://github.com/schotime/NPoco/issues/517 while we wait for the fix
|
||||
public override DbCommand CreateCommand(DbConnection connection, CommandType commandType, string sql, params object[] args)
|
||||
{
|
||||
var command = base.CreateCommand(connection, commandType, sql, args);
|
||||
|
||||
if (!DatabaseType.IsSqlCe()) return command;
|
||||
|
||||
foreach (DbParameter parameter in command.Parameters)
|
||||
if (parameter.Value == DBNull.Value)
|
||||
parameter.DbType = DbType.String;
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Testing, Debugging and Troubleshooting
|
||||
|
||||
private bool _enableCount;
|
||||
@@ -242,7 +260,9 @@ namespace Umbraco.Core.Persistence
|
||||
sb.Append(" @");
|
||||
sb.Append(i++);
|
||||
sb.Append(":");
|
||||
sb.Append(arg);
|
||||
if (arg == DBNull.Value) sb.Append("<dbNull>");
|
||||
else if (arg == null) sb.Append("<null>");
|
||||
else sb.Append(arg);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using NPoco;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
@@ -11,6 +12,80 @@ namespace Umbraco.Tests.Persistence.NPocoTests
|
||||
[TestFixture]
|
||||
public class NPocoSqlExtensionsTests : BaseUsingSqlCeSyntax
|
||||
{
|
||||
[Test]
|
||||
public void WhereTest()
|
||||
{
|
||||
var sql = new Sql<ISqlContext>(SqlContext)
|
||||
.Select("*")
|
||||
.From<PropertyDataDto>()
|
||||
.Where<PropertyDataDto>(x => x.LanguageId == null);
|
||||
Assert.AreEqual("SELECT *\nFROM [umbracoPropertyData]\nWHERE (([umbracoPropertyData].[languageId] is null))", sql.SQL, sql.SQL);
|
||||
|
||||
sql = new Sql<ISqlContext>(SqlContext)
|
||||
.Select("*")
|
||||
.From<PropertyDataDto>()
|
||||
.Where<PropertyDataDto>(x => x.LanguageId == 123);
|
||||
Assert.AreEqual("SELECT *\nFROM [umbracoPropertyData]\nWHERE (([umbracoPropertyData].[languageId] = @0))", sql.SQL, sql.SQL);
|
||||
|
||||
var id = 123;
|
||||
|
||||
sql = new Sql<ISqlContext>(SqlContext)
|
||||
.Select("*")
|
||||
.From<PropertyDataDto>()
|
||||
.Where<PropertyDataDto>(x => x.LanguageId == id);
|
||||
Assert.AreEqual("SELECT *\nFROM [umbracoPropertyData]\nWHERE (([umbracoPropertyData].[languageId] = @0))", sql.SQL, sql.SQL);
|
||||
|
||||
int? nid = 123;
|
||||
|
||||
sql = new Sql<ISqlContext>(SqlContext)
|
||||
.Select("*")
|
||||
.From<PropertyDataDto>()
|
||||
.Where<PropertyDataDto>(x => x.LanguageId == nid);
|
||||
Assert.AreEqual("SELECT *\nFROM [umbracoPropertyData]\nWHERE (([umbracoPropertyData].[languageId] = @0))", sql.SQL, sql.SQL);
|
||||
|
||||
// but the above comparison fails if @0 is null
|
||||
// what we want is something similar to:
|
||||
|
||||
sql = new Sql<ISqlContext>(SqlContext)
|
||||
.Select("*")
|
||||
.From<PropertyDataDto>()
|
||||
.Where<PropertyDataDto>(x => (nid == null && x.LanguageId == null) || (nid != null && x.LanguageId == nid));
|
||||
Assert.AreEqual("SELECT *\nFROM [umbracoPropertyData]\nWHERE ((((@0 is null) AND ([umbracoPropertyData].[languageId] is null)) OR ((@1 is not null) AND ([umbracoPropertyData].[languageId] = @2))))", sql.SQL, sql.SQL);
|
||||
|
||||
// new NEquals method does it automatically
|
||||
// 'course it would be nicer if '==' could do it
|
||||
// see note in ExpressionVisitorBase for NEquals
|
||||
|
||||
//sql = new Sql<ISqlContext>(SqlContext)
|
||||
// .Select("*")
|
||||
// .From<PropertyDataDto>()
|
||||
// .Where<PropertyDataDto>(x => x.LanguageId.NEquals(nid));
|
||||
//Assert.AreEqual("SELECT *\nFROM [umbracoPropertyData]\nWHERE ((((@0 is null) AND ([umbracoPropertyData].[languageId] is null)) OR ((@0 is not null) AND ([umbracoPropertyData].[languageId] = @0))))", sql.SQL, sql.SQL);
|
||||
|
||||
// but, the expression above fails with SQL CE, 'specified argument for the function is not valid' in 'isnull' function
|
||||
// so... compare with fallback values
|
||||
|
||||
sql = new Sql<ISqlContext>(SqlContext)
|
||||
.Select("*")
|
||||
.From<PropertyDataDto>()
|
||||
.Where<PropertyDataDto>(x => x.LanguageId.NEquals(nid, -1));
|
||||
Assert.AreEqual("SELECT *\nFROM [umbracoPropertyData]\nWHERE ((COALESCE([umbracoPropertyData].[languageId],@0) = COALESCE(@1,@0)))", sql.SQL, sql.SQL);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NEqualsTest()
|
||||
{
|
||||
int? a, b;
|
||||
a = b = null;
|
||||
Assert.IsTrue(a.NEquals(b, -1));
|
||||
b = 2;
|
||||
Assert.IsFalse(a.NEquals(b, -1));
|
||||
a = 2;
|
||||
Assert.IsTrue(a.NEquals(b, -1));
|
||||
b = null;
|
||||
Assert.IsFalse(a.NEquals(b, -1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WhereInValueFieldTest()
|
||||
{
|
||||
|
||||
@@ -129,6 +129,133 @@ namespace Umbraco.Tests.Services
|
||||
Assert.IsFalse(enTagGroup.Any(x => x.Text == "plus"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TagsCanBecomeVariant()
|
||||
{
|
||||
var enId = ServiceContext.LocalizationService.GetLanguageIdByIsoCode("en-US").Value;
|
||||
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var contentTypeService = ServiceContext.ContentTypeService;
|
||||
var tagService = ServiceContext.TagService;
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true);
|
||||
PropertyType propertyType;
|
||||
contentType.PropertyGroups.First().PropertyTypes.Add(
|
||||
propertyType = new PropertyType("test", ValueStorageType.Ntext, "tags")
|
||||
{
|
||||
DataTypeId = 1041
|
||||
});
|
||||
contentTypeService.Save(contentType);
|
||||
|
||||
IContent content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1);
|
||||
content1.AssignTags("tags", new[] { "hello", "world", "another", "one" });
|
||||
contentService.SaveAndPublish(content1);
|
||||
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
contentTypeService.Save(contentType);
|
||||
|
||||
// no changes
|
||||
content1 = contentService.GetById(content1.Id);
|
||||
|
||||
var tags = content1.Properties["tags"].GetTagsValue().ToArray();
|
||||
Assert.AreEqual(4, tags.Length);
|
||||
Assert.Contains("one", tags);
|
||||
Assert.AreEqual(-1, tags.IndexOf("plus"));
|
||||
|
||||
var tagGroups = tagService.GetAllTags().GroupBy(x => x.LanguageId);
|
||||
foreach (var tag in tagService.GetAllTags())
|
||||
Console.WriteLine($"{tag.Group}:{tag.Text} {tag.LanguageId}");
|
||||
Assert.AreEqual(1, tagGroups.Count());
|
||||
var enTagGroup = tagGroups.FirstOrDefault(x => x.Key == null);
|
||||
Assert.IsNotNull(enTagGroup);
|
||||
Assert.AreEqual(4, enTagGroup.Count());
|
||||
Assert.IsTrue(enTagGroup.Any(x => x.Text == "one"));
|
||||
Assert.IsFalse(enTagGroup.Any(x => x.Text == "plus"));
|
||||
|
||||
propertyType.Variations = ContentVariation.Culture;
|
||||
contentTypeService.Save(contentType);
|
||||
|
||||
// changes
|
||||
content1 = contentService.GetById(content1.Id);
|
||||
|
||||
// property value has been moved from invariant to en-US
|
||||
tags = content1.Properties["tags"].GetTagsValue().ToArray();
|
||||
Assert.IsEmpty(tags);
|
||||
|
||||
tags = content1.Properties["tags"].GetTagsValue("en-US").ToArray();
|
||||
Assert.AreEqual(4, tags.Length);
|
||||
Assert.Contains("one", tags);
|
||||
Assert.AreEqual(-1, tags.IndexOf("plus"));
|
||||
|
||||
// tags have been copied from invariant to en-US
|
||||
tagGroups = tagService.GetAllTags(culture: "*").GroupBy(x => x.LanguageId);
|
||||
foreach (var tag in tagService.GetAllTags("*"))
|
||||
Console.WriteLine($"{tag.Group}:{tag.Text} {tag.LanguageId}");
|
||||
Assert.AreEqual(1, tagGroups.Count());
|
||||
|
||||
enTagGroup = tagGroups.FirstOrDefault(x => x.Key == enId);
|
||||
Assert.IsNotNull(enTagGroup);
|
||||
Assert.AreEqual(4, enTagGroup.Count());
|
||||
Assert.IsTrue(enTagGroup.Any(x => x.Text == "one"));
|
||||
Assert.IsFalse(enTagGroup.Any(x => x.Text == "plus"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TagsCanBecomeInvariant()
|
||||
{
|
||||
var languageService = ServiceContext.LocalizationService;
|
||||
languageService.Save(new Language("fr-FR")); // en-US is already there
|
||||
|
||||
var enId = ServiceContext.LocalizationService.GetLanguageIdByIsoCode("en-US").Value;
|
||||
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var contentTypeService = ServiceContext.ContentTypeService;
|
||||
var tagService = ServiceContext.TagService;
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true);
|
||||
PropertyType propertyType;
|
||||
contentType.PropertyGroups.First().PropertyTypes.Add(
|
||||
propertyType = new PropertyType("test", ValueStorageType.Ntext, "tags")
|
||||
{
|
||||
DataTypeId = 1041,
|
||||
Variations = ContentVariation.Culture
|
||||
});
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
contentTypeService.Save(contentType);
|
||||
|
||||
IContent content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1);
|
||||
content1.SetCultureName("name-fr", "fr-FR");
|
||||
content1.SetCultureName("name-en", "en-US");
|
||||
content1.AssignTags("tags", new[] { "hello", "world", "some", "tags", "plus" }, culture: "fr-FR");
|
||||
content1.AssignTags("tags", new[] { "hello", "world", "another", "one" }, culture: "en-US");
|
||||
contentService.SaveAndPublish(content1);
|
||||
|
||||
contentType.Variations = ContentVariation.Nothing;
|
||||
contentTypeService.Save(contentType);
|
||||
|
||||
// changes
|
||||
content1 = contentService.GetById(content1.Id);
|
||||
|
||||
// property value has been moved from en-US to invariant, fr-FR tags are gone
|
||||
Assert.IsEmpty(content1.Properties["tags"].GetTagsValue("fr-FR"));
|
||||
Assert.IsEmpty(content1.Properties["tags"].GetTagsValue("en-US"));
|
||||
|
||||
var tags = content1.Properties["tags"].GetTagsValue().ToArray();
|
||||
Assert.AreEqual(4, tags.Length);
|
||||
Assert.Contains("one", tags);
|
||||
Assert.AreEqual(-1, tags.IndexOf("plus"));
|
||||
|
||||
// tags have been copied from en-US to invariant, fr-FR tags are gone
|
||||
var tagGroups = tagService.GetAllTags(culture: "*").GroupBy(x => x.LanguageId);
|
||||
foreach (var tag in tagService.GetAllTags("*"))
|
||||
Console.WriteLine($"{tag.Group}:{tag.Text} {tag.LanguageId}");
|
||||
Assert.AreEqual(1, tagGroups.Count());
|
||||
|
||||
var enTagGroup = tagGroups.FirstOrDefault(x => x.Key == null);
|
||||
Assert.IsNotNull(enTagGroup);
|
||||
Assert.AreEqual(4, enTagGroup.Count());
|
||||
Assert.IsTrue(enTagGroup.Any(x => x.Text == "one"));
|
||||
Assert.IsFalse(enTagGroup.Any(x => x.Text == "plus"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TagsAreUpdatedWhenContentIsTrashedAndUnTrashed_One()
|
||||
{
|
||||
@@ -193,7 +320,7 @@ namespace Umbraco.Tests.Services
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("U4-8442, will need to be fixed eventually.")]
|
||||
//[Ignore("U4-8442, will need to be fixed eventually.")]
|
||||
public void TagsAreUpdatedWhenContentIsTrashedAndUnTrashed_Tree()
|
||||
{
|
||||
var contentService = ServiceContext.ContentService;
|
||||
@@ -209,12 +336,10 @@ namespace Umbraco.Tests.Services
|
||||
|
||||
var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1);
|
||||
content1.AssignTags("tags", new[] { "hello", "world", "some", "tags", "plus" });
|
||||
content1.PublishCulture();
|
||||
contentService.SaveAndPublish(content1);
|
||||
|
||||
var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", content1.Id);
|
||||
content2.AssignTags("tags", new[] { "hello", "world", "some", "tags" });
|
||||
content2.PublishCulture();
|
||||
contentService.SaveAndPublish(content2);
|
||||
|
||||
// verify
|
||||
@@ -298,7 +423,7 @@ namespace Umbraco.Tests.Services
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("U4-8442, will need to be fixed eventually.")]
|
||||
//[Ignore("U4-8442, will need to be fixed eventually.")]
|
||||
public void TagsAreUpdatedWhenContentIsUnpublishedAndRePublished_Tree()
|
||||
{
|
||||
var contentService = ServiceContext.ContentService;
|
||||
@@ -314,12 +439,10 @@ namespace Umbraco.Tests.Services
|
||||
|
||||
var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1);
|
||||
content1.AssignTags("tags", new[] { "hello", "world", "some", "tags", "bam" });
|
||||
content1.PublishCulture();
|
||||
contentService.SaveAndPublish(content1);
|
||||
|
||||
var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", content1);
|
||||
content2.AssignTags("tags", new[] { "hello", "world", "some", "tags" });
|
||||
content2.PublishCulture();
|
||||
contentService.SaveAndPublish(content2);
|
||||
|
||||
contentService.Unpublish(content1);
|
||||
|
||||
Reference in New Issue
Block a user