diff --git a/uSync.BackOffice/SyncHandlers/Handlers/LanguageHandler.cs b/uSync.BackOffice/SyncHandlers/Handlers/LanguageHandler.cs index 87126e5f..2e0885a9 100644 --- a/uSync.BackOffice/SyncHandlers/Handlers/LanguageHandler.cs +++ b/uSync.BackOffice/SyncHandlers/Handlers/LanguageHandler.cs @@ -24,208 +24,218 @@ namespace uSync.BackOffice.SyncHandlers.Handlers { - /// - /// Handler to mange language settings in uSync - /// - [SyncHandler(uSyncConstants.Handlers.LanguageHandler, "Languages", "Languages", uSyncConstants.Priorites.Languages, - Icon = "icon-globe", EntityType = UdiEntityType.Language, IsTwoPass = true)] - public class LanguageHandler : SyncHandlerBase, ISyncHandler, - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler> - { - private readonly ILocalizationService localizationService; - - /// - public LanguageHandler( - ILogger logger, - IEntityService entityService, - ILocalizationService localizationService, - AppCaches appCaches, - IShortStringHelper shortStringHelper, - SyncFileService syncFileService, - uSyncEventService mutexService, - uSyncConfigService uSyncConfig, - ISyncItemFactory syncItemFactory) - : base(logger, entityService, appCaches, shortStringHelper, syncFileService, mutexService, uSyncConfig, syncItemFactory) - { - this.localizationService = localizationService; - } - - /// - // language guids are not consistant (at least in alpha) - // so we don't save by Guid we save by ISO name everytime. - protected override string GetPath(string folder, ILanguage item, bool GuidNames, bool isFlat) - { - return Path.Combine(folder, $"{this.GetItemPath(item, GuidNames, isFlat)}.{this.uSyncConfig.Settings.DefaultExtension}"); - } - - /// - protected override string GetItemPath(ILanguage item, bool useGuid, bool isFlat) - => item.IsoCode.ToSafeFileName(shortStringHelper); - - - /// - /// ensure we import the 'default' language first, so we don't get errors doing it. - /// - protected override IEnumerable GetImportFiles(string folder) - { - var files = base.GetImportFiles(folder); - - try - { - Dictionary ordered = new Dictionary(); - foreach (var file in files) - { - var node = XElement.Load(file); - var order = (node.Element("IsDefault").ValueOrDefault(false) ? "0" : "1") + Path.GetFileName(file); - ordered[file] = order; - } - - return ordered.OrderBy(x => x.Value).Select(x => x.Key).ToList(); - } - catch - { - return files; - } - - } - - /// - protected override IEnumerable GetChildItems(int parent) - { - if (parent == -1) - return localizationService.GetAllLanguages(); - - return Enumerable.Empty(); - } - - /// - protected override string GetItemName(ILanguage item) => item.IsoCode; - - /// - protected override void CleanUp(ILanguage item, string newFile, string folder) - { - base.CleanUp(item, newFile, folder); - - // for languages we also clean up by id. - // this happens when the language changes . - var physicalFile = syncFileService.GetAbsPath(newFile); - var installedLanguages = localizationService.GetAllLanguages() - .Select(x => x.IsoCode).ToList(); - - var files = syncFileService.GetFiles(folder, $"*.{this.uSyncConfig.Settings.DefaultExtension}"); - - foreach (string file in files) - { - var node = syncFileService.LoadXElement(file); - var IsoCode = node.Element("IsoCode").ValueOrDefault(string.Empty); - - if (!String.IsNullOrWhiteSpace(IsoCode)) - { - if (!file.InvariantEquals(physicalFile)) - { - // not the file we just saved, but matching IsoCode, we remove it. - if (node.Element("IsoCode").ValueOrDefault(string.Empty) == item.IsoCode) - { - logger.LogDebug("Found Matching Lang File, cleaning"); - var attempt = serializer.SerializeEmpty(item, SyncActionType.Rename, node.GetAlias()); - if (attempt.Success) - { - syncFileService.SaveXElement(attempt.Item, file); - } - } - } - - if (!installedLanguages.InvariantContains(IsoCode)) - { - // language is no longer installed, make the file empty. - logger.LogDebug("Language in file is not on the site, cleaning"); - var attempt = serializer.SerializeEmpty(item, SyncActionType.Delete, node.GetAlias()); - if (attempt.Success) - { - syncFileService.SaveXElement(attempt.Item, file); - } - } - } - } - } - - private static ConcurrentDictionary newLanguages = new ConcurrentDictionary(); - - /// - public override void Handle(SavingNotification notification) - { - if (_mutexService.IsPaused) return; - - if (ShouldBlockRootChanges(notification.SavedEntities)) - { - notification.Cancel = true; - notification.Messages.Add(GetCancelMessageForRoots()); - return; - } - - foreach (var item in notification.SavedEntities) - { - // - if (item.Id == 0) - { - newLanguages[item.IsoCode] = item.CultureName; - // is new, we want to set this as a flag, so we don't do the full content save.n - // newLanguages.Add(item.IsoCode); - } - } - } - - /// - public override void Handle(SavedNotification notification) - { - if (_mutexService.IsPaused) return; - - foreach (var item in notification.SavedEntities) - { - bool newItem = false; - if (newLanguages.Count > 0 && newLanguages.ContainsKey(item.IsoCode)) - { - newItem = true; - newLanguages.TryRemove(item.IsoCode, out string name); - } - - var targetFolders = GetDefaultHandlerFolders(); - - if (item.WasPropertyDirty("IsDefault")) - { - // changing, this change doesn't trigger a save of the other languages. - // so we need to save all language files. - this.ExportAll(targetFolders, DefaultConfig, null); - } - - - var attempts = Export(item,targetFolders, DefaultConfig); - - if (!newItem && item.WasPropertyDirty(nameof(ILanguage.IsoCode))) - { - // The language code changed, this can mean we need to do a full content export. - // + we should export the languages again! - uSyncTriggers.TriggerExport(targetFolders, new List() { - UdiEntityType.Document, UdiEntityType.Language }, null); - } - - // we always clean up languages, because of the way they are stored. - foreach (var attempt in attempts.Where(x => x.Success)) - { - this.CleanUp(item, attempt.FileName, targetFolders.Last()); - } - - } - } - - /// - /// we don't support language deletion (because the keys are unstable) - /// - protected override IEnumerable DeleteMissingItems(int parentId, IEnumerable keys, bool reportOnly) - => Enumerable.Empty(); - - } + /// + /// Handler to mange language settings in uSync + /// + [SyncHandler(uSyncConstants.Handlers.LanguageHandler, "Languages", "Languages", uSyncConstants.Priorites.Languages, + Icon = "icon-globe", EntityType = UdiEntityType.Language, IsTwoPass = true)] + public class LanguageHandler : SyncHandlerBase, ISyncHandler, + INotificationHandler>, + INotificationHandler>, + INotificationHandler>, + INotificationHandler> + { + private readonly ILocalizationService localizationService; + + /// + public LanguageHandler( + ILogger logger, + IEntityService entityService, + ILocalizationService localizationService, + AppCaches appCaches, + IShortStringHelper shortStringHelper, + SyncFileService syncFileService, + uSyncEventService mutexService, + uSyncConfigService uSyncConfig, + ISyncItemFactory syncItemFactory) + : base(logger, entityService, appCaches, shortStringHelper, syncFileService, mutexService, uSyncConfig, syncItemFactory) + { + this.localizationService = localizationService; + } + + /// + // language guids are not consistant (at least in alpha) + // so we don't save by Guid we save by ISO name everytime. + protected override string GetPath(string folder, ILanguage item, bool GuidNames, bool isFlat) + { + return Path.Combine(folder, $"{this.GetItemPath(item, GuidNames, isFlat)}.{this.uSyncConfig.Settings.DefaultExtension}"); + } + + /// + protected override string GetItemPath(ILanguage item, bool useGuid, bool isFlat) + => item.IsoCode.ToSafeFileName(shortStringHelper); + + /// + /// order the merged items, making sure the default language is first. + /// + protected override IReadOnlyList GetMergedItems(string[] folders) + => base.GetMergedItems(folders) + .OrderBy(x => x.Node.Element("IsDefault").ValueOrDefault(false) ? 0 : 1) + .ToList(); + + /// + /// ensure we import the 'default' language first, so we don't get errors doing it. + /// + /// + /// prost v13.1 this method isn't used to determain the order for all options. + /// + protected override IEnumerable GetImportFiles(string folder) + { + var files = base.GetImportFiles(folder); + + try + { + Dictionary ordered = new Dictionary(); + foreach (var file in files) + { + var node = XElement.Load(file); + var order = (node.Element("IsDefault").ValueOrDefault(false) ? "0" : "1") + Path.GetFileName(file); + ordered[file] = order; + } + + return ordered.OrderBy(x => x.Value).Select(x => x.Key).ToList(); + } + catch + { + return files; + } + + } + + /// + protected override IEnumerable GetChildItems(int parent) + { + if (parent == -1) + return localizationService.GetAllLanguages(); + + return Enumerable.Empty(); + } + + /// + protected override string GetItemName(ILanguage item) => item.IsoCode; + + /// + protected override void CleanUp(ILanguage item, string newFile, string folder) + { + base.CleanUp(item, newFile, folder); + + // for languages we also clean up by id. + // this happens when the language changes . + var physicalFile = syncFileService.GetAbsPath(newFile); + var installedLanguages = localizationService.GetAllLanguages() + .Select(x => x.IsoCode).ToList(); + + var files = syncFileService.GetFiles(folder, $"*.{this.uSyncConfig.Settings.DefaultExtension}"); + + foreach (string file in files) + { + var node = syncFileService.LoadXElement(file); + var IsoCode = node.Element("IsoCode").ValueOrDefault(string.Empty); + + if (!String.IsNullOrWhiteSpace(IsoCode)) + { + if (!file.InvariantEquals(physicalFile)) + { + // not the file we just saved, but matching IsoCode, we remove it. + if (node.Element("IsoCode").ValueOrDefault(string.Empty) == item.IsoCode) + { + logger.LogDebug("Found Matching Lang File, cleaning"); + var attempt = serializer.SerializeEmpty(item, SyncActionType.Rename, node.GetAlias()); + if (attempt.Success) + { + syncFileService.SaveXElement(attempt.Item, file); + } + } + } + + if (!installedLanguages.InvariantContains(IsoCode)) + { + // language is no longer installed, make the file empty. + logger.LogDebug("Language in file is not on the site, cleaning"); + var attempt = serializer.SerializeEmpty(item, SyncActionType.Delete, node.GetAlias()); + if (attempt.Success) + { + syncFileService.SaveXElement(attempt.Item, file); + } + } + } + } + } + + private static ConcurrentDictionary newLanguages = new ConcurrentDictionary(); + + /// + public override void Handle(SavingNotification notification) + { + if (_mutexService.IsPaused) return; + + if (ShouldBlockRootChanges(notification.SavedEntities)) + { + notification.Cancel = true; + notification.Messages.Add(GetCancelMessageForRoots()); + return; + } + + foreach (var item in notification.SavedEntities) + { + // + if (item.Id == 0) + { + newLanguages[item.IsoCode] = item.CultureName; + // is new, we want to set this as a flag, so we don't do the full content save.n + // newLanguages.Add(item.IsoCode); + } + } + } + + /// + public override void Handle(SavedNotification notification) + { + if (_mutexService.IsPaused) return; + + foreach (var item in notification.SavedEntities) + { + bool newItem = false; + if (newLanguages.Count > 0 && newLanguages.ContainsKey(item.IsoCode)) + { + newItem = true; + newLanguages.TryRemove(item.IsoCode, out string name); + } + + var targetFolders = GetDefaultHandlerFolders(); + + if (item.WasPropertyDirty("IsDefault")) + { + // changing, this change doesn't trigger a save of the other languages. + // so we need to save all language files. + this.ExportAll(targetFolders, DefaultConfig, null); + } + + + var attempts = Export(item, targetFolders, DefaultConfig); + + if (!newItem && item.WasPropertyDirty(nameof(ILanguage.IsoCode))) + { + // The language code changed, this can mean we need to do a full content export. + // + we should export the languages again! + uSyncTriggers.TriggerExport(targetFolders, new List() { + UdiEntityType.Document, UdiEntityType.Language }, null); + } + + // we always clean up languages, because of the way they are stored. + foreach (var attempt in attempts.Where(x => x.Success)) + { + this.CleanUp(item, attempt.FileName, targetFolders.Last()); + } + + } + } + + /// + /// we don't support language deletion (because the keys are unstable) + /// + protected override IEnumerable DeleteMissingItems(int parentId, IEnumerable keys, bool reportOnly) + => Enumerable.Empty(); + + } }