Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Dimitris Chrysomallis 23 posts 137 karma points
    Jan 18, 2018 @ 11:53
    Dimitris Chrysomallis
    0

    Extending uSync to handle Relations and RelationTypes

    I am using Relations extensively in one of our websites for handling i18n. The problem is that Relations are not being handled by uSync.

    What does it take to extend uSync in order to handle Relations? I looked up uSync.Core and I suspect that some core classes would have to be modified (e.g. uSyncContext). Any guidelines or suggestions?

    Thanks Dimitris

  • Kevin Jump 2310 posts 14695 karma points MVP 7x c-trib
    Jan 18, 2018 @ 13:18
    Kevin Jump
    100

    Hi

    Yes it could be extended to handle relations.

    Most things usync export have two elements, a Serializer and a Handler.

    The serializer is the thing responsible for getting the settings in and out of umbraco - Some of the seralizers are more complex than others - but the Domain Serializer is probably a good example of how they might work *Generally Serializers live in uSync.Core.

    Handlers manage the IO for uSync - reading and writing things from disk, and acting on the events that are triggered in umbraco (like when you save a datatype). the Language Handler probably shows the simpliest way this works. Handlers usually live in uSync.BackOffice or uSync.ContentEdition

    Relations could be added into this structure, or indeed they could be added as an extension, (there is no reason why the serializer/handlers have to be in the core or backoffice projects) - i suppose the real question i've had is where relations sit - most of the time they are content relations (although they can be something else) - So i would suppose relations would be part of ContentEdition?

  • Dimitris Chrysomallis 23 posts 137 karma points
    Jan 18, 2018 @ 14:19
    Dimitris Chrysomallis
    0

    Thanks,

    Since Relations could relate more than Content nodes, why not a separate package? Anyway, I will have to look more closely at your code to understand the uSync api first. I hope to find the time to write a minimal extension for this.

    Thank you for your fantastic work.

  • Aron Lakatos 5 posts 25 karma points
    Jun 14, 2018 @ 06:12
    Aron Lakatos
    0

    Dear Dimitris,

    Looks like we are in the same boat, we'd like to use uSync on an i18n website for handling relations between localized pages.

    I was wondering if you managed to make a head start on this by any chance. We'd be glad to chip in and complete the feature together if you are still up for this.

    Thanks Aron

  • David McDonnell 53 posts 251 karma points
    Apr 11, 2020 @ 13:23
    David McDonnell
    0

    Hi Kevin, I would just like to have the relation types managed by usync so that I do not have to create them manually on each environment.

    Could you provide a sample of what I need to do to achieve this?

    I'm using Umbraco 8.5.5

    Thanks David

  • David McDonnell 53 posts 251 karma points
    Apr 11, 2020 @ 17:12
    David McDonnell
    2

    For Umbraco 8: Managed to do this by copying the Macro Handler, Macro Serializer, Macro Tracker and Macro Checker

    Create following files: RelationTypeHandler

    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Umbraco.Core;
    using Umbraco.Core.Cache;
    using Umbraco.Core.Logging;
    using Umbraco.Core.Models;
    using Umbraco.Core.Models.Entities;
    using Umbraco.Core.Services;
    using Umbraco.Core.Services.Implement;
    using uSync8.BackOffice;
    using uSync8.BackOffice.Configuration;
    using uSync8.BackOffice.Services;
    using uSync8.BackOffice.SyncHandlers;
    using uSync8.Core;
    using uSync8.Core.Dependency;
    using uSync8.Core.Extensions;
    using uSync8.Core.Serialization;
    using uSync8.Core.Tracking;
    
    using static Umbraco.Core.Constants;
    
    
    namespace CustomNameSpace
    {
        [SyncHandler("relationTypeHandler", "Relation Types", "RelationTypes", uSyncBackOfficeConstants.Priorites.MemberTypes,
            Icon = "icon-diagonal-arrow-alt", EntityType = UdiEntityType.RelationType, IsTwoPass = true)]
        public class RelationTypeHandler : SyncHandlerBase<IRelationType, IRelationService>, ISyncExtendedHandler
        {
            private readonly IRelationService relationService;
    
            public RelationTypeHandler(
                IEntityService entityService,
                IProfilingLogger logger,
                IRelationService relationService,
                ISyncSerializer<IRelationType> serializer,
                ISyncTracker<IRelationType> tracker,
                AppCaches appCaches,
                ISyncDependencyChecker<IRelationType> checker,
                SyncFileService syncFileService)
                : base(entityService, logger, serializer, tracker, appCaches, checker, syncFileService)
            {
                this.relationService = relationService;
            }
    
    
    
    
            /// <summary>
            ///  overrider the default export, because macros, don't exist as an object type???
            /// </summary>
            public override IEnumerable<uSyncAction> ExportAll(int parent, string folder, HandlerSettings config, SyncUpdateCallback callback)
            {
                // we clean the folder out on an export all. 
                syncFileService.CleanFolder(folder);
    
                var actions = new List<uSyncAction>();
    
                var items = relationService.GetAllRelationTypes().ToList();
                int count = 0;
                foreach (var item in items)
                {
                    count++;
                    callback?.Invoke(item.Name, count, items.Count);
                    actions.AddRange(Export(item, folder, config));
                }
    
                return actions;
            }
    
            protected override IRelationType GetFromService(int id)
                => relationService.GetRelationTypeById(id);
    
            // not sure we can trust macro guids in the path just yet.
            protected override string GetItemPath(IRelationType item, bool useGuid, bool isFlat)
            {
                if (useGuid) return item.Key.ToString();
                return item.Alias.ToSafeAlias();
            }
    
            protected override void InitializeEvents(HandlerSettings settings)
            {
                RelationService.SavedRelationType += EventSavedItem;
                RelationService.DeletedRelationType += EventDeletedItem;
            }
    
            protected override IRelationType GetFromService(Guid key)
            {
                return relationService.GetAllRelationTypes().FirstOrDefault(rt => rt.Key == key);
            }
    
            protected override IRelationType GetFromService(string alias)
                => relationService.GetRelationTypeByAlias(alias);
    
            protected override void DeleteViaService(IRelationType item)
                => relationService.Delete(item);
    
            protected override string GetItemName(IRelationType item)
                => item.Name;
            protected override string GetItemAlias(IRelationType item)
                => item.Alias;
    
            protected override IEnumerable<IEntity> GetChildItems(int parent)
            {
                if (parent == -1)
                {
                    return relationService.GetAllRelationTypes().Where(x => x is IEntity)
                        .Select(x => x as IEntity);
                }
    
                return Enumerable.Empty<IEntity>();
            }
    
        }
    
    }
    

    RelationTypeSerializer

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Xml.Linq;
    using Umbraco.Core.Logging;
    using Umbraco.Core.Models;
    using Umbraco.Core.Services;
    using uSync8.Core;
    using uSync8.Core.Extensions;
    using uSync8.Core.Models;
    using uSync8.Core.Serialization;
    
    namespace CustomNameSpace
    {
        [SyncSerializer("EE5439FF-37B5-4C2C-A396-233FE0563825", "RelationTypeSerializer", "RelationType")]
            public class RelationTypeSerializer: SyncSerializerBase<IRelationType>, ISyncSerializer<IRelationType>
        {
            private readonly IRelationService relationTypeService;
    
            public RelationTypeSerializer(
                IEntityService entityService, ILogger logger,
                IRelationService relationTypeService)
                : base(entityService, logger)
            {
                this.relationTypeService = relationTypeService;
            }
    
            protected override SyncAttempt<IRelationType> DeserializeCore(XElement node)
            {
                var changes = new List<uSyncChange>();
    
                if (node.Element("Name") == null)
                    throw new ArgumentNullException("XML missing Name parameter");
    
                var item = default(IRelationType);
    
                var key = node.GetKey();
                var alias = node.GetAlias();
                var name = node.Element("Name").ValueOrDefault(string.Empty);
    
                var realtionType = node.Element("RelationType").ValueOrDefault(string.Empty);
    
                logger.Debug<RelationTypeSerializer>("Relation Type by Alias [{0}]", key);
                item = relationTypeService.GetRelationTypeByAlias(alias);
    
                /*
                if (item == null)
                {
                    logger.Debug<RelationTypeSerializer>("Relation Type by Key [{0}]", key);
                    item = relationTypeService.GetRelationTypeById(key);
                }
                */
                if (item == null)
                {
                    logger.Debug<RelationTypeSerializer>("Creating New [{0}]", key);
                    item = new RelationType(Guid.Empty, Guid.Empty, alias, name);
                    changes.Add(uSyncChange.Create(alias, name, "New RelationType"));
                }
    
                item.Key = key;
                item.Name = name;
                item.Alias = alias;
    
                item.ParentObjectType = node.Element("parent").ValueOrDefault(Guid.Empty);
                item.ChildObjectType = node.Element("child").ValueOrDefault(Guid.Empty);
                item.IsBidirectional = node.Element("direction").ValueOrDefault(false);
    
                var attempt = SyncAttempt<IRelationType>.Succeed(item.Name, item, ChangeType.Import);
                if (changes.Any())
                    attempt.Details = changes;
    
                return attempt;
            }
    
            private void RemoveOrphanProperties(IRelationType item, XElement properties)
            {
                var removalKeys = new List<string>();
    
    
            }
    
            protected override SyncAttempt<XElement> SerializeCore(IRelationType item)
            {
                var node = this.InitializeBaseNode(item, item.Alias);
    
                node.Add(new XElement("Name", item.Name));
    
    
                node.Add(new XElement("parent", item.ParentObjectType));
                node.Add(new XElement("child", item.ChildObjectType));
                node.Add(new XElement("direction", item.IsBidirectional));
    
                return SyncAttempt<XElement>.SucceedIf(
                    node != null,
                    item.Name,
                    node,
                    typeof(IRelationType),
                    ChangeType.Export);
            }
    
    
    
            protected override IRelationType FindItem(string alias)
                => relationTypeService.GetRelationTypeByAlias(alias);
    
            protected override void SaveItem(IRelationType item)
                => relationTypeService.Save(item);
    
            protected override void DeleteItem(IRelationType item)
                => relationTypeService.Delete(item);
    
            protected override string ItemAlias(IRelationType item)
                => item.Alias;
    
            protected override IRelationType FindItem(Guid key)
            {
                return relationTypeService.GetAllRelationTypes().FirstOrDefault(rt => rt.Key == key);
            }
        }
    }
    

    RelationTypeTracker

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Umbraco.Core.Models;
    using uSync8.Core.Serialization;
    using uSync8.Core.Tracking;
    
    namespace CustomNamespace
    {
        public class RelationTypeTracker : SyncBaseTracker<IRelationType>, ISyncTracker<IRelationType>
        {
            public RelationTypeTracker(ISyncSerializer<IRelationType> serializer) : base(serializer)
            {
            }
    
            protected override TrackedItem TrackChanges()
            {
                return new TrackedItem(serializer.ItemType, true)
                {
                    Children = new List<TrackedItem>()
                    {
                        new TrackedItem("Name", "/Name", true),
                        new TrackedItem("parent", "/parent", true),
                        new TrackedItem("child", "/child", true),
                        new TrackedItem("direction", "/direction", true)
    
                    }
    
                };
            }
        }
    }
    

    RelationTypeChecker

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Umbraco.Core;
    using Umbraco.Core.Models;
    using uSync8.Core.Dependency;
    
    namespace CustomNameSpace
    {
        public class RelationTypeChecker : ISyncDependencyChecker<IRelationType>
        {
            public UmbracoObjectTypes ObjectType => UmbracoObjectTypes.Unknown;
    
            public IEnumerable<uSyncDependency> GetDependencies(IRelationType item, DependencyFlags flags)
            {
                var dependencies = new List<uSyncDependency>
                {
                    new uSyncDependency()
                    {
                        Name = item.Name,
                        Udi = item.GetUdi(),
                        Order = DependencyOrders.Media,
                        Flags = flags
    
                    }
                };
    
                return dependencies;
            }
        }
    }
    

    Then resolve the dependencies in your composer:

                composition.Register<ISyncSerializer<IRelationType>, RelationTypeSerializer>();
                composition.Register<ISyncTracker<IRelationType>, RelationTypeTracker>();
                composition.Register<ISyncDependencyChecker<IRelationType>, RelationTypeChecker>();
    

    Update your usync.config file

    <?xml version="1.0" encoding="utf-8"?>
    <uSync>
      <BackOffice>
        <Folder>~/uSync/v8/</Folder>
        <FlatFolders>True</FlatFolders>
        <ImportAtStartup>True</ImportAtStartup>
        <ExportAtStartup>False</ExportAtStartup>
        <ExportOnSave>True</ExportOnSave>
        <UseGuidFilenames>False</UseGuidFilenames>
        <BatchSave>False</BatchSave>
        <!-- calls a rebuild cache when an import completes
            (for Umbraco 8.3+ recommended value is false)  -->
        <RebuildCacheOnCompletion>False</RebuildCacheOnCompletion>
        <!-- handler sets -->
        <HandlerSets default="default" Default="default">
          <Handlers Name="default">
            <Handler Alias="dataTypeHandler" Enabled="true" Actions="All" />
            <Handler Alias="languageHandler" Enabled="true" Actions="All" />
            <Handler Alias="macroHandler" Enabled="true" Actions="All" />
            <Handler Alias="mediaTypeHandler" Enabled="true" Actions="All" />
            <Handler Alias="memberTypeHandler" Enabled="true" Actions="All" />
            <Handler Alias="templateHandler" Enabled="true" Actions="All" />
            <Handler Alias="contentTypeHandler" Enabled="true" Actions="All" />
            <Handler Alias="contentHandler" Enabled="true" Actions="All" />
            <Handler Alias="contentTemplateHandler" Enabled="true" Actions="All" />
            <Handler Alias="dictionaryHandler" Enabled="true" Actions="All" />
            <Handler Alias="domainHandler" Enabled="true" Actions="All" />
            <Handler Alias="mediaHandler" Enabled="true" Actions="All" />
            <Handler Alias="relationTypeHandler" Enabled="true" Actions="All" />
          </Handlers>
        </HandlerSets>
        <ReportDebug>False</ReportDebug>
        <FailOnMissingParent>True</FailOnMissingParent>
      </BackOffice>
    </uSync>
    

    Compile your code, open umbraco and just click save on all existing Relation Types

    Then import and export to include files in source control usync folder

  • Lee Kelleher 4020 posts 15802 karma points MVP 13x admin c-trib
    Jun 17, 2020 @ 17:14
    Lee Kelleher
    0

    Hi all. I'm wondering if there's any thoughts on including this RelationTypes handler in uSync, whether it's already on the roadmap - or part of another package (that I'm not aware of)?

    It's the first time that I'm using RelationTypes and uSync on the same project, so interested to see how this pans out.

    Many thanks,
    - Lee

  • Kevin Jump 2310 posts 14695 karma points MVP 7x c-trib
    Jun 17, 2020 @ 17:16
    Kevin Jump
    1

    Hi Lee,

    Its in the wings, ready for our next release:

    https://github.com/KevinJump/uSync8/commit/28bcf4733f82ab65969dbc6adf2306807ca18908

    In terms of timescales we are currently working out if we are waiting for Umbraco 8.7 - because we might want to also squish all the block editor stuff in too.

  • Kevin Jump 2310 posts 14695 karma points MVP 7x c-trib
    Jun 17, 2020 @ 17:21
    Kevin Jump
    0

    It would also be good to get feedback on how it might work.

    at the moment we are syncing RelationTypes by default with options to also Sync the actual Relations.

    also settings to not sync certain relation types (e.g you might not want some of the built in ones).

    If you can think of any other use cases / configuration options that would be good.

  • Lee Kelleher 4020 posts 15802 karma points MVP 13x admin c-trib
    Jun 18, 2020 @ 10:11
    Lee Kelleher
    0

    Thanks Kevin! I did have a search around your uSync repos, but didn't spot the branch/commit (probably because GitHub search only works in the main/default branch).

    Looking forward to v8.7!

    I'll have a think about my use cases, see how I'd expect it work, etc - and feed back.

    Thanks again!
    - Lee

  • Kevin Jump 2310 posts 14695 karma points MVP 7x c-trib
    Jun 18, 2020 @ 10:22
    Kevin Jump
    0

    Yeah we've just been talking about it, we might be settling on the next release being uSync 8.6 and three quarters. 😊

Please Sign in or register to post replies

Write your reply to:

Draft