diff --git a/uSync.BackOffice.Targets/appsettings-schema.usync.json b/uSync.BackOffice.Targets/appsettings-schema.usync.json
index 6c64cb33..278bc8f7 100644
--- a/uSync.BackOffice.Targets/appsettings-schema.usync.json
+++ b/uSync.BackOffice.Targets/appsettings-schema.usync.json
@@ -248,6 +248,10 @@
"description": "Override the group the handler belongs too.",
"default": ""
},
+ "CreateClean": {
+ "type": "boolean",
+ "description": "create a corresponding _clean file for this export \n "
+ },
"Settings": {
"type": "object",
"description": "Additional settings for the handler",
diff --git a/uSync.BackOffice/Configuration/uSyncHandlerSettings.cs b/uSync.BackOffice/Configuration/uSyncHandlerSettings.cs
index d1f10da4..4c0df1d2 100644
--- a/uSync.BackOffice/Configuration/uSyncHandlerSettings.cs
+++ b/uSync.BackOffice/Configuration/uSyncHandlerSettings.cs
@@ -50,6 +50,14 @@ public class HandlerSettings
[DefaultValue("")]
public string Group { get; set; } = string.Empty;
+ ///
+ /// create a corresponding _clean file for this export
+ ///
+ ///
+ /// the clean file will only get created if the item in question has children.
+ ///
+ public bool CreateClean { get; set; } = false;
+
///
/// Additional settings for the handler
///
diff --git a/uSync.BackOffice/Services/uSyncService_Single.cs b/uSync.BackOffice/Services/uSyncService_Single.cs
index 32cc9bc3..6113ee54 100644
--- a/uSync.BackOffice/Services/uSyncService_Single.cs
+++ b/uSync.BackOffice/Services/uSyncService_Single.cs
@@ -1,11 +1,11 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Extensions.Logging;
-using Umbraco.Cms.Core.Scoping;
using Umbraco.Extensions;
using uSync.BackOffice.Extensions;
@@ -29,11 +29,21 @@ public partial class uSyncService
public IEnumerable ReportPartial(string folder, uSyncPagedImportOptions options, out int total)
{
var orderedNodes = LoadOrderedNodes(folder);
+ return ReportPartial(orderedNodes, options, out total);
+ }
+
+ ///
+ /// perform a paged report with the supplied ordered nodes
+ ///
+ public IEnumerable ReportPartial(IList orderedNodes, uSyncPagedImportOptions options, out int total)
+ {
total = orderedNodes.Count;
var actions = new List();
var lastType = string.Empty;
+ var folder = Path.GetDirectoryName(orderedNodes.FirstOrDefault()?.FileName ?? options.RootFolder);
+
SyncHandlerOptions syncHandlerOptions = HandlerOptionsFromPaged(options);
HandlerConfigPair handlerPair = null;
@@ -47,11 +57,13 @@ public IEnumerable ReportPartial(string folder, uSyncPagedImportOpt
{
lastType = itemType;
handlerPair = _handlerFactory.GetValidHandlerByTypeName(itemType, syncHandlerOptions);
+
+ handlerPair?.Handler.PreCacheFolderKeys(folder, orderedNodes.Select(x => x.Key).ToList());
}
if (handlerPair == null)
{
- _logger.LogWarning("No handler was found for {alias} ({itemType}) item might not process correctly", itemType);
+ _logger.LogWarning("No handler for {itemType} {alias}", itemType, item.Node.GetAlias());
continue;
}
@@ -73,12 +85,20 @@ public IEnumerable ReportPartial(string folder, uSyncPagedImportOpt
/// Perform a paged Import against a given folder
///
public IEnumerable ImportPartial(string folder, uSyncPagedImportOptions options, out int total)
+ {
+ var orderedNodes = LoadOrderedNodes(folder);
+ return ImportPartial(orderedNodes, options, out total);
+ }
+
+ ///
+ /// perform an import of items from the suppled ordered node list.
+ ///
+ public IEnumerable ImportPartial(IList orderedNodes, uSyncPagedImportOptions options, out int total)
{
lock (_importLock)
{
using (var pause = _mutexService.ImportPause(options.PauseDuringImport))
{
- var orderedNodes = LoadOrderedNodes(folder);
total = orderedNodes.Count;
@@ -99,20 +119,23 @@ public IEnumerable ImportPartial(string folder, uSyncPagedImportOpt
{
foreach (var item in orderedNodes.Skip(options.PageNumber * options.PageSize).Take(options.PageSize))
{
+ if (item.Node == null)
+ item.Node = XElement.Load(item.FileName);
+
var itemType = item.Node.GetItemType();
if (!itemType.InvariantEquals(lastType))
{
lastType = itemType;
handlerPair = _handlerFactory.GetValidHandlerByTypeName(itemType, syncHandlerOptions);
- // special case, blueprints looks like IContent items, except they are slightly different
- // so we check for them specifically and get the handler for the entity rather than the object type.
- if (item.Node.IsContent() && item.Node.IsBlueprint())
- {
- lastType = UdiEntityType.DocumentBlueprint;
- handlerPair = _handlerFactory.GetValidHandlerByEntityType(UdiEntityType.DocumentBlueprint);
- }
- }
+ // special case, blueprints looks like IContent items, except they are slightly different
+ // so we check for them specifically and get the handler for the entity rather than the object type.
+ if (item.Node.IsContent() && item.Node.IsBlueprint())
+ {
+ lastType = UdiEntityType.DocumentBlueprint;
+ handlerPair = _handlerFactory.GetValidHandlerByEntityType(UdiEntityType.DocumentBlueprint);
+ }
+ }
if (handlerPair == null)
{
@@ -306,7 +329,7 @@ private SyncHandlerOptions HandlerOptionsFromPaged(uSyncPagedImportOptions optio
///
/// Load the xml in a folder in level order so we process the higher level items first.
///
- private IList LoadOrderedNodes(string folder)
+ public IList LoadOrderedNodes(string folder)
{
var files = _syncFileService.GetFiles(folder, $"*.{_uSyncConfig.Settings.DefaultExtension}", true);
@@ -322,19 +345,6 @@ private IList LoadOrderedNodes(string folder)
.ToList();
}
- private class OrderedNodeInfo
- {
- public OrderedNodeInfo(string filename, XElement node)
- {
- this.FileName = filename;
- this.Node = node;
- }
-
- public XElement Node { get; set; }
- public string FileName { get; set; }
- }
-
-
///
/// calculate the percentage progress we are making between a range.
///
@@ -343,6 +353,38 @@ public OrderedNodeInfo(string filename, XElement node)
///
private int CalculateProgress(int value, int total, int min, int max)
=> (int)(min + (((float)value / total) * (max - min)));
+ }
+
+ ///
+ /// detail for a usync file that can be ordered
+ ///
+ public class OrderedNodeInfo
+ {
+ ///
+ /// constructor
+ ///
+ public OrderedNodeInfo(string filename, XElement node)
+ {
+ FileName = filename;
+ Node = node;
+ Key = node.GetKey();
+ }
+
+ ///
+ /// xml element of the node
+ ///
+ public XElement Node { get; set; }
+ ///
+ /// the Guid key for this item, so we can cache the list of keys
+ ///
+ public Guid Key { get; set; }
+
+ ///
+ /// path to the physical file
+ ///
+ public string FileName { get; set; }
}
+
+
}
diff --git a/uSync.BackOffice/SyncHandlers/Handlers/ContentHandler.cs b/uSync.BackOffice/SyncHandlers/Handlers/ContentHandler.cs
index 8911f335..a64cb808 100644
--- a/uSync.BackOffice/SyncHandlers/Handlers/ContentHandler.cs
+++ b/uSync.BackOffice/SyncHandlers/Handlers/ContentHandler.cs
@@ -58,6 +58,10 @@ public ContentHandler(
this.serializer = syncItemFactory.GetSerializer("ContentSerializer");
}
+ ///
+ protected override bool HasChildren(IContent item)
+ => contentService.HasChildren(item.Id);
+
///
/// Get child items
///
diff --git a/uSync.BackOffice/SyncHandlers/Handlers/ContentHandlerBase.cs b/uSync.BackOffice/SyncHandlers/Handlers/ContentHandlerBase.cs
index aaf06223..ec555713 100644
--- a/uSync.BackOffice/SyncHandlers/Handlers/ContentHandlerBase.cs
+++ b/uSync.BackOffice/SyncHandlers/Handlers/ContentHandlerBase.cs
@@ -103,7 +103,7 @@ private bool ImportTrashedItem(XElement node, HandlerSettings config)
{
// unless the setting is explicit we don't import trashed items.
var trashed = node.Element("Info")?.Element("Trashed").ValueOrDefault(false);
- if (trashed.GetValueOrDefault(false) && !config.GetSetting("ImportTrashed", true)) return false;
+ if (trashed.GetValueOrDefault(false) && !config.GetSetting("ImportTrashed", false)) return false;
return true;
}
diff --git a/uSync.BackOffice/SyncHandlers/Handlers/MediaHandler.cs b/uSync.BackOffice/SyncHandlers/Handlers/MediaHandler.cs
index 7498360b..49fbd247 100644
--- a/uSync.BackOffice/SyncHandlers/Handlers/MediaHandler.cs
+++ b/uSync.BackOffice/SyncHandlers/Handlers/MediaHandler.cs
@@ -51,6 +51,10 @@ public MediaHandler(
this.mediaService = mediaService;
}
+ ///
+ protected override bool HasChildren(IMedia item)
+ => mediaService.HasChildren(item.Id);
+
///
protected override IEnumerable GetChildItems(IEntity parent)
{
diff --git a/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs b/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs
index 76762769..88c252f4 100644
--- a/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs
+++ b/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs
@@ -76,7 +76,7 @@ public interface ISyncHandler
string EntityType { get; }
///
- /// The type name of the items hanled (Item.getType().ToString())
+ /// The type name of the items handled (Item.getType().ToString())
///
string TypeName { get; }
@@ -95,7 +95,7 @@ public interface ISyncHandler
///
/// folder to use when exporting
/// Handler settings to use for export
- /// Callbacks to keep UI uptodate
+ /// Callbacks to keep UI upto date
/// List of actions detailing changes
IEnumerable ExportAll(string folder, HandlerSettings settings, SyncUpdateCallback callback);
@@ -124,7 +124,7 @@ public interface ISyncHandler
/// folder to use when Importing
/// Handler settings to use for import
/// Force the import even if the settings haven't changed
- /// Callbacks to keep UI uptodate
+ /// Callbacks to keep UI upto date
/// List of actions detailing changes
IEnumerable ImportAll(string folder, HandlerSettings settings, bool force, SyncUpdateCallback callback);
@@ -138,7 +138,7 @@ public interface ISyncHandler
///
/// folder to use when reporting
/// Handler settings to use for report
- /// Callbacks to keep UI uptodate
+ /// Callbacks to keep UI upto date
/// List of actions detailing changes
IEnumerable Report(string folder, HandlerSettings settings, SyncUpdateCallback callback);
@@ -154,14 +154,21 @@ public interface ISyncHandler
IEnumerable ImportSecondPass(uSyncAction action, HandlerSettings settings, uSyncImportOptions options);
///
- /// default impimentation, roothandler does do this.
+ /// default implementation, root handler does do this.
///
Udi FindFromNode(XElement node) => null;
///
- /// is this a current node (roothandler can do this too)
+ /// is this a current node (root handler can do this too)
///
ChangeType GetItemStatus(XElement node) => ChangeType.NoChange;
+
+ ///
+ /// precaches the keys of a folder
+ ///
+ ///
+ ///
+ void PreCacheFolderKeys(string folder, IList keys) { }
}
}
diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerBase.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerBase.cs
index 06a57561..a9a10092 100644
--- a/uSync.BackOffice/SyncHandlers/SyncHandlerBase.cs
+++ b/uSync.BackOffice/SyncHandlers/SyncHandlerBase.cs
@@ -11,6 +11,7 @@
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
+using Umbraco.Extensions;
using uSync.BackOffice.Configuration;
using uSync.BackOffice.Services;
@@ -50,6 +51,10 @@ public SyncHandlerBase(
this.entityService = entityService;
}
+ ///
+ protected override bool HasChildren(TObject item)
+ => entityService.GetChildren(item.Id).Any();
+
///
/// given a folder we calculate what items we can remove, becuase they are
/// not in one the the files in the folder.
@@ -189,7 +194,12 @@ protected override IEnumerable GetChildItems(IEntity parent)
///
virtual protected IEnumerable GetChildItems(int parent)
{
- if (this.itemObjectType != UmbracoObjectTypes.Unknown)
+ if (this.itemObjectType == UmbracoObjectTypes.Unknown)
+ return Enumerable.Empty();
+
+ var cacheKey = $"{GetCacheKeyBase()}_parent_{parent}";
+
+ return runtimeCache.GetCacheItem(cacheKey, () =>
{
if (parent == -1)
{
@@ -201,9 +211,7 @@ virtual protected IEnumerable GetChildItems(int parent)
// load it, so GetChildren without the object type is quicker.
return entityService.GetChildren(parent);
}
- }
-
- return Enumerable.Empty();
+ }, null);
}
///
diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs
index d1d2d16c..e2252510 100644
--- a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs
+++ b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs
@@ -609,6 +609,20 @@ protected virtual IEnumerable CleanFolder(string cleanFile, bool re
}
}
+ ///
+ /// pre-populates the cache folder key list.
+ ///
+ ///
+ /// this means if we are calling the process multiple times,
+ /// we can optimise the key code and only load it once.
+ ///
+ public void PreCacheFolderKeys(string folder, IList folderKeys)
+ {
+ var cacheKey = $"{GetCacheKeyBase()}_{folder.GetHashCode()}";
+ runtimeCache.ClearByKey(cacheKey) ;
+ runtimeCache.GetCacheItem(cacheKey, () => folderKeys);
+ }
+
///
/// Get the GUIDs for all items in a folder
///
@@ -624,10 +638,11 @@ protected IList GetFolderKeys(string folder, bool flat)
var cacheKey = $"{GetCacheKeyBase()}_{folderKey}";
- logger.LogDebug("Getting Folder Keys : {cacheKey}", cacheKey);
return runtimeCache.GetCacheItem(cacheKey, () =>
{
+ logger.LogDebug("Getting Folder Keys : {cacheKey}", cacheKey);
+
// when it's not flat structure we also get the sub folders. (extra defensive get them all)
var keys = new List();
var files = syncFileService.GetFiles(folder, $"*.{this.uSyncConfig.Settings.DefaultExtension}", !flat).ToList();
@@ -853,6 +868,11 @@ virtual public IEnumerable Export(TObject item, string folder, Hand
{
// only write the file to disk if it should be exported.
syncFileService.SaveXElement(attempt.Item, filename);
+
+ if (config.CreateClean && HasChildren(item))
+ {
+ CreateCleanFile(GetItemKey(item), filename);
+ }
}
else
{
@@ -865,6 +885,31 @@ virtual public IEnumerable Export(TObject item, string folder, Hand
return uSyncActionHelper.SetAction(attempt, filename, GetItemKey(item), this.Alias).AsEnumerableOfOne();
}
+ ///
+ /// does this item have any children ?
+ ///
+ ///
+ /// on items where we can check this (quickly) we can reduce the number of checks we might
+ /// make on child items or cleaning up where we don't need to.
+ ///
+ protected virtual bool HasChildren(TObject item)
+ => true;
+
+ private void CreateCleanFile(Guid key, string filename)
+ {
+ if (string.IsNullOrWhiteSpace(filename) || key == Guid.Empty)
+ return;
+
+ var folder = Path.GetDirectoryName(filename);
+ var name = Path.GetFileNameWithoutExtension(filename);
+
+ var cleanPath = Path.Combine(folder, $"{name}_clean.config");
+
+ var node = XElementExtensions.MakeEmpty(key, SyncActionType.Clean, $"clean {name} children");
+ node.Add(new XAttribute("itemType", serializer.ItemType));
+ syncFileService.SaveXElement(node, cleanPath);
+ }
+
#endregion
#region Reporting
@@ -1749,7 +1794,11 @@ private string GetNameFromFileOrNode(string filename, XElement node)
=> !string.IsNullOrWhiteSpace(filename) ? filename : node.GetAlias();
- private string GetCacheKeyBase()
+ ///
+ /// get thekey for any caches we might call (thread based cache value)
+ ///
+ ///
+ protected string GetCacheKeyBase()
=> $"keycache_{this.Alias}_{Thread.CurrentThread.ManagedThreadId}";
private string PrepCaches()
diff --git a/uSync.Core/Serialization/Serializers/ContentSerializer.cs b/uSync.Core/Serialization/Serializers/ContentSerializer.cs
index 7f44d2a2..acc02213 100644
--- a/uSync.Core/Serialization/Serializers/ContentSerializer.cs
+++ b/uSync.Core/Serialization/Serializers/ContentSerializer.cs
@@ -173,7 +173,8 @@ protected override SyncAttempt DeserializeCore(XElement node, SyncSeri
if (node.Element("Info") != null)
{
var trashed = node.Element("Info").Element("Trashed").ValueOrDefault(false);
- details.AddNotNull(HandleTrashedState(item, trashed));
+ var restoreParent = node.Element("Info").Element("Trashed").Attribute("Parent").ValueOrDefault(Guid.Empty);
+ details.AddNotNull(HandleTrashedState(item, trashed, restoreParent));
}
details.AddNotNull(DeserializeTemplate(item, node));
@@ -395,29 +396,35 @@ private ContentSchedule FindSchedule(ContentScheduleCollection currentSchedules,
}
- protected override uSyncChange HandleTrashedState(IContent item, bool trashed)
+ protected override uSyncChange HandleTrashedState(IContent item, bool trashed, Guid restoreParentKey)
{
if (!trashed && item.Trashed)
{
// if the item is trashed, then the change of it's parent
// should restore it (as long as we do a move!)
- contentService.Move(item, item.ParentId);
+
+ var restoreParentId = GetRelationParentId(item, restoreParentKey, Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias);
+ contentService.Move(item, restoreParentId);
// clean out any relations for this item (some versions of Umbraco don't do this on a Move)
- CleanRelations(item, "relateParentDocumentOnDelete");
+ CleanRelations(item, Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias);
- return uSyncChange.Update("Restored", item.Name, "Recycle Bin", item.ParentId.ToString());
+ return uSyncChange.Update("Restored", item.Name, "Recycle Bin", restoreParentKey.ToString());
}
else if (trashed && !item.Trashed)
{
+ // not already in the recycle bin?
+ if (item.ParentId > Constants.System.RecycleBinContent)
+ {
+ // clean any relations that may be there (stops an error)
+ CleanRelations(item, Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias);
- // clean any relations that may be there (stops an error)
- CleanRelations(item, "relateParentDocumentOnDelete");
+ // move to the recycle bin
+ contentService.MoveToRecycleBin(item);
+ }
- // move to the recycle bin
- contentService.MoveToRecycleBin(item);
return uSyncChange.Update("Moved to Bin", item.Name, "", "Recycle Bin");
}
@@ -432,8 +439,9 @@ protected virtual Attempt DoSaveOrPublish(IContent item, XElement node,
return Attempt.Succeed("No Changes");
}
+ var trashed = item.Trashed || (node.Element("Info")?.Element("Trashed").ValueOrDefault(false) ?? false);
var publishedNode = node.Element("Info")?.Element("Published");
- if (!item.Trashed && publishedNode != null)
+ if (!trashed && publishedNode != null)
{
var schedules = GetSchedules(node.Element("Info")?.Element("Schedule"));
diff --git a/uSync.Core/Serialization/Serializers/ContentSerializerBase.cs b/uSync.Core/Serialization/Serializers/ContentSerializerBase.cs
index 0300a0d9..e53bc176 100644
--- a/uSync.Core/Serialization/Serializers/ContentSerializerBase.cs
+++ b/uSync.Core/Serialization/Serializers/ContentSerializerBase.cs
@@ -371,20 +371,25 @@ protected virtual IEnumerable DeserializeBase(TObject item, XElemen
}
}
- if (item.ParentId != parentId)
+ if (!item.Trashed)
{
- changes.AddUpdate(uSyncConstants.Xml.Parent, item.ParentId, parentId);
- logger.LogTrace("{Id} Setting Parent {ParentId}", item.Id, parentId);
- item.ParentId = parentId;
- }
+ // we change if its not in the bin,
+ // if its in the bin it will get fixed by handle trashed state.
+ if (item.ParentId != parentId)
+ {
+ changes.AddUpdate(uSyncConstants.Xml.Parent, item.ParentId, parentId);
+ logger.LogTrace("{Id} Setting Parent {ParentId}", item.Id, parentId);
+ item.ParentId = parentId;
+ }
- // the following are calculated (not in the file
- // because they might change without this node being saved).
- if (item.Path != nodePath)
- {
- changes.AddUpdate(uSyncConstants.Xml.Path, item.Path, nodePath);
- logger.LogDebug("{Id} Setting Path {idPath} was {oldPath}", item.Id, nodePath, item.Path);
- item.Path = nodePath;
+ // the following are calculated (not in the file
+ // because they might change without this node being saved).
+ if (item.Path != nodePath)
+ {
+ changes.AddUpdate(uSyncConstants.Xml.Path, item.Path, nodePath);
+ logger.LogDebug("{Id} Setting Path {idPath} was {oldPath}", item.Id, nodePath, item.Path);
+ item.Path = nodePath;
+ }
}
if (item.Level != nodeLevel)
@@ -394,7 +399,17 @@ protected virtual IEnumerable DeserializeBase(TObject item, XElemen
item.Level = nodeLevel;
}
}
-
+ else // trashed.
+ {
+ // we need to set the parent to something,
+ // or the move will fail.
+ if (item.ParentId == -1)
+ {
+ item.ParentId = item is IContent
+ ? Constants.System.RecycleBinContent
+ : Constants.System.RecycleBinMedia;
+ }
+ }
var key = node.GetKey();
if (key != Guid.Empty && item.Key != key)
@@ -626,7 +641,12 @@ protected uSyncChange HandleSortOrder(TObject item, int sortOrder)
return null;
}
- protected abstract uSyncChange HandleTrashedState(TObject item, bool trashed);
+ [Obsolete("Pass in a restore guid for the parent - should relationships be missing")]
+ protected virtual uSyncChange HandleTrashedState(TObject item, bool trashed)
+ => uSyncChange.NoChange($"Member/{item.Name}", item.Name);
+
+ protected virtual uSyncChange HandleTrashedState(TObject item, bool trashed, Guid restoreParent)
+ => uSyncChange.NoChange($"Member/{item.Name}", item.Name);
protected string GetExportValue(object value, IPropertyType propertyType, string culture, string segment)
{
@@ -679,13 +699,17 @@ protected override Attempt FindOrCreate(XElement node)
var alias = node.GetAlias();
- var parentKey = node.Attribute(uSyncConstants.Xml.Parent).ValueOrDefault(Guid.Empty);
+ var parentKey = node.Element(uSyncConstants.Xml.Info)
+ ?.Element(uSyncConstants.Xml.Parent)
+ ?.Attribute(uSyncConstants.Xml.Key)
+ .ValueOrDefault(Guid.Empty) ?? Guid.Empty;
+
if (parentKey != Guid.Empty)
{
item = FindItem(alias, parentKey);
if (item != null) return Attempt.Succeed(item);
}
-
+
// create
var parent = default(TObject);
@@ -982,6 +1006,28 @@ protected void CleanRelations(TObject item, string relationType)
}
+ protected int GetRelationParentId(TObject item, Guid restoreParentKey, string relationType)
+ {
+ var parentId = -1;
+ try
+ {
+ var deleteRelations = relationService.GetByChild(item, relationType);
+ if (deleteRelations.Any())
+ parentId = deleteRelations.FirstOrDefault()?.ParentId ?? -1;
+
+ if (parentId != -1) return parentId;
+ return restoreParentKey == Guid.Empty ? -1 : entityService.Get(restoreParentKey)?.Id ?? -1;
+ }
+ catch (Exception ex)
+ {
+ // unable to find an existing delete relation.
+ logger.LogWarning(ex, "Error finding restore relation");
+ }
+
+ return -1;
+
+ }
+
private List GetExcludedProperties(SyncSerializerOptions options)
{
diff --git a/uSync.Core/Serialization/Serializers/ContentTypeBaseSerializer.cs b/uSync.Core/Serialization/Serializers/ContentTypeBaseSerializer.cs
index 6ec16d26..281f956e 100644
--- a/uSync.Core/Serialization/Serializers/ContentTypeBaseSerializer.cs
+++ b/uSync.Core/Serialization/Serializers/ContentTypeBaseSerializer.cs
@@ -1126,7 +1126,7 @@ private IPropertyType GetOrCreateProperty(TObject item,
if (dataType == null)
{
- logger.LogWarning("Cannot find underling DataType {key} {alias} for {property} it is likely you are missing a package?", definitionKey, propertyEditorAlias, alias);
+ logger.LogWarning("Cannot find underling DataType {key} {alias} for {property} - Either your datatypes are out of sync or you are missing a package?", definitionKey, propertyEditorAlias, alias);
return null;
}
diff --git a/uSync.Core/Serialization/Serializers/MediaSerializer.cs b/uSync.Core/Serialization/Serializers/MediaSerializer.cs
index 92d77ea0..09ca7b12 100644
--- a/uSync.Core/Serialization/Serializers/MediaSerializer.cs
+++ b/uSync.Core/Serialization/Serializers/MediaSerializer.cs
@@ -65,7 +65,8 @@ protected override SyncAttempt DeserializeCore(XElement node, SyncSerial
if (node.Element("Info") != null)
{
var trashed = node.Element("Info").Element("Trashed").ValueOrDefault(false);
- details.AddNotNull( HandleTrashedState(item, trashed));
+ var restoreParent = node.Element("Info").Element("Trashed").Attribute("Parent").ValueOrDefault(Guid.Empty);
+ details.AddNotNull(HandleTrashedState(item, trashed, restoreParent));
}
var propertyAttempt = DeserializeProperties(item, node, options);
@@ -103,22 +104,24 @@ protected override SyncAttempt DeserializeCore(XElement node, SyncSerial
return SyncAttempt.Succeed(item.Name, item, ChangeType.Import, "", true, propertyAttempt.Result);
}
- protected override uSyncChange HandleTrashedState(IMedia item, bool trashed)
+ protected override uSyncChange HandleTrashedState(IMedia item, bool trashed, Guid restoreParentKey)
{
if (!trashed && item.Trashed)
{
// if the item is trashed, then moving it back to the parent value
// restores it.
- _mediaService.Move(item, item.ParentId);
- CleanRelations(item, "relateParentMediaFolderOnDelete");
+ var restoreParentId = GetRelationParentId(item, restoreParentKey, Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias);
+ _mediaService.Move(item, restoreParentId);
+
+ CleanRelations(item, Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias);
return uSyncChange.Update("Restored", item.Name, "Recycle Bin", item.ParentId.ToString());
}
else if (trashed && !item.Trashed)
{
// clean any rouge relations
- CleanRelations(item, "relateParentMediaFolderOnDelete");
+ CleanRelations(item, Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias);
// move to the recycle bin
_mediaService.MoveToRecycleBin(item);