Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ protected IEnumerable<uSyncChange> DeserializeName(TObject item, XElement node,
item.Name = name;
}

if (nameNode.HasElements)
if (nameNode.HasElements && item.ContentType.VariesByCulture())
{
var activeCultures = options.GetDeserializedCultures(node);

Expand Down
18 changes: 18 additions & 0 deletions uSync.Extend/Example/MyCustomComposer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Microsoft.Extensions.DependencyInjection;

using System;
using System.Collections.Generic;
using System.Text;

using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;

namespace uSync.Extend.Example;

public class MyCustomComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
builder.Services.AddSingleton<IMyCustomObjectService, MyCustomObjectService>();
}
}
24 changes: 24 additions & 0 deletions uSync.Extend/Example/MyCustomObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;

namespace uSync.Extend.Example;


/// <summary>
/// this is the object you want to serialze,
/// you can just use the object you alredy have,
/// or you can have something that represents only
/// the bits you want to serialize.
/// </summary>
public class MyCustomObject
{
public Guid Key { get; set; }
public string Alias { get; set; }
public string Name { get; set; }
public int Value { get; set; }

[IgnoreDataMember]
public string DontShowThis { get; set; } = string.Empty;
}
48 changes: 48 additions & 0 deletions uSync.Extend/Example/MyCustomObjectHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Microsoft.Extensions.Logging;

using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Strings;

using uSync.BackOffice;
using uSync.BackOffice.Configuration;
using uSync.BackOffice.Services;
using uSync.BackOffice.SyncHandlers;
using uSync.BackOffice.SyncHandlers.Models;

namespace uSync.Extend.Example;

/// <summary>
/// the attribute set it all up. this is how it's discovered and registered in uSync
/// </summary>
[SyncHandler("MyCustomObjectHandler",
"My Custom Object Handler",
"MyCustomObjects",
uSyncConstants.Priorites.USYNC_RESERVED_UPPER + 100, // after all the core things
Icon = "icon-files",
EntityType = "myCustomObject")]
public class MyCustomObjectHandler : SyncObjectHandler<MyCustomObject>
{
public override string Group => "My Custom Group";

private readonly IMyCustomObjectService _myCustomObjectService;

public MyCustomObjectHandler(
ILogger<SyncHandlerRoot<MyCustomObject, MyCustomObject>> logger,
AppCaches appCaches,
IShortStringHelper shortStringHelper,
ISyncFileService syncFileService,
ISyncEventService mutexService,
ISyncConfigService uSyncConfig,
Core.ISyncItemFactory itemFactory,
IMyCustomObjectService myCustomObjectService) : base(logger, appCaches, shortStringHelper, syncFileService, mutexService, uSyncConfig, itemFactory)
{
_myCustomObjectService = myCustomObjectService;
}

// here you would get all your items from your data source.
protected override async Task<IEnumerable<MyCustomObject>> GetAllItems()
=> await _myCustomObjectService.GetAllAsync();

protected override string GetItemName(MyCustomObject item)
=> item.Name;
}
48 changes: 48 additions & 0 deletions uSync.Extend/Example/MyCustomObjectSerializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Microsoft.Extensions.Logging;

using System.Xml.Linq;

using uSync.Core;
using uSync.Core.Serialization;

namespace uSync.Extend.Example;

[SyncSerializer("c61e5987-020b-4ba8-899f-957d63443ac1", // uniqe id need to be a GUID
"My Custom Object Serializer", // name
"MyCustomObject")] // the object type (node name in the xml)
public class MyCustomObjectSerializer : SyncObjectSerializer<MyCustomObject>
{
private readonly IMyCustomObjectService _service;

public MyCustomObjectSerializer(
ILogger<SyncSerializerRoot<MyCustomObject>> logger,
IMyCustomObjectService service) : base(logger)
{
_service = service;
}
public override MyCustomObject CreateItem(XElement node)
{
return new MyCustomObject
{
Key = node.GetKey(),
Alias = node.GetAlias(),
};
}

public override async Task DeleteItemAsync(MyCustomObject item)
=> await _service.DeleteAsync(item);

public override async Task<MyCustomObject?> FindItemAsync(Guid key)
=> await _service.FindByKeyAsync(key);

public override async Task<MyCustomObject?> FindItemAsync(string alias)
=> await _service.FindByAliasAsync(alias);

public override string ItemAlias(MyCustomObject item) => item.Alias;

public override Guid ItemKey(MyCustomObject item) => item.Key;

public override Task SaveItemAsync(MyCustomObject item)
=> _service.SaveAsync(item);
}

