/// <summary> /// Initialize the XElement with the core Key, Name, Level values /// </summary> protected virtual XElement InitializeNode(TObject item, string typeName, SyncSerializerOptions options) { var node = new XElement(this.ItemType, new XAttribute("Key", item.Key), new XAttribute("Alias", item.Name), new XAttribute("Level", GetLevel(item))); // are we only serizling some cultures ? var cultures = options.GetSetting(uSyncConstants.CultureKey, string.Empty); if (!string.IsNullOrWhiteSpace(cultures) && item.ContentType.VariesByCulture()) { node.Add(new XAttribute(uSyncConstants.CultureKey, cultures)); } // are we only serizling some segments ? var segments = options.GetSetting(uSyncConstants.SegmentKey, string.Empty); if (!string.IsNullOrWhiteSpace(segments) && item.ContentType.Variations.VariesBySegment()) { node.Add(new XAttribute(uSyncConstants.SegmentKey, segments)); } // are we including the default (not variant) values in the serialized result? if (options.GetSetting(uSyncConstants.DefaultsKey, false) && !item.ContentType.VariesByNothing()) { node.Add(new XAttribute(uSyncConstants.DefaultsKey, true)); } return(node); }
/// <summary> /// Initialize the XElement with the core Key, Name, Level values /// </summary> protected virtual XElement InitializeNode(TObject item, string typeName, SyncSerializerOptions options) { var node = new XElement(this.ItemType, new XAttribute("Key", item.Key), new XAttribute("Alias", item.Name), new XAttribute("Level", GetLevel(item))); // are we only serizling some cultures ? var cultures = options.GetSetting(uSyncConstants.CultureKey, string.Empty); if (IsPartialCultureElement(item, cultures)) { node.Add(new XAttribute(uSyncConstants.CultureKey, cultures)); } // are we only serizling some segments ? var segments = options.GetSetting(uSyncConstants.SegmentKey, string.Empty); if (IsPartialSegmentElement(item, segments)) { node.Add(new XAttribute(uSyncConstants.SegmentKey, segments)); } // are we including the default (not variant) values in the serialized result? // we only worry about this when we are passing partial cultures or segments // to the file, when we sync complte content items, this is reduntant. if (options.GetSetting(uSyncConstants.DefaultsKey, true) && IsPartialElement(item, cultures, segments)) { node.Add(new XAttribute(uSyncConstants.DefaultsKey, true)); } return(node); }
protected override SyncAttempt <XElement> SerializeCore(IRelationType item, SyncSerializerOptions options) { var node = this.InitializeBaseNode(item, item.Alias); var isDependency = false; if (item is IRelationTypeWithIsDependency dependencyItem) { isDependency = dependencyItem.IsDependency; } node.Add(new XElement("Info", new XElement("Name", item.Name), new XElement("ParentType", GetGuidValue(item, nameof(item.ParentObjectType))), new XElement("ChildType", GetGuidValue(item, nameof(item.ChildObjectType))), new XElement("Bidirectional", item.IsBidirectional), new XElement("IsDependency", isDependency))); if (options.GetSetting <bool>("IncludeRelations", false)) { node.Add(SerializeRelations(item)); } return(SyncAttempt <XElement> .SucceedIf( node != null, item.Name, node, typeof(IRelationType), ChangeType.Export)); }
/// <summary> /// Checks the config to see if we should be deserializing the config element of a data type. /// </summary> /// <remarks> /// a key value on the handler will allow users to add editorAliases that they don't want the /// config importing for. /// e.g - to not import all the colour picker values. /// <code> /// <Add Key="NoConfigEditors" Value="Umbraco.ColorPicker" /> /// </code> /// /// To ignore just specific colour pickers (so still import config for other colour pickers) /// <code> /// <Add Key="NoConfigNames" Value="Approved Colour,My Colour Picker" /> /// </code> /// </remarks> private bool ShouldDeserilizeConfig(string itemName, string editorAlias, SyncSerializerOptions options) { var noConfigEditors = options.GetSetting("NoConfigEditors", string.Empty); if (!string.IsNullOrWhiteSpace(noConfigEditors) && noConfigEditors.InvariantContains(editorAlias)) { return(false); } var noConfigAliases = options.GetSetting("NoConfigNames", string.Empty); if (!string.IsNullOrWhiteSpace(noConfigAliases) && noConfigAliases.InvariantContains(itemName)) { return(false); } return(true); }
protected override SyncAttempt <IDictionaryItem> DeserializeCore(XElement node, SyncSerializerOptions options) { var item = FindItem(node); var info = node.Element("Info"); var alias = node.GetAlias(); var details = new List <uSyncChange>(); Guid?parentKey = null; var parentItemKey = info.Element("Parent").ValueOrDefault(string.Empty); if (parentItemKey != string.Empty) { var parent = localizationService.GetDictionaryItemByKey(parentItemKey); if (parent != null) { parentKey = parent.Key; } } var key = node.GetKey(); if (item == null) { item = new DictionaryItem(parentKey, alias); item.Key = key; } else { item.ParentId = parentKey; } if (item.ItemKey != alias) { details.AddUpdate("ItemKey", item.ItemKey, alias); item.ItemKey = alias; } if (item.Key != key) { details.AddUpdate("Key", item.Key, key); item.Key = key; } // key only translationm, would not add the translation values. if (!options.GetSetting("KeysOnly", false)) { details.AddRange(DeserializeTranslations(item, node)); } // this.SaveItem(item); return(SyncAttempt <IDictionaryItem> .Succeed(item.ItemKey, item, ChangeType.Import, details)); }
protected override SyncAttempt <XElement> SerializeCore(ITemplate item, SyncSerializerOptions options) { var node = this.InitializeBaseNode(item, item.Alias, this.CalculateLevel(item)); node.Add(new XElement("Name", item.Name)); node.Add(new XElement("Parent", item.MasterTemplateAlias)); if (options.GetSetting(uSyncConstants.Conventions.IncludeContent, false)) { node.Add(SerializeContent(item)); } return(SyncAttempt <XElement> .Succeed(item.Name, node, typeof(ITemplate), ChangeType.Export)); }
public override SyncAttempt <ITemplate> DeserializeSecondPass(ITemplate item, XElement node, SyncSerializerOptions options) { var details = new List <uSyncChange>(); var saved = false; var msg = ""; var master = node.Element("Parent").ValueOrDefault(string.Empty); if (master != string.Empty && item.MasterTemplateAlias != master) { logger.LogDebug("Looking for master {0}", master); var masterItem = fileService.GetTemplate(master); if (masterItem != null && item.MasterTemplateAlias != master) { details.AddUpdate("Parent", item.MasterTemplateAlias, master); logger.LogDebug("Setting Master {0}", masterItem.Alias); item.SetMasterTemplate(masterItem); SaveItem(item); saved = true; } } if (options.GetSetting("UsingRazorViews", false)) { // using razor views - we delete the template file at the end (because its in a razor view). var templatePath = ViewPath(item.Alias); if (_viewFileSystem.FileExists(templatePath)) { var fullPath = _viewFileSystem.GetFullPath(templatePath); if (System.IO.File.Exists(fullPath)) { var content = System.IO.File.ReadAllText(fullPath); if (content.Contains($"[uSyncMarker:{this.Id}]")) { logger.LogDebug($"Removing the file from disk, because it exists in a razor view {templatePath}"); msg = "Using Razor views"; _viewFileSystem.DeleteFile(templatePath); // we have to tell the handlers we saved it - or they will and write the file back return(SyncAttempt <ITemplate> .Succeed(item.Name, item, ChangeType.Import, "Razor view removed", true, details)); } } } } return(SyncAttempt <ITemplate> .Succeed(item.Name, item, ChangeType.Import, msg, saved, details)); }
protected override SyncAttempt <XElement> SerializeCore(IDictionaryItem item, SyncSerializerOptions options) { var node = InitializeBaseNode(item, item.ItemKey, GetLevel(item)); // if we are serializing by culture, then add the culture attribute here. var cultures = options.GetSetting(uSyncConstants.CultureKey, string.Empty); if (!string.IsNullOrWhiteSpace(cultures)) { node.Add(new XAttribute(uSyncConstants.CultureKey, cultures)); } var info = new XElement("Info"); if (item.ParentId.HasValue) { var parent = FindItem(item.ParentId.Value); if (parent != null) { info.Add(new XElement("Parent", parent.ItemKey)); } } var activeCultures = options.GetCultures(); var translationsNode = new XElement("Translations"); foreach (var translation in item.Translations .SafeDistinctBy(x => x.Language.IsoCode) .OrderBy(x => x.Language.IsoCode)) { if (activeCultures.IsValid(translation.Language.IsoCode)) { translationsNode.Add(new XElement("Translation", translation.Value, new XAttribute("Language", translation.Language.IsoCode))); } } node.Add(info); node.Add(translationsNode); return(SyncAttempt <XElement> .Succeed( item.ItemKey, node, typeof(IDictionaryItem), ChangeType.Export)); }
protected override SyncAttempt <XElement> SerializeCore(IRelationType item, SyncSerializerOptions options) { var node = this.InitializeBaseNode(item, item.Alias); node.Add(new XElement("Info", new XElement("Name", item.Name), new XElement("ParentType", item.ParentObjectType), new XElement("ChildType", item.ChildObjectType), new XElement("Bidirectional", item.IsBidirectional))); if (options.GetSetting <bool>("IncludeRelations", true)) { node.Add(SerializeRelations(item)); } return(SyncAttempt <XElement> .SucceedIf( node != null, item.Name, node, typeof(IRelationType), ChangeType.Export)); }
protected override SyncAttempt <XElement> SerializeCore(IMedia item, SyncSerializerOptions options) { var node = InitializeNode(item, item.ContentType.Alias, options); var info = SerializeInfo(item, options); var properties = SerializeProperties(item, options); node.Add(info); node.Add(properties); // serializing the file hash, will mean if the image changes, then the media item will // trigger as a change - this doesn't mean the image will be updated other methods are // used to copy media between servers (uSync.Complete) if (options.GetSetting("IncludeFileHash", true)) { info.Add(SerializeFileHash(item)); } return(SyncAttempt <XElement> .Succeed( item.Name, node, typeof(IMedia), ChangeType.Export)); }
protected override SyncAttempt <IRelationType> DeserializeCore(XElement node, SyncSerializerOptions options) { var key = node.GetKey(); var alias = node.GetAlias(); var info = node.Element("Info"); var name = info.Element("Name").ValueOrDefault(string.Empty); var parentType = info.Element("ParentType").ValueOrDefault(Guid.Empty); var childType = info.Element("ChildType").ValueOrDefault(Guid.Empty); var bidirectional = info.Element("Bidirectional").ValueOrDefault(false); var item = FindItem(node); if (item == null) { item = new RelationType(childType, parentType, alias); } var details = new List <uSyncChange>(); if (item.Key != key) { details.AddUpdate("Key", item.Key, key); item.Key = key; } if (item.Name != name) { details.AddUpdate("Name", item.Name, name); item.Name = name; } if (item.Alias != alias) { details.AddUpdate("Alias", item.Alias, alias); item.Alias = alias; } if (item.ParentObjectType != parentType) { details.AddUpdate("ParentType", item.ParentObjectType, parentType); item.ParentObjectType = parentType; } if (item.ChildObjectType != childType) { details.AddUpdate("ChildType", item.ChildObjectType, childType); item.ChildObjectType = childType; } if (item.IsBidirectional = bidirectional) { details.AddUpdate("Bidirectional", item.IsBidirectional, bidirectional); item.IsBidirectional = bidirectional; } var hasBeenSaved = false; if (options.GetSetting <bool>("IncludeRelations", true)) { // we have to save before we can add the relations. this.SaveItem(item); hasBeenSaved = true; details.AddRange(DeserializeRelations(node, item)); } return(SyncAttempt <IRelationType> .Succeed(item.Name, item, ChangeType.Import, hasBeenSaved, details)); }
protected override SyncAttempt <ITemplate> DeserializeCore(XElement node, SyncSerializerOptions options) { var key = node.GetKey(); var alias = node.GetAlias(); var name = node.Element("Name").ValueOrDefault(string.Empty); var item = default(ITemplate); var details = new List <uSyncChange>(); if (key != Guid.Empty) { item = fileService.GetTemplate(key); } if (item == null) { item = fileService.GetTemplate(alias); } if (item == null) { item = new Template(shortStringHelper, name, alias); details.AddNew(alias, alias, "Template"); if (ShouldGetContentFromNode(node, options)) { logger.LogDebug("Getting content for Template from XML"); item.Content = GetContentFromConfig(node); } else { logger.LogDebug("Loading template content from disk"); var templatePath = ViewPath(alias); if (_viewFileSystem.FileExists(templatePath)) { logger.LogDebug("Reading {0} contents", templatePath); item.Content = GetContentFromFile(templatePath); item.Path = templatePath; } else { if (!options.GetSetting <bool>("UsingRazorViews", false)) { // template is missing // we can't create logger.LogWarning("Failed to create template {path} the local file is missing", templatePath); return(SyncAttempt <ITemplate> .Fail(name, ChangeType.Import, $"The template {templatePath} file is missing.")); } else { // template is not on disk, we could use the viewEngine to find the view // if this finds the view it tells us that the view is somewhere else ? logger.LogDebug("Failed to find content, but UsingRazorViews so will create anyway, then delete the file"); item.Content = $"<!-- [uSyncMarker:{this.Id}] template content - will be removed -->"; } } } } if (item == null) { // creating went wrong logger.LogWarning("Failed to create template"); return(SyncAttempt <ITemplate> .Fail(name, ChangeType.Import, "Failed to create template")); } if (item.Key != key) { details.AddUpdate("Key", item.Key, key); item.Key = key; } if (item.Name != name) { details.AddUpdate("Name", item.Name, name); item.Name = name; } if (item.Alias != alias) { details.AddUpdate("Alias", item.Alias, alias); item.Alias = alias; } if (ShouldGetContentFromNode(node, options)) { var content = GetContentFromConfig(node); if (content != item.Content) { details.AddUpdate("Content", item.Content, content); item.Content = content; } } //var master = node.Element("Parent").ValueOrDefault(string.Empty); //if (master != string.Empty) //{ // var masterItem = fileService.GetTemplate(master); // if (masterItem != null) // item.SetMasterTemplate(masterItem); //} // Deserialize now takes care of the save. // fileService.SaveTemplate(item); return(SyncAttempt <ITemplate> .Succeed(item.Name, item, ChangeType.Import, details)); }
protected virtual Attempt <string> DoSaveOrPublish(IContent item, XElement node, SyncSerializerOptions options) { if (options.GetSetting(uSyncConstants.DefaultSettings.OnlyPublishDirty, uSyncConstants.DefaultSettings.OnlyPublishDirty_Default) && !item.IsDirty()) { logger.LogDebug("{name} not publishing because nothing is dirty [{dirty} {userDirty}]", item.Name, item.IsDirty(), item.IsAnyUserPropertyDirty()); return(Attempt.Succeed("No Changes")); } var publishedNode = node.Element("Info")?.Element("Published"); if (!item.Trashed && publishedNode != null) { var schedules = GetSchedules(node.Element("Info")?.Element("Schedule")); if (publishedNode.HasElements) { // culture based publishing. var cultures = options.GetDeserializedCultures(node); // Only unpublish other cultures, when we are not already filtered by cultures // this stops things we don't care about this time being unpublished. var unpublishMissingCultures = cultures.Count == 0; var cultureStatuses = new Dictionary <string, uSyncContentState>(); foreach (var culturePublish in publishedNode.Elements("Published")) { var culture = culturePublish.Attribute("Culture").ValueOrDefault(string.Empty); if (!string.IsNullOrWhiteSpace(culture) && cultures.IsValid(culture)) { // is the item published in the config file var configState = culturePublish.ValueOrDefault(false) ? uSyncContentState.Published : uSyncContentState.Unpublished; // pending or outstanding scheduled actions can change the action we take. cultureStatuses[culture] = schedules.CalculateCultureState(culture, configState); } } if (cultureStatuses.Count > 0) { return(PublishItem(item, cultureStatuses, unpublishMissingCultures)); } } else { var state = publishedNode.Attribute("Default").ValueOrDefault(false) ? uSyncContentState.Published : uSyncContentState.Unpublished; state = schedules.CalculateCultureState(string.Empty, state); if (state == uSyncContentState.Published) { return(PublishItem(item)); } else if (state == uSyncContentState.Unpublished && item.Published == true) { contentService.Unpublish(item); } } } this.SaveItem(item); return(Attempt.Succeed("Saved")); }
protected override SyncAttempt <IContent> 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, options)); if (node.Element("Info") != null) { var trashed = node.Element("Info").Element("Trashed").ValueOrDefault(false); details.AddNotNull(HandleTrashedState(item, trashed)); } details.AddNotNull(DeserializeTemplate(item, node)); var propertiesAttempt = DeserializeProperties(item, node, options); if (!propertiesAttempt.Success) { return(SyncAttempt <IContent> .Fail(item.Name, item, ChangeType.ImportFail, "Failed to deserialize properties", attempt.Exception)); } details.AddRange(propertiesAttempt.Result); // sort order var sortOrder = node.Element("Info").Element("SortOrder").ValueOrDefault(-1); details.AddNotNull(HandleSortOrder(item, sortOrder)); var publishTimer = Stopwatch.StartNew(); if (details.HasWarning() && options.FailOnWarnings()) { // Fail on warning. means we don't save or publish because something is wrong ? return(SyncAttempt <IContent> .Fail(item.Name, item, ChangeType.ImportFail, "Failed with warnings", details, new Exception("Import failed because of warnings, and fail on warnings is true"))); } // published status // this does the last save and publish var saveAttempt = DoSaveOrPublish(item, node, options); if (saveAttempt.Success) { var message = saveAttempt.Result; if (details.Any(x => x.Change == ChangeDetailType.Warning)) { message += $" with warning(s)"; } if (publishTimer.ElapsedMilliseconds > 10000) { message += $" (Slow publish {publishTimer.ElapsedMilliseconds}ms)"; } var changeType = options.GetSetting(uSyncConstants.DefaultSettings.OnlyPublishDirty, uSyncConstants.DefaultSettings.OnlyPublishDirty_Default) && !item.IsDirty() ? ChangeType.NoChange : ChangeType.Import; // we say no change back, this stops the core second pass function from saving // this item (which we have just done with DoSaveOrPublish) return(SyncAttempt <IContent> .Succeed(item.Name, item, changeType, message, true, details)); } else { return(SyncAttempt <IContent> .Fail(item.Name, item, ChangeType.ImportFail, saveAttempt.Result, saveAttempt.Exception)); } }
/// <summary> /// serialize all the properties for the item /// </summary> protected virtual XElement SerializeProperties(TObject item, SyncSerializerOptions options) { var cultures = options.GetCultures(); var segments = options.GetSegments(); var includeDefaults = (cultures.Count == 0 && segments.Count == 0) || options.GetSetting(uSyncConstants.DefaultsKey, false); var node = new XElement("Properties"); foreach (var property in item.Properties .Where(x => !dontSerialize.InvariantContains(x.Alias)) .OrderBy(x => x.Alias)) { var propertyNode = new XElement(property.Alias); // this can cause us false change readings // but we need to preserve the values if they are blank // because we have to be able to set them to blank on deserialization foreach (var value in property.Values) { var valueNode = new XElement("Value"); // valid if there is no culture, or segment and // we are includeing default values var validNode = string.IsNullOrWhiteSpace(value.Culture) && string.IsNullOrWhiteSpace(value.Segment) && includeDefaults; // or b) it is a valid culture/segment. if (!string.IsNullOrWhiteSpace(value.Culture) && cultures.IsValid(value.Culture)) { valueNode.Add(new XAttribute("Culture", value.Culture ?? string.Empty)); validNode = true; } if (!string.IsNullOrWhiteSpace(value.Segment) && segments.IsValid(value.Segment)) { valueNode.Add(new XAttribute("Segment", value.Segment ?? string.Empty)); validNode = true; } if (validNode) { valueNode.Add(new XCData(GetExportValue(GetPropertyValue(value), property.PropertyType, value.Culture, value.Segment))); propertyNode.Add(valueNode); } } if (property.Values == null || property.Values.Count == 0 && includeDefaults) { // add a blank one, for change clarity // we do it like this because then it doesn't get collapsed in the XML serialization var emptyValue = new XElement("Value"); emptyValue.Add(new XCData(string.Empty)); propertyNode.Add(emptyValue); } if (propertyNode.HasElements) { node.Add(propertyNode); } } return(node); }
/// <summary> /// perform the removal of properties and items. /// </summary> public static bool DeleteItems(this SyncSerializerOptions options) => !options.GetSetting <bool>(uSyncConstants.DefaultSettings.NoRemove, uSyncConstants.DefaultSettings.NoRemove_Default);