From ffd4e9507ba5ac174ef892a8d6f61d3187e443f8 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 12 Jan 2015 18:52:30 +1100 Subject: [PATCH] WIP Gets legacy dictionary, language classes to wrap the Core services implementation properly. Obsoletes a bunch of stuff, creates a few more tests, need to test UI now. --- src/Umbraco.Core/Constants-Conventions.cs | 8 + src/Umbraco.Core/Models/DictionaryItem.cs | 4 +- .../Models/DictionaryTranslation.cs | 2 + .../Repositories/DictionaryRepository.cs | 2 +- .../Services/ILocalizationService.cs | 9 + .../Services/LocalizationService.cs | 48 +- .../Services/LocalizationServiceTests.cs | 179 +++++-- .../Cache/DictionaryCacheRefresher.cs | 8 +- .../Cache/LanguageCacheRefresher.cs | 2 +- src/umbraco.cms/businesslogic/Dictionary.cs | 451 ++++++++++-------- .../businesslogic/language/Item.cs | 225 +++++---- 11 files changed, 603 insertions(+), 335 deletions(-) diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index bdbd46e0cb..bcf012e42f 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -11,6 +11,14 @@ namespace Umbraco.Core /// public static class Conventions { + public static class Localization + { + /// + /// The root id for all top level dictionary items + /// + public const string DictionaryItemRootId = "41c7638d-f529-4bff-853e-59a0c2fb1bde"; + } + public static class DataTypes { public const string ListViewPrefix = "List View - "; diff --git a/src/Umbraco.Core/Models/DictionaryItem.cs b/src/Umbraco.Core/Models/DictionaryItem.cs index 6c2ee47714..de7fd3414f 100644 --- a/src/Umbraco.Core/Models/DictionaryItem.cs +++ b/src/Umbraco.Core/Models/DictionaryItem.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Models private IEnumerable _translations; public DictionaryItem(string itemKey) - : this(new Guid("41c7638d-f529-4bff-853e-59a0c2fb1bde"), itemKey) + : this(new Guid(Constants.Conventions.Localization.DictionaryItemRootId), itemKey) {} public DictionaryItem(Guid parentId, string itemKey) @@ -95,7 +95,7 @@ namespace Umbraco.Core.Models //If ParentId is not set we should default to the root parent id if(ParentId == Guid.Empty) - _parentId = new Guid("41c7638d-f529-4bff-853e-59a0c2fb1bde"); + _parentId = new Guid(Constants.Conventions.Localization.DictionaryItemRootId); } } diff --git a/src/Umbraco.Core/Models/DictionaryTranslation.cs b/src/Umbraco.Core/Models/DictionaryTranslation.cs index f1c1af0eb7..782ff14413 100644 --- a/src/Umbraco.Core/Models/DictionaryTranslation.cs +++ b/src/Umbraco.Core/Models/DictionaryTranslation.cs @@ -18,12 +18,14 @@ namespace Umbraco.Core.Models public DictionaryTranslation(ILanguage language, string value) { + if (language == null) throw new ArgumentNullException("language"); _language = language; _value = value; } public DictionaryTranslation(ILanguage language, string value, Guid uniqueId) { + if (language == null) throw new ArgumentNullException("language"); _language = language; _value = value; Key = uniqueId; diff --git a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs index 85e2c6726e..390982b11c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs @@ -130,7 +130,7 @@ namespace Umbraco.Core.Persistence.Repositories /// protected override Guid NodeObjectTypeId { - get { return new Guid("41c7638d-f529-4bff-853e-59a0c2fb1bde"); } + get { return new Guid(Constants.Conventions.Localization.DictionaryItemRootId); } } #endregion diff --git a/src/Umbraco.Core/Services/ILocalizationService.cs b/src/Umbraco.Core/Services/ILocalizationService.cs index 5c3bc965ec..afac56f6f8 100644 --- a/src/Umbraco.Core/Services/ILocalizationService.cs +++ b/src/Umbraco.Core/Services/ILocalizationService.cs @@ -15,6 +15,15 @@ namespace Umbraco.Core.Services //Add/Set Text (Insert/Update) //Remove Text (in translation) + /// + /// Creates and saves a new dictionary item and assigns a value to all languages if defaultValue is specified. + /// + /// + /// + /// + /// + IDictionaryItem CreateDictionaryItemWithIdentity(string key, Guid? parentId, string defaultValue = null); + /// /// Gets a by its id /// diff --git a/src/Umbraco.Core/Services/LocalizationService.cs b/src/Umbraco.Core/Services/LocalizationService.cs index 98912de3ad..dafbc6191f 100644 --- a/src/Umbraco.Core/Services/LocalizationService.cs +++ b/src/Umbraco.Core/Services/LocalizationService.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Services { private readonly RepositoryFactory _repositoryFactory; private readonly IDatabaseUnitOfWorkProvider _uowProvider; - private static readonly Guid RootParentId = new Guid("41c7638d-f529-4bff-853e-59a0c2fb1bde"); + private static readonly Guid RootParentId = new Guid(Constants.Conventions.Localization.DictionaryItemRootId); [Obsolete("Use the constructors that specify all dependencies instead")] public LocalizationService() @@ -44,6 +44,52 @@ namespace Umbraco.Core.Services _uowProvider = provider; } + /// + /// Creates and saves a new dictionary item and assigns a value to all languages if defaultValue is specified. + /// + /// + /// + /// + /// + public IDictionaryItem CreateDictionaryItemWithIdentity(string key, Guid? parentId, string defaultValue = null) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var repository = _repositoryFactory.CreateDictionaryRepository(uow)) + { + //validate the parent + if (parentId.HasValue && parentId.Value != Guid.Empty) + { + var parent = GetDictionaryItemById(parentId.Value); + if (parent == null) + { + throw new ArgumentException("No parent dictionary item was found with id " + parentId.Value); + } + } + + var item = new DictionaryItem(parentId.HasValue ? parentId.Value : RootParentId, key); + + if (defaultValue.IsNullOrWhiteSpace() == false) + { + var langs = GetAllLanguages(); + var translations = langs.Select(language => new DictionaryTranslation(language, defaultValue)) + .Cast() + .ToList(); + + item.Translations = translations; + } + + if (SavingDictionaryItem.IsRaisedEventCancelled(new SaveEventArgs(item), this)) + return item; + + repository.AddOrUpdate(item); + uow.Commit(); + + SavedDictionaryItem.RaiseEvent(new SaveEventArgs(item), this); + + return item; + } + } + /// /// Gets a by its id /// diff --git a/src/Umbraco.Tests/Services/LocalizationServiceTests.cs b/src/Umbraco.Tests/Services/LocalizationServiceTests.cs index 0d78f56939..32ffb7a305 100644 --- a/src/Umbraco.Tests/Services/LocalizationServiceTests.cs +++ b/src/Umbraco.Tests/Services/LocalizationServiceTests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; @@ -36,7 +37,7 @@ namespace Umbraco.Tests.Services } [Test] - public void LocalizationService_Can_Get_Root_Dictionary_Items() + public void Can_Get_Root_Dictionary_Items() { var rootItems = ServiceContext.LocalizationService.GetRootDictionaryItems(); @@ -45,14 +46,14 @@ namespace Umbraco.Tests.Services } [Test] - public void LocalizationService_Can_Determint_If_DictionaryItem_Exists() + public void Can_Determint_If_DictionaryItem_Exists() { var exists = ServiceContext.LocalizationService.DictionaryItemExists("Parent"); Assert.IsTrue(exists); } [Test] - public void LocalizationService_Can_Get_All_Languages() + public void Can_Get_All_Languages() { var languages = ServiceContext.LocalizationService.GetAllLanguages(); Assert.NotNull(languages); @@ -61,7 +62,7 @@ namespace Umbraco.Tests.Services } [Test] - public void LocalizationService_Can_Get_Dictionary_Item_By_Int_Id() + public void Can_Get_Dictionary_Item_By_Int_Id() { var parentItem = ServiceContext.LocalizationService.GetDictionaryItemById(_parentItemIntId); Assert.NotNull(parentItem); @@ -71,7 +72,7 @@ namespace Umbraco.Tests.Services } [Test] - public void LocalizationService_Can_Get_Dictionary_Item_By_Guid_Id() + public void Can_Get_Dictionary_Item_By_Guid_Id() { var parentItem = ServiceContext.LocalizationService.GetDictionaryItemById(_parentItemGuidId); Assert.NotNull(parentItem); @@ -81,7 +82,7 @@ namespace Umbraco.Tests.Services } [Test] - public void LocalizationService_Can_Get_Dictionary_Item_By_Key() + public void Can_Get_Dictionary_Item_By_Key() { var parentItem = ServiceContext.LocalizationService.GetDictionaryItemByKey("Parent"); Assert.NotNull(parentItem); @@ -91,7 +92,7 @@ namespace Umbraco.Tests.Services } [Test] - public void LocalizationService_Can_Get_Dictionary_Item_Children() + public void Can_Get_Dictionary_Item_Children() { var item = ServiceContext.LocalizationService.GetDictionaryItemChildren(_parentItemGuidId); Assert.NotNull(item); @@ -104,7 +105,7 @@ namespace Umbraco.Tests.Services } [Test] - public void LocalizationService_Can_Get_Language_By_Culture_Code() + public void Can_Get_Language_By_Culture_Code() { var danish = ServiceContext.LocalizationService.GetLanguageByCultureCode("Danish"); var english = ServiceContext.LocalizationService.GetLanguageByCultureCode("English"); @@ -113,7 +114,7 @@ namespace Umbraco.Tests.Services } [Test] - public void LocalizationService_Can_GetLanguageById() + public void Can_GetLanguageById() { var danish = ServiceContext.LocalizationService.GetLanguageById(_danishLangId); var english = ServiceContext.LocalizationService.GetLanguageById(_englishLangId); @@ -122,7 +123,7 @@ namespace Umbraco.Tests.Services } [Test] - public void LocalizationService_Can_GetLanguageByIsoCode() + public void Can_GetLanguageByIsoCode() { var danish = ServiceContext.LocalizationService.GetLanguageByIsoCode("da-DK"); var english = ServiceContext.LocalizationService.GetLanguageByIsoCode("en-GB"); @@ -131,21 +132,21 @@ namespace Umbraco.Tests.Services } [Test] - public void LocalizationService_Does_Not_Fail_When_Language_Doesnt_Exist() + public void Does_Not_Fail_When_Language_Doesnt_Exist() { var language = ServiceContext.LocalizationService.GetLanguageByIsoCode("sv-SE"); Assert.Null(language); } [Test] - public void LocalizationService_Does_Not_Fail_When_DictionaryItem_Doesnt_Exist() + public void Does_Not_Fail_When_DictionaryItem_Doesnt_Exist() { var item = ServiceContext.LocalizationService.GetDictionaryItemByKey("RandomKey"); Assert.Null(item); } [Test] - public void LocalizationService_Can_Delete_Language() + public void Can_Delete_Language() { var norwegian = new Language("nb-NO") { CultureName = "Norwegian" }; ServiceContext.LocalizationService.Save(norwegian, 0); @@ -159,7 +160,87 @@ namespace Umbraco.Tests.Services } [Test] - public void LocalizationService_Can_Delete_DictionaryItem() + public void Can_Create_DictionaryItem_At_Root() + { + var english = ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"); + + var item = (IDictionaryItem)new DictionaryItem("Testing123") + { + Translations = new List + { + new DictionaryTranslation(english, "Hello world") + } + }; + ServiceContext.LocalizationService.Save(item); + + //re-get + item = ServiceContext.LocalizationService.GetDictionaryItemById(item.Id); + + Assert.Greater(item.Id, 0); + Assert.IsTrue(item.HasIdentity); + Assert.AreEqual(new Guid(Constants.Conventions.Localization.DictionaryItemRootId), item.ParentId); + Assert.AreEqual("Testing123", item.ItemKey); + Assert.AreEqual(1, item.Translations.Count()); + } + + [Test] + public void Can_Create_DictionaryItem_At_Root_With_Identity() + { + + var item = ServiceContext.LocalizationService.CreateDictionaryItemWithIdentity( + "Testing12345", null, "Hellooooo"); + + Assert.Greater(item.Id, 0); + Assert.IsTrue(item.HasIdentity); + Assert.AreEqual(new Guid(Constants.Conventions.Localization.DictionaryItemRootId), item.ParentId); + Assert.AreEqual("Testing12345", item.ItemKey); + var allLangs = ServiceContext.LocalizationService.GetAllLanguages(); + Assert.Greater(allLangs.Count(), 0); + foreach (var language in allLangs) + { + Assert.AreEqual("Hellooooo", item.Translations.Single(x => x.Language.CultureName == language.CultureName).Value); + } + + } + + [Test] + public void Can_Add_Translation_To_Existing_Dictionary_Item() + { + var english = ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"); + + var item = (IDictionaryItem)new DictionaryItem("Testing123") + { + Translations = new List + { + new DictionaryTranslation(english, "Hello world") + } + }; + ServiceContext.LocalizationService.Save(item); + + //re-get + item = ServiceContext.LocalizationService.GetDictionaryItemById(item.Id); + + var newTranslations = new List(item.Translations) + { + new DictionaryTranslation( + ServiceContext.LocalizationService.GetLanguageByIsoCode("en-GB"), + "My new value") + }; + item.Translations = newTranslations; + + ServiceContext.LocalizationService.Save(item); + //re-get + item = ServiceContext.LocalizationService.GetDictionaryItemById(item.Id); + + Assert.AreEqual(2, item.Translations.Count()); + foreach (var translation in item.Translations) + { + Assert.AreEqual(translation.Language.CultureName == "en-US" ? "Hello world" : "My new value", translation.Value); + } + } + + [Test] + public void Can_Delete_DictionaryItem() { var item = ServiceContext.LocalizationService.GetDictionaryItemByKey("Child"); Assert.NotNull(item); @@ -171,7 +252,7 @@ namespace Umbraco.Tests.Services } [Test] - public void LocalizationService_Can_Update_Existing_DictionaryItem() + public void Can_Update_Existing_DictionaryItem() { var item = ServiceContext.LocalizationService.GetDictionaryItemByKey("Child"); foreach (var translation in item.Translations) @@ -190,40 +271,6 @@ namespace Umbraco.Tests.Services } } - public override void CreateTestData() - { - var danish = new Language("da-DK") { CultureName = "Danish" }; - var english = new Language("en-GB") { CultureName = "English"}; - ServiceContext.LocalizationService.Save(danish, 0); - ServiceContext.LocalizationService.Save(english, 0); - _danishLangId = danish.Id; - _englishLangId = english.Id; - - var parentItem = new DictionaryItem("Parent") - { - Translations = new List - { - new DictionaryTranslation(english, "ParentValue"), - new DictionaryTranslation(danish, "ForældreVærdi") - } - }; - ServiceContext.LocalizationService.Save(parentItem); - _parentItemGuidId = parentItem.Key; - _parentItemIntId = parentItem.Id; - - var childItem = new DictionaryItem(parentItem.Key, "Child") - { - Translations = new List - { - new DictionaryTranslation(english, "ChildValue"), - new DictionaryTranslation(danish, "BørnVærdi") - } - }; - ServiceContext.LocalizationService.Save(childItem); - _childItemGuidId = childItem.Key; - _childItemIntId = childItem.Id; - } - [Test] public void Find_BaseData_Language() { @@ -284,5 +331,39 @@ namespace Umbraco.Tests.Services Assert.Null(result); } + public override void CreateTestData() + { + var danish = new Language("da-DK") { CultureName = "Danish" }; + var english = new Language("en-GB") { CultureName = "English" }; + ServiceContext.LocalizationService.Save(danish, 0); + ServiceContext.LocalizationService.Save(english, 0); + _danishLangId = danish.Id; + _englishLangId = english.Id; + + var parentItem = new DictionaryItem("Parent") + { + Translations = new List + { + new DictionaryTranslation(english, "ParentValue"), + new DictionaryTranslation(danish, "ForældreVærdi") + } + }; + ServiceContext.LocalizationService.Save(parentItem); + _parentItemGuidId = parentItem.Key; + _parentItemIntId = parentItem.Id; + + var childItem = new DictionaryItem(parentItem.Key, "Child") + { + Translations = new List + { + new DictionaryTranslation(english, "ChildValue"), + new DictionaryTranslation(danish, "BørnVærdi") + } + }; + ServiceContext.LocalizationService.Save(childItem); + _childItemGuidId = childItem.Key; + _childItemIntId = childItem.Id; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Cache/DictionaryCacheRefresher.cs b/src/Umbraco.Web/Cache/DictionaryCacheRefresher.cs index 8ee199c6fa..02c9a3656f 100644 --- a/src/Umbraco.Web/Cache/DictionaryCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/DictionaryCacheRefresher.cs @@ -28,18 +28,18 @@ namespace Umbraco.Web.Cache public override void Refresh(int id) { RuntimeCacheProvider.Current.Clear(typeof(IDictionaryItem)); - global::umbraco.cms.businesslogic.Dictionary.ClearCache(); + //global::umbraco.cms.businesslogic.Dictionary.ClearCache(); //when a dictionary item is updated we must also clear the text cache! - global::umbraco.cms.businesslogic.language.Item.ClearCache(); + //global::umbraco.cms.businesslogic.language.Item.ClearCache(); base.Refresh(id); } public override void Remove(int id) { RuntimeCacheProvider.Current.Clear(typeof(IDictionaryItem)); - global::umbraco.cms.businesslogic.Dictionary.ClearCache(); + //global::umbraco.cms.businesslogic.Dictionary.ClearCache(); //when a dictionary item is removed we must also clear the text cache! - global::umbraco.cms.businesslogic.language.Item.ClearCache(); + //global::umbraco.cms.businesslogic.language.Item.ClearCache(); base.Remove(id); } } diff --git a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs b/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs index fac08fdf5c..33ff1f7237 100644 --- a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs @@ -36,7 +36,7 @@ namespace Umbraco.Web.Cache { RuntimeCacheProvider.Current.Clear(typeof(ILanguage)); //when a language is removed we must also clear the text cache! - global::umbraco.cms.businesslogic.language.Item.ClearCache(); + //global::umbraco.cms.businesslogic.language.Item.ClearCache(); base.Remove(id); } } diff --git a/src/umbraco.cms/businesslogic/Dictionary.cs b/src/umbraco.cms/businesslogic/Dictionary.cs index cd32162fa7..745d38de4a 100644 --- a/src/umbraco.cms/businesslogic/Dictionary.cs +++ b/src/umbraco.cms/businesslogic/Dictionary.cs @@ -1,15 +1,19 @@ using System; using System.Collections; using System.Collections.Concurrent; +using System.ComponentModel; using System.Data; +using System.Globalization; using System.Threading; using System.Xml; using System.Linq; using Umbraco.Core; using umbraco.cms.businesslogic.language; +using Umbraco.Core.Models; using umbraco.DataLayer; using umbraco.BusinessLogic; using System.Runtime.CompilerServices; +using Language = umbraco.cms.businesslogic.language.Language; namespace umbraco.cms.businesslogic { @@ -20,57 +24,58 @@ namespace umbraco.cms.businesslogic /// public class Dictionary { - private static volatile bool _cacheIsEnsured = false; - private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(); - private static readonly ConcurrentDictionary DictionaryItems = new ConcurrentDictionary(); - private static readonly Guid TopLevelParent = new Guid("41c7638d-f529-4bff-853e-59a0c2fb1bde"); + + //private static volatile bool _cacheIsEnsured = false; + //private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(); + //private static readonly ConcurrentDictionary DictionaryItems = new ConcurrentDictionary(); + private static readonly Guid TopLevelParent = new Guid(Constants.Conventions.Localization.DictionaryItemRootId); protected static ISqlHelper SqlHelper { get { return Application.SqlHelper; } } + + ///// + ///// Reads all items from the database and stores in local cache + ///// + //private static void EnsureCache() + //{ + // using (var lck = new UpgradeableReadLock(Locker)) + // { + // if (_cacheIsEnsured) return; - /// - /// Reads all items from the database and stores in local cache - /// - private static void EnsureCache() - { - using (var lck = new UpgradeableReadLock(Locker)) - { - if (_cacheIsEnsured) return; - - lck.UpgradeToWriteLock(); + // lck.UpgradeToWriteLock(); - using (var dr = SqlHelper.ExecuteReader("Select pk, id, [key], parent from cmsDictionary")) - { - while (dr.Read()) - { - //create new dictionaryitem object and put in cache - var item = new DictionaryItem(dr.GetInt("pk"), - dr.GetString("key"), - dr.GetGuid("id"), - dr.GetGuid("parent")); + // using (var dr = SqlHelper.ExecuteReader("Select pk, id, [key], parent from cmsDictionary")) + // { + // while (dr.Read()) + // { + // //create new dictionaryitem object and put in cache + // var item = new DictionaryItem(dr.GetInt("pk"), + // dr.GetString("key"), + // dr.GetGuid("id"), + // dr.GetGuid("parent")); - DictionaryItems.TryAdd(item.key, item); - } - } + // DictionaryItems.TryAdd(item.key, item); + // } + // } - _cacheIsEnsured = true; - } - } + // _cacheIsEnsured = true; + // } + //} - /// - /// Used by the cache refreshers to clear the cache on distributed servers - /// - internal static void ClearCache() - { - using (new WriteLock(Locker)) - { - DictionaryItems.Clear(); - //ensure the flag is reset so that EnsureCache will re-cache everything - _cacheIsEnsured = false; - } - } + ///// + ///// Used by the cache refreshers to clear the cache on distributed servers + ///// + //internal static void ClearCache() + //{ + // using (new WriteLock(Locker)) + // { + // DictionaryItems.Clear(); + // //ensure the flag is reset so that EnsureCache will re-cache everything + // _cacheIsEnsured = false; + // } + //} /// /// Retrieve a list of toplevel DictionaryItems @@ -79,11 +84,15 @@ namespace umbraco.cms.businesslogic { get { - EnsureCache(); - - return DictionaryItems.Values - .Where(x => x.ParentId == TopLevelParent).OrderBy(item => item.key) + return ApplicationContext.Current.Services.LocalizationService.GetRootDictionaryItems() + .Select(x => new DictionaryItem(x)) .ToArray(); + + //EnsureCache(); + + //return DictionaryItems.Values + // .Where(x => x.ParentId == TopLevelParent).OrderBy(item => item.key) + // .ToArray(); } } @@ -93,90 +102,106 @@ namespace umbraco.cms.businesslogic /// public class DictionaryItem { - - private string _key; - - internal Guid UniqueId { get; private set; } - internal Guid ParentId { get; private set; } - - /// - /// Used internally to construct a new item object and store in cache - /// - /// - /// - /// - /// - internal DictionaryItem(int id, string key, Guid uniqueKey, Guid parentId) + public DictionaryItem() { - this.id = id; - this._key = key; - this.UniqueId = uniqueKey; - this.ParentId = parentId; + } + internal DictionaryItem(IDictionaryItem item) + { + _dictionaryItem = item; + //this.id = id; + //this._key = key; + //this.UniqueId = item.Key; + //this.ParentId = item.ParentId; + } + + private readonly IDictionaryItem _dictionaryItem; + private DictionaryItem _parent; + //private string _key; + + + //internal Guid UniqueId { get; private set; } + //internal Guid ParentId { get; private set; } + + ///// + ///// Used internally to construct a new item object and store in cache + ///// + ///// + ///// + ///// + ///// + //internal DictionaryItem(int id, string key, Guid uniqueKey, Guid parentId) + //{ + // this.id = id; + // this._key = key; + // this.UniqueId = uniqueKey; + // this.ParentId = parentId; + //} public DictionaryItem(string key) { - EnsureCache(); + //EnsureCache(); - var item = DictionaryItems.Values.SingleOrDefault(x => x.key == key); + //var item = DictionaryItems.Values.SingleOrDefault(x => x.key == key); + _dictionaryItem = ApplicationContext.Current.Services.LocalizationService.GetDictionaryItemByKey(key); - if (item == null) + if (_dictionaryItem == null) { throw new ArgumentException("No key " + key + " exists in dictionary"); } - this.id = item.id; - this._key = item.key; - this.ParentId = item.ParentId; - this.UniqueId = item.UniqueId; + //this.id = item.Id; + //this._key = item.ItemKey; + //this.ParentId = item.ParentId; + //this.UniqueId = item.Key; } public DictionaryItem(Guid id) { - EnsureCache(); + //EnsureCache(); - var item = DictionaryItems.Values.SingleOrDefault(x => x.UniqueId == id); + //var item = DictionaryItems.Values.SingleOrDefault(x => x.UniqueId == id); + _dictionaryItem = ApplicationContext.Current.Services.LocalizationService.GetDictionaryItemById(id); - if (item == null) + if (_dictionaryItem == null) { - throw new ArgumentException("No unique id " + id.ToString() + " exists in dictionary"); + throw new ArgumentException("No unique id " + id + " exists in dictionary"); } - this.id = item.id; - this._key = item.key; - this.ParentId = item.ParentId; - this.UniqueId = item.UniqueId; + //this.id = item.Id; + //this._key = item.ItemKey; + //this.ParentId = item.ParentId; + //this.UniqueId = item.Key; } public DictionaryItem(int id) { - EnsureCache(); + //EnsureCache(); - var item = DictionaryItems.Values.SingleOrDefault(x => x.id == id); + _dictionaryItem = ApplicationContext.Current.Services.LocalizationService.GetDictionaryItemById(id); - if (item == null) + if (_dictionaryItem == null) { throw new ArgumentException("No id " + id + " exists in dictionary"); } - this.id = item.id; - this._key = item.key; - this.ParentId = item.ParentId; - this.UniqueId = item.UniqueId; + //this.id = item.Id; + //this._key = item.ItemKey; + //this.ParentId = item.ParentId; + //this.UniqueId = item.Key; } - private DictionaryItem _parent; - - /// - /// Returns if the dictionaryItem is the root item. - /// + [Obsolete("This is no longer used and will be removed from the codebase in future versions")] public bool IsTopMostItem() { - EnsureCache(); - return DictionaryItems.Values - .Where(x => x.id == id) - .Select(x => x.ParentId) - .SingleOrDefault() == TopLevelParent; + //EnsureCache(); + + //return DictionaryItems.Values + // .Where(x => x.id == id) + // .Select(x => x.ParentId) + // .SingleOrDefault() == TopLevelParent; + + return _dictionaryItem.ParentId == new Guid(Constants.Conventions.Localization.DictionaryItemRootId); } /// @@ -186,10 +211,12 @@ namespace umbraco.cms.businesslogic { get { - EnsureCache(); + //EnsureCache(); if (_parent == null) { - var p = DictionaryItems.Values.SingleOrDefault(x => x.UniqueId == this.ParentId); + //var p = DictionaryItems.Values.SingleOrDefault(x => x.UniqueId == this.ParentId); + + var p = ApplicationContext.Current.Services.LocalizationService.GetDictionaryItemById(_dictionaryItem.ParentId); if (p == null) { @@ -197,7 +224,7 @@ namespace umbraco.cms.businesslogic } else { - _parent = p; + _parent = new DictionaryItem(p); } } @@ -210,33 +237,41 @@ namespace umbraco.cms.businesslogic /// public int id { - get; - private set; + get { return _dictionaryItem.Id; } } public DictionaryItem[] Children { get { - EnsureCache(); - return DictionaryItems.Values - .Where(x => x.ParentId == this.UniqueId).OrderBy(item => item.key) + return ApplicationContext.Current.Services.LocalizationService.GetDictionaryItemChildren(_dictionaryItem.Key) + .WhereNotNull() + .Select(x => new DictionaryItem(x)) .ToArray(); + + //EnsureCache(); + //return DictionaryItems.Values + // .Where(x => x.ParentId == this.UniqueId).OrderBy(item => item.key) + // .ToArray(); } } public static bool hasKey(string key) { - EnsureCache(); - return DictionaryItems.ContainsKey(key); + return ApplicationContext.Current.Services.LocalizationService.DictionaryItemExists(key); + + //EnsureCache(); + //return DictionaryItems.ContainsKey(key); } public bool hasChildren { get { - EnsureCache(); - return DictionaryItems.Values.Any(x => x.ParentId == UniqueId); + //EnsureCache(); + //return DictionaryItems.Values.Any(x => x.ParentId == UniqueId); + + return Children.Any(); } } @@ -245,37 +280,38 @@ namespace umbraco.cms.businesslogic /// public string key { - get { return _key; } + get { return _dictionaryItem.ItemKey; } set { - if (!hasKey(value)) + if (hasKey(value) == false) { - lock (Locker) - { - SqlHelper.ExecuteNonQuery("Update cmsDictionary set [key] = @key WHERE pk = @Id", SqlHelper.CreateParameter("@key", value), - SqlHelper.CreateParameter("@Id", id)); - - using (IRecordsReader dr = - SqlHelper.ExecuteReader("Select pk, id, [key], parent from cmsDictionary where id=@id", - SqlHelper.CreateParameter("@id", this.UniqueId))) - { - if (dr.Read()) - { - //create new dictionaryitem object and put in cache - var item = new DictionaryItem(dr.GetInt("pk"), - dr.GetString("key"), - dr.GetGuid("id"), - dr.GetGuid("parent")); - } - else - { - throw new DataException("Could not load updated created dictionary item with id " + id); - } - } + //lock (Locker) + //{ + // SqlHelper.ExecuteNonQuery("Update cmsDictionary set [key] = @key WHERE pk = @Id", SqlHelper.CreateParameter("@key", value), + // SqlHelper.CreateParameter("@Id", id)); - //finally update this objects value - this._key = value; - } + // using (IRecordsReader dr = + // SqlHelper.ExecuteReader("Select pk, id, [key], parent from cmsDictionary where id=@id", + // SqlHelper.CreateParameter("@id", this.UniqueId))) + // { + // if (dr.Read()) + // { + // //create new dictionaryitem object and put in cache + // var item = new DictionaryItem(dr.GetInt("pk"), + // dr.GetString("key"), + // dr.GetGuid("id"), + // dr.GetGuid("parent")); + // } + // else + // { + // throw new DataException("Could not load updated created dictionary item with id " + id); + // } + // } + + // //finally update this objects value + // this._key = value; + //} + _dictionaryItem.ItemKey = value; } else throw new ArgumentException("New value of key already exists (is key)"); @@ -287,43 +323,56 @@ namespace umbraco.cms.businesslogic if (languageId == 0) return Value(); - if (Item.hasText(UniqueId, languageId)) - return Item.Text(UniqueId, languageId); + var translation = _dictionaryItem.Translations.FirstOrDefault(x => x.Language.Id == languageId); + return translation == null ? string.Empty : translation.Value; - return ""; + //if (Item.hasText(_dictionaryItem.Key, languageId)) + // return Item.Text(_dictionaryItem.Key, languageId); + + //return ""; } public void setValue(int languageId, string value) { - if (Item.hasText(UniqueId, languageId)) - Item.setText(languageId, UniqueId, value); - else - Item.addText(languageId, UniqueId, value); + foreach (var translation in _dictionaryItem.Translations.Where(x => x.Language.Id == languageId)) + { + translation.Value = value; + } + //if (Item.hasText(_dictionaryItem.Key, languageId)) + // Item.setText(languageId, _dictionaryItem.Key, value); + //else + // Item.addText(languageId, _dictionaryItem.Key, value); // Calling Save method triggers the Saving event + Save(); } + /// + /// Returns the default value based on the default language for this item + /// + /// public string Value() { - if (Item.hasText(UniqueId, 1)) - { - return Item.Text(UniqueId, 1); - } + var defaultTranslation = _dictionaryItem.Translations.FirstOrDefault(x => x.Language.Id == 1); + return defaultTranslation == null ? string.Empty : defaultTranslation.Value; - return string.Empty; + //if (Item.hasText(_dictionaryItem.Key, 1)) + //{ + // return Item.Text(_dictionaryItem.Key, 1); + //} + + //return string.Empty; } - /// - /// This sets the value for the placeholder language (id = 0), not for a language with an ID - /// - /// + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("This is not used and should never be used, it will be removed from the codebase in future versions")] public void setValue(string value) { - if (Item.hasText(UniqueId, 0)) - Item.setText(0, UniqueId, value); + if (Item.hasText(_dictionaryItem.Key, 0)) + Item.setText(0, _dictionaryItem.Key, value); else - Item.addText(0, UniqueId, value); + Item.addText(0, _dictionaryItem.Key, value); // Calling Save method triggers the Saving event Save(); @@ -331,11 +380,11 @@ namespace umbraco.cms.businesslogic public static int addKey(string key, string defaultValue, string parentKey) { - EnsureCache(); + //EnsureCache(); if (hasKey(parentKey)) { - int retval = createKey(key, new DictionaryItem(parentKey).UniqueId, defaultValue); + int retval = CreateKey(key, new DictionaryItem(parentKey)._dictionaryItem.Key, defaultValue); return retval; } else @@ -344,8 +393,9 @@ namespace umbraco.cms.businesslogic public static int addKey(string key, string defaultValue) { - EnsureCache(); - int retval = createKey(key, TopLevelParent, defaultValue); + //EnsureCache(); + + int retval = CreateKey(key, TopLevelParent, defaultValue); return retval; } @@ -353,15 +403,17 @@ namespace umbraco.cms.businesslogic { OnDeleting(EventArgs.Empty); - // delete recursive - foreach (DictionaryItem dd in Children) - dd.delete(); + ApplicationContext.Current.Services.LocalizationService.Delete(_dictionaryItem); - // remove all language values from key - Item.removeText(UniqueId); + //// delete recursive + //foreach (DictionaryItem dd in Children) + // dd.delete(); - // remove key from database - SqlHelper.ExecuteNonQuery("delete from cmsDictionary where [key] = @key", SqlHelper.CreateParameter("@key", key)); + //// remove all language values from key + //Item.removeText(_dictionaryItem.Key); + + //// remove key from database + //SqlHelper.ExecuteNonQuery("delete from cmsDictionary where [key] = @key", SqlHelper.CreateParameter("@key", key)); OnDeleted(EventArgs.Empty); } @@ -372,10 +424,12 @@ namespace umbraco.cms.businesslogic public void Save() { OnSaving(EventArgs.Empty); + + ApplicationContext.Current.Services.LocalizationService.Save(_dictionaryItem); } - public System.Xml.XmlNode ToXml(XmlDocument xd) + public XmlNode ToXml(XmlDocument xd) { XmlNode dictionaryItem = xd.CreateElement("DictionaryItem"); @@ -383,7 +437,7 @@ namespace umbraco.cms.businesslogic foreach (Language lang in Language.GetAllAsList()) { XmlNode itemValue = xmlHelper.addCDataNode(xd, "Value", this.Value(lang.id)); - itemValue.Attributes.Append(xmlHelper.addAttribute(xd, "LanguageId", lang.id.ToString())); + itemValue.Attributes.Append(xmlHelper.addAttribute(xd, "LanguageId", lang.id.ToString(CultureInfo.InvariantCulture))); itemValue.Attributes.Append(xmlHelper.addAttribute(xd, "LanguageCultureAlias", lang.CultureAlias)); dictionaryItem.AppendChild(itemValue); } @@ -456,40 +510,43 @@ namespace umbraco.cms.businesslogic return null; } - [MethodImpl(MethodImplOptions.Synchronized)] - private static int createKey(string key, Guid parentId, string defaultValue) + private static int CreateKey(string key, Guid parentId, string defaultValue) { if (!hasKey(key)) { - Guid newId = Guid.NewGuid(); - SqlHelper.ExecuteNonQuery("Insert into cmsDictionary (id,parent,[key]) values (@id, @parentId, @dictionaryKey)", - SqlHelper.CreateParameter("@id", newId), - SqlHelper.CreateParameter("@parentId", parentId), - SqlHelper.CreateParameter("@dictionaryKey", key)); + var item = ApplicationContext.Current.Services.LocalizationService.CreateDictionaryItemWithIdentity( + key, parentId, defaultValue); - using (IRecordsReader dr = - SqlHelper.ExecuteReader("Select pk, id, [key], parent from cmsDictionary where id=@id", - SqlHelper.CreateParameter("@id", newId))) - { - if (dr.Read()) - { - //create new dictionaryitem object and put in cache - var item = new DictionaryItem(dr.GetInt("pk"), - dr.GetString("key"), - dr.GetGuid("id"), - dr.GetGuid("parent")); - - item.setValue(defaultValue); + return item.Id; - item.OnNew(EventArgs.Empty); + //SqlHelper.ExecuteNonQuery("Insert into cmsDictionary (id,parent,[key]) values (@id, @parentId, @dictionaryKey)", + // SqlHelper.CreateParameter("@id", newId), + // SqlHelper.CreateParameter("@parentId", parentId), + // SqlHelper.CreateParameter("@dictionaryKey", key)); - return item.id; - } - else - { - throw new DataException("Could not load newly created dictionary item with id " + newId.ToString()); - } - } + //using (IRecordsReader dr = + // SqlHelper.ExecuteReader("Select pk, id, [key], parent from cmsDictionary where id=@id", + // SqlHelper.CreateParameter("@id", newId))) + //{ + // if (dr.Read()) + // { + // //create new dictionaryitem object and put in cache + // var item = new DictionaryItem(dr.GetInt("pk"), + // dr.GetString("key"), + // dr.GetGuid("id"), + // dr.GetGuid("parent")); + + // item.setValue(defaultValue); + + // item.OnNew(EventArgs.Empty); + + // return item.id; + // } + // else + // { + // throw new DataException("Could not load newly created dictionary item with id " + newId.ToString()); + // } + //} } else @@ -540,16 +597,16 @@ namespace umbraco.cms.businesslogic return text; var lang = Language.GetByCultureCode(Thread.CurrentThread.CurrentCulture.Name); - + if (lang == null) return "[" + text + "]"; - if (DictionaryItem.hasKey(text.Substring(1, text.Length - 1)) == false) + if (DictionaryItem.hasKey(text.Substring(1, text.Length - 1)) == false) return "[" + text + "]"; var di = new DictionaryItem(text.Substring(1, text.Length - 1)); return di.Value(lang.id); } - + } } \ No newline at end of file diff --git a/src/umbraco.cms/businesslogic/language/Item.cs b/src/umbraco.cms/businesslogic/language/Item.cs index 6d5146b0a3..3ea16bdfb3 100644 --- a/src/umbraco.cms/businesslogic/language/Item.cs +++ b/src/umbraco.cms/businesslogic/language/Item.cs @@ -1,10 +1,12 @@ using System; using System.Collections; using System.Collections.Concurrent; +using System.ComponentModel; using System.Data; using System.Linq; using Umbraco.Core; +using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; using umbraco.DataLayer; @@ -14,19 +16,13 @@ using Umbraco.Core.Models.Rdbms; namespace umbraco.cms.businesslogic.language { - /// - /// THIS CLASS IS NOT INTENDED TO BE USED DIRECTLY IN YOUR CODE, USE THE umbraco.cms.businesslogic.Dictionary class instead - /// - /// - /// This class is used by the DictionaryItem, all caching is handled in the DictionaryItem.Save() method which will ensure that - /// cache is invalidated if anything is changed. - /// - [Obsolete("THIS CLASS IS NOT INTENDED TO BE USED DIRECTLY IN YOUR CODE, USE THE umbraco.cms.businesslogic.Dictionary class instead")] + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("This class is no longer used, nor should it ever be used, it will be removed from the codebase in future versions")] public class Item { - private static readonly ConcurrentDictionary> Items = new ConcurrentDictionary>(); - private static volatile bool _isInitialize; - private static readonly object Locker = new object(); + //private static readonly ConcurrentDictionary> Items = new ConcurrentDictionary>(); + //private static volatile bool _isInitialize; + //private static readonly object Locker = new object(); /// /// Gets the SQL helper. @@ -38,54 +34,54 @@ namespace umbraco.cms.businesslogic.language get { return Application.SqlHelper; } } - /// - /// Populates the global hash table with the data from the database. - /// - private static void EnsureCache() - { - if (!_isInitialize) - { - lock (Locker) - { - //double check - if (!_isInitialize) - { + ///// + ///// Populates the global hash table with the data from the database. + ///// + //private static void EnsureCache() + //{ + // if (!_isInitialize) + // { + // lock (Locker) + // { + // //double check + // if (!_isInitialize) + // { - var dtos = ApplicationContext.Current.DatabaseContext.Database.Fetch("ORDER BY UniqueId"); - foreach (var dto in dtos) - { - var languageId = dto.LanguageId; - var uniqueId = dto.UniqueId; - var text = dto.Value; + // var dtos = ApplicationContext.Current.DatabaseContext.Database.Fetch("ORDER BY UniqueId"); + // foreach (var dto in dtos) + // { + // var languageId = dto.LanguageId; + // var uniqueId = dto.UniqueId; + // var text = dto.Value; - Items.AddOrUpdate(uniqueId, guid => - { - var languagevalues = new Dictionary { { languageId, text } }; - return languagevalues; - }, (guid, dictionary) => - { - // add/update the text for the id - dictionary[languageId] = text; - return dictionary; - }); - } + // Items.AddOrUpdate(uniqueId, guid => + // { + // var languagevalues = new Dictionary { { languageId, text } }; + // return languagevalues; + // }, (guid, dictionary) => + // { + // // add/update the text for the id + // dictionary[languageId] = text; + // return dictionary; + // }); + // } - _isInitialize = true; - } - } + // _isInitialize = true; + // } + // } - } - } + // } + //} - /// - /// Clears the cache, this is used for cache refreshers to ensure that the cache is up to date across all servers - /// - internal static void ClearCache() - { - Items.Clear(); - //reset the flag so that we re-lookup the cache - _isInitialize = false; - } + ///// + ///// Clears the cache, this is used for cache refreshers to ensure that the cache is up to date across all servers + ///// + //internal static void ClearCache() + //{ + // Items.Clear(); + // //reset the flag so that we re-lookup the cache + // _isInitialize = false; + //} /// /// Retrieves the value of a languagetranslated item given the key @@ -95,13 +91,24 @@ namespace umbraco.cms.businesslogic.language /// The language translated text public static string Text(Guid key, int languageId) { - EnsureCache(); + //EnsureCache(); - Dictionary val; - if (Items.TryGetValue(key, out val)) + //Dictionary val; + //if (Items.TryGetValue(key, out val)) + //{ + // return val[languageId]; + //} + + var item = ApplicationContext.Current.Services.LocalizationService.GetDictionaryItemById(key); + if (item != null) { - return val[languageId]; - } + var translation = item.Translations.FirstOrDefault(x => x.Language.Id == languageId); + if (translation != null) + { + return translation.Value; + } + } + throw new ArgumentException("Key being requested does not exist"); } @@ -113,14 +120,23 @@ namespace umbraco.cms.businesslogic.language /// returns True if there is a value associated to the unique identifier with the specified language public static bool hasText(Guid key, int languageId) { - EnsureCache(); + //EnsureCache(); - Dictionary val; - if (Items.TryGetValue(key, out val)) + //Dictionary val; + //if (Items.TryGetValue(key, out val)) + //{ + // return val.ContainsKey(languageId); + //} + //return false; + + try { - return val.ContainsKey(languageId); + return Text(key, languageId).IsNullOrWhiteSpace() == false; + } + catch (ArgumentException) + { + return false; } - return false; } /// @@ -133,12 +149,31 @@ namespace umbraco.cms.businesslogic.language public static void setText(int languageId, Guid key, string value) { - if (!hasText(key, languageId)) throw new ArgumentException("Key does not exist"); + var lang = ApplicationContext.Current.Services.LocalizationService.GetLanguageById(languageId); + if (lang == null) return; + + var item = ApplicationContext.Current.Services.LocalizationService.GetDictionaryItemById(key); + if (item != null) + { + var translation = item.Translations.FirstOrDefault(x => x.Language.Id == languageId); + if (translation == null) + { + throw new ArgumentException("Key does not exist"); + } + var newTranslations = new List(item.Translations) + { + new DictionaryTranslation(lang, value) + }; + item.Translations = newTranslations; + ApplicationContext.Current.Services.LocalizationService.Save(item); + } + + //if (!hasText(key, languageId)) throw new ArgumentException("Key does not exist"); - ApplicationContext.Current.DatabaseContext.Database.Update( - string.Format("set {0} = @value where LanguageId = @languageId And UniqueId = @key", - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("value")), - new {value = value, languageId = languageId, key = key}); + //ApplicationContext.Current.DatabaseContext.Database.Update( + // string.Format("set {0} = @value where LanguageId = @languageId And UniqueId = @key", + // SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("value")), + // new {value = value, languageId = languageId, key = key}); } /// @@ -150,14 +185,33 @@ namespace umbraco.cms.businesslogic.language /// public static void addText(int languageId, Guid key, string value) { - if (hasText(key, languageId)) throw new ArgumentException("Key being add'ed already exists"); + var lang = ApplicationContext.Current.Services.LocalizationService.GetLanguageById(languageId); + if (lang == null) return; - ApplicationContext.Current.DatabaseContext.Database.Insert(new LanguageTextDto - { - LanguageId = languageId, - Value = value, - UniqueId = key - }); + var item = ApplicationContext.Current.Services.LocalizationService.GetDictionaryItemById(key); + if (item != null) + { + var translation = item.Translations.FirstOrDefault(x => x.Language.Id == languageId); + if (translation != null) + { + throw new ArgumentException("Key being add'ed already exists"); + } + var newTranslations = new List(item.Translations) + { + new DictionaryTranslation(lang, value) + }; + item.Translations = newTranslations; + ApplicationContext.Current.Services.LocalizationService.Save(item); + } + + //if (hasText(key, languageId)) throw new ArgumentException("Key being add'ed already exists"); + + //ApplicationContext.Current.DatabaseContext.Database.Insert(new LanguageTextDto + // { + // LanguageId = languageId, + // Value = value, + // UniqueId = key + // }); } /// @@ -166,8 +220,13 @@ namespace umbraco.cms.businesslogic.language /// Unique identifier public static void removeText(Guid key) { - // remove from database - ApplicationContext.Current.DatabaseContext.Database.Delete("where UniqueId = @UniqueId", new { UniqueId = key }); + //// remove from database + //ApplicationContext.Current.DatabaseContext.Database.Delete("where UniqueId = @UniqueId", new { UniqueId = key }); + var found = ApplicationContext.Current.Services.LocalizationService.GetDictionaryItemById(key); + if (found != null) + { + ApplicationContext.Current.Services.LocalizationService.Delete(found); + } } /// @@ -178,8 +237,14 @@ namespace umbraco.cms.businesslogic.language [Obsolete("This is no longer used and will be removed from the codebase in future versions")] public static void RemoveByLanguage(int languageId) { - // remove from database - ApplicationContext.Current.DatabaseContext.Database.Delete("where languageId = @languageId", new { languageId = languageId }); + var lang = ApplicationContext.Current.Services.LocalizationService.GetLanguageById(languageId); + if (lang != null) + { + ApplicationContext.Current.Services.LocalizationService.Delete(lang); + } + + //// remove from database + //ApplicationContext.Current.DatabaseContext.Database.Delete("where languageId = @languageId", new { languageId = languageId }); } } } \ No newline at end of file