57 changes: 57 additions & 0 deletions uSync.Extend/Example/MyCustomObjectService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
namespace uSync.Extend.Example;

/// <summary>
/// interface for the service, you don't need to have an interface
/// but in this example we are using one. it makes testing eaiser.
/// </summary>
public interface IMyCustomObjectService
{
Task<IEnumerable<MyCustomObject>> GetAllAsync();
Task<MyCustomObject?> FindByKeyAsync(Guid key);
Task<MyCustomObject?> FindByAliasAsync(string alias);
Task SaveAsync(MyCustomObject item);
Task DeleteAsync(MyCustomObject item);
}


/// <summary>
/// a exmaple service (you probibly already have one?)
/// that can be used to answer the basic data access needs of the handler and serializer.
/// </summary>
internal class MyCustomObjectService : IMyCustomObjectService
{
List<MyCustomObject> data =
[
new() { Key = Guid.Parse("b2885bf2-7780-40ad-b0c8-09473fd53712"), Alias = "item1", Name = "Item 1", Value = 1, DontShowThis = "Hidden 1" },
new() { Key = Guid.Parse("db31860f-4ccb-43bb-9cb6-8701e3549163"), Alias = "item2", Name = "Item 2", Value = 2, DontShowThis = "Hidden 2" },
new() { Key = Guid.Parse("ac417ecf-d270-4ce3-9816-0bbb1a709163"), Alias = "item3", Name = "Item 3", Value = 3, DontShowThis = "Hidden 3" },
];

public Task DeleteAsync(MyCustomObject item) {
data.Remove(item);
return Task.CompletedTask;
}

public Task<MyCustomObject?> FindByAliasAsync(string alias)
{
var item = data.FirstOrDefault(x => x.Alias == alias);
return Task.FromResult(item);
}

public Task<MyCustomObject?> FindByKeyAsync(Guid key)
{
var item = data.FirstOrDefault(x => x.Key == key);
return Task.FromResult(item);
}

public Task<IEnumerable<MyCustomObject>> GetAllAsync()
{
return Task.FromResult(data.AsEnumerable());
}

public Task SaveAsync(MyCustomObject item)
{
data.Add(item);
return Task.CompletedTask;
}
}
64 changes: 64 additions & 0 deletions uSync.Extend/SyncObjectHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using Microsoft.Extensions.Logging;

using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Strings;

using uSync.BackOffice;
using uSync.BackOffice.Configuration;
using uSync.BackOffice.Services;
using uSync.BackOffice.SyncHandlers;
using uSync.BackOffice.SyncHandlers.Interfaces;
using uSync.Core;

namespace uSync.Extend;

/// <summary>
/// object based handler, this is the most basic handler to implement,
/// you just need to return all your items and then uSync will do the rest.
/// </summary>
public abstract class SyncObjectHandler<TObject> : SyncHandlerRoot<TObject, TObject>, ISyncHandler
{
protected SyncObjectHandler(
ILogger<SyncHandlerRoot<TObject, TObject>> logger,
AppCaches appCaches,
IShortStringHelper shortStringHelper,
ISyncFileService syncFileService,
ISyncEventService mutexService,
ISyncConfigService uSyncConfig,
ISyncItemFactory itemFactory)
: base(logger, appCaches, shortStringHelper, syncFileService, mutexService, uSyncConfig, itemFactory)
{ }

protected override Task<IEnumerable<uSyncAction>> DeleteMissingItemsAsync(TObject parent, IEnumerable<Guid> keysToKeep, bool reportOnly)
{
// you don't have to support deleing missing items, but if you do, this is where you would do it.
// You would get all the items that are children of the parent, and then compare them to the keysToKeep.
// If there are any items that are not in the keysToKeep, then you would delete them.
// If reportOnly is true, then you would just return a list of actions that would be taken,
// but not actually perform the deletions.
return Task.FromResult(Enumerable.Empty<uSyncAction>());
}

protected override Task<IEnumerable<TObject>> GetChildItemsAsync(TObject? parent)
{
if (parent is not null) return Task.FromResult(Enumerable.Empty<TObject>());
return GetAllItems();
}

/// <summary>
/// a container (folder) item, is not supported in this handler,
/// so we return null to indicate that the item is not found.
/// </summary>
protected override Task<TObject?> GetFromServiceAsync(TObject? item) =>
Task.FromResult(default(TObject));

protected abstract Task<IEnumerable<TObject>> GetAllItems();

/// <summary>
/// we are not supporing folders this handler assumes its all flat.
/// </summary>
/// <param name="parent"></param>
/// <returns></returns>
protected override Task<IEnumerable<TObject>> GetFoldersAsync(TObject? parent) =>
Task.FromResult(Enumerable.Empty<TObject>());
}
96 changes: 96 additions & 0 deletions uSync.Extend/SyncObjectSerializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using Microsoft.Extensions.Logging;

using System.Runtime.Serialization;
using System.Xml.Linq;

using uSync.Core;
using uSync.Core.Models;
using uSync.Core.Serialization;

namespace uSync.Extend;

/// <summary>
/// a generic seriazlier that will serialize any object,
/// </summary>
public abstract class SyncObjectSerializer<TObject> : SyncSerializerRoot<TObject>, ISyncSerializer<TObject>
{
protected SyncObjectSerializer(ILogger<SyncSerializerRoot<TObject>> logger) : base(logger)
{ }

public abstract TObject CreateItem(XElement node);

protected virtual void SetKey(TObject item, Guid key)
{
var property = typeof(TObject).GetProperty("Key");
if (property != null && property.CanWrite)
{
property.SetValue(item, key);
}
}

protected virtual void SetAlias(TObject item, string alias)
{
var property = typeof(TObject).GetProperty("Alias");
if (property != null && property.CanWrite)
{
property.SetValue(item, alias);
}
}

protected override async Task<SyncAttempt<TObject>> DeserializeCoreAsync(XElement node, SyncSerializerOptions options)
{
var item = (await FindItemAsync(node)) ?? CreateItem(node);

SetKey(item, node.GetKey());
SetAlias(item, node.GetAlias());

var propertyNode = node.Element("Properties");
if (propertyNode != null)
{
var properties = typeof(TObject).GetProperties();
foreach (var property in properties)
{
var valueNode = propertyNode.Element(property.Name);
if (valueNode != null)
{
var currentValue = property.GetValue(item)?.ToString() ?? string.Empty;
if (valueNode.Value != currentValue)
{
var value = Convert.ChangeType(valueNode.Value, property.PropertyType);
property.SetValue(item, value);
}
}
}
}

return SyncAttempt<TObject>.Succeed(ItemAlias(item), item, ChangeType.Import, []);
}

protected override Task<SyncAttempt<XElement>> SerializeCoreAsync(TObject item, SyncSerializerOptions options)
{
if (item == null)
return Task.FromResult(SyncAttempt<XElement>.Fail(string.Empty, null, ChangeType.Fail, "Item is null", new ArgumentNullException(nameof(item))));

var node = new XElement(ItemType,
new XAttribute(uSyncConstants.Xml.Key, ItemKey(item)),
new XAttribute(uSyncConstants.Xml.Alias, ItemAlias(item)));

var propertyNode = new XElement("Properties");

Type objectType = item.GetType();
var properies = objectType.GetProperties();
foreach (var property in properies)
{
// ignore if the object has "IgnoreDataMember" attribute
if (property.GetCustomAttributes(typeof(IgnoreDataMemberAttribute), true).Length > 0)
continue;

var value = property.GetValue(item)?.ToString() ?? string.Empty;
propertyNode.Add(new XElement(property.Name, value));
}

node.Add(propertyNode);

return Task.FromResult(SyncAttempt<XElement>.Succeed(ItemAlias(item), node, ChangeType.Export, []));
}
}
Loading