Contnet Type Allowed Templates that is not registed on uSync got removed
Describe the bug We have a deployed umbraco instance where customers can add custom templates and link them with Document Types. In that case this change is not committed to our repository. In our next deployment this link got removed as it is not registered in uSync.
To Reproduce
Steps to reproduce the behavior:
1 - Go to document type
2- Link extra template
3- Then trigger another deployment from your repository

Expected behavior uSync should not remove existing links between DocumentTypes and AllowedTemplates if they are not configured in the config file For Ex: Template1 & Template2 are configured in the confige file, that doesn't mean that those templates are the only ones. It means that uSync should make sure to link them to DocumentType and that's it. May be we can add a configuration if the extra linked templates should be removed.
<ContentType Key="b51331b0-cd02-404f-82b9-24489a238575" Alias="contentPage" Level="1">
<Info>
<Name>Content Page</Name>
<Icon>icon-document</Icon>
<Thumbnail>folder.png</Thumbnail>
<Description></Description>
<AllowAtRoot>False</AllowAtRoot>
<IsListView>False</IsListView>
<Variations>Nothing</Variations>
<IsElement>false</IsElement>
<HistoryCleanup>
<PreventCleanup>False</PreventCleanup>
<KeepAllVersionsNewerThanDays></KeepAllVersionsNewerThanDays>
<KeepLatestVersionPerDayForDays></KeepLatestVersionPerDayForDays>
</HistoryCleanup>
<Compositions>
<Composition Key="d2b99d9d-23cd-40e9-8417-f49d22c20e6d">navigationBase</Composition>
</Compositions>
<DefaultTemplate>amcsPublicPage</DefaultTemplate>
<AllowedTemplates>
**<Template Key="29ce7840-92fa-4c34-b45c-db5ff0416dfa">Template1</Template>
<Template Key="2b37d72c-b8ea-4a02-b166-2944dce100e9">Template2</Template>**
</AllowedTemplates>
About your Site (please complete the following information):
- Umbraco Version:8.18.5
- uSync Version: v8.11.1 + Content Edition (v8.11.1)
- Browser chrome
cc/@KevinJump
Hi,
Unfortunately, this is the expected behaviour - uSync will sync things exactly like they appear the aim is to have both sites be identical in every way.
However we are always option to adding options to change behaviour, we could for example have a 'KeepTemplates* option that would not remove them - but this would be an enhancement not a bug fix
Thanks @KevinJump, For now I have created a custom ContentTypeHandler & Serializer and added AddClientCustomTemplatesTouSyncAllowedTemplates to serializer
[SyncHandler("customContentTypeHandler", "DocTypes", "ContentTypes", uSyncBackOfficeConstants.Priorites.ContentTypes,
IsTwoPass = true, Icon = "icon-item-arrangement", EntityType = UdiEntityType.DocumentType)]
public class CustomContentTypeHandler : ContentTypeHandler
{
private readonly IContentTypeService contentTypeService;
public CustomContentTypeHandler(
IContentTypeService contentTypeService,
IEntityService entityService,
IProfilingLogger logger,
AppCaches appCaches,
CustomContentTypeSerlializer serializer,
ISyncItemFactory syncItemFactory,
SyncFileService syncFileService)
: base(contentTypeService, entityService, logger, appCaches, serializer, syncItemFactory, syncFileService)
{
this.contentTypeService = contentTypeService;
}
}
[SyncSerializer("EBCD359D-295C-4B8B-B5CF-D44690DC5FB1", "CustomContentTypeSerlializer", uSyncConstants.Serialization.ContentType)]
public class CustomContentTypeSerlializer : ContentTypeSerializer
{
private readonly IContentTypeService contentTypeService;
private readonly IFileService fileService;
public CustomContentTypeSerlializer(
IEntityService entityService, ILogger logger,
IDataTypeService dataTypeService,
IContentTypeService contentTypeService,
IFileService fileService)
: base(entityService, logger, dataTypeService, contentTypeService, fileService)
{
this.contentTypeService = contentTypeService;
this.fileService = fileService;
}
protected override SyncAttempt<IContentType> DeserializeCore(XElement node, SyncSerializerOptions options)
{
var attempt = FindOrCreate(node);
if (!attempt.Success) throw attempt.Exception;
var item = attempt.Result;
var details = new List<uSyncChange>();
details.AddRange(DeserializeBase(item, node));
details.AddRange(DeserializeTabs(item, node));
details.AddRange(DeserializeProperties(item, node, options));
// content type only property stuff.
details.AddRange(DeserializeContentTypeProperties(item, node));
// templates
details.AddRange(DeserializeTemplates(item, node));
return SyncAttempt<IContentType>.Succeed(item.Name, item, ChangeType.Import, details);
}
protected override IEnumerable<uSyncChange> DeserializeExtraProperties(IContentType item, PropertyType property, XElement node)
{
var variations = node.Element("Variations").ValueOrDefault(ContentVariation.Nothing);
if (property.Variations != variations)
{
var change = uSyncChange.Update("Property/Variations", "Variations", property.Variations, variations);
property.Variations = variations;
return change.AsEnumerableOfOne();
}
return Enumerable.Empty<uSyncChange>();
}
private IEnumerable<uSyncChange> DeserializeContentTypeProperties(IContentType item, XElement node)
{
var info = node?.Element("Info");
if (info == null) return Enumerable.Empty<uSyncChange>();
var changes = new List<uSyncChange>();
var isContainer = info.Element("IsListView").ValueOrDefault(false);
if (item.IsContainer != isContainer)
{
changes.AddUpdate("IsListView", item.IsContainer, isContainer, "Info/IsListView");
item.IsContainer = isContainer;
}
var masterTemplate = info.Element("DefaultTemplate").ValueOrDefault(string.Empty);
if (!string.IsNullOrEmpty(masterTemplate))
{
var template = fileService.GetTemplate(masterTemplate);
if (template != null)
{
if (item.DefaultTemplate == null || template.Alias != item.DefaultTemplate.Alias)
{
changes.AddUpdate("DefaultTemplate", item.DefaultTemplate?.Alias ?? string.Empty, masterTemplate, "DefaultTemplate");
item.SetDefaultTemplate(template);
}
}
else
{
// elements don't have a defaultTemplate, but it can be valid to have the old defaultTemplate in the db.
// (it would then re-appear if the user untoggles is element) See issue #203
//
// So we only log this as a problem if the default template is missing on a non-element doctype.
if (!item.IsElement)
{
changes.AddUpdate("DefaultTemplate", item.DefaultTemplate?.Alias ?? string.Empty, "Cannot find Template", "DefaultTemplate", false);
}
}
}
return changes;
}
private IEnumerable<uSyncChange> DeserializeTemplates(IContentType contentType, XElement node)
{
var templates = node?.Element("Info")?.Element("AllowedTemplates");
if (templates == null) return Enumerable.Empty<uSyncChange>();
var uSyncAllowedTemplates = new List<ITemplate>();
var changes = new List<uSyncChange>();
foreach (var template in templates.Elements("Template"))
{
var alias = template.Value;
var key = template.Attribute("Key").ValueOrDefault(Guid.Empty);
var templateItem = GetTemplate(alias, key);
if (templateItem != null)
{
logger.Debug<ContentTypeSerializer>("Adding Template: {0}", templateItem.Alias);
uSyncAllowedTemplates.Add(templateItem);
}
}
AddClientCustomTemplatesTouSyncAllowedTemplates(contentType, uSyncAllowedTemplates);
var currentTemplates = string.Join(",", contentType.AllowedTemplates.Select(x => x.Alias).OrderBy(x => x));
var newTemplates = string.Join(",", uSyncAllowedTemplates.Select(x => x.Alias).OrderBy(x => x));
if (currentTemplates != newTemplates)
{
changes.AddUpdate("AllowedTemplates", currentTemplates, newTemplates, "AllowedTemplates");
}
contentType.AllowedTemplates = uSyncAllowedTemplates;
return changes;
}
private void **AddClientCustomTemplatesTouSyncAllowedTemplates**(IContentType contentType, List<ITemplate> uSyncAllowedTemplates)
{
var currentTemplateAliases = contentType.AllowedTemplates.Select(x => x.Alias).OrderBy(x => x);
var newTemplateAliases = uSyncAllowedTemplates.Select(x => x.Alias).OrderBy(x => x);
var customTemplatesAliasesAddedByClient = currentTemplateAliases.Except(newTemplateAliases);
foreach (var templateAlias in customTemplatesAliasesAddedByClient)
{
var templateItem = GetTemplate(templateAlias, Guid.Empty);
if (templateItem != null)
{
logger.Debug<ContentTypeSerializer>("Adding Template: {0}", templateItem.Alias);
uSyncAllowedTemplates.Add(templateItem);
}
}
}
private ITemplate GetTemplate(string alias, Guid key)
{
var templateItem = default(ITemplate);
if (key != Guid.Empty)
templateItem = fileService.GetTemplate(key);
if (templateItem == null)
templateItem = fileService.GetTemplate(alias);
return templateItem;
}
}
Added "KeepTemplates" option for next v11 version