From f1c0aa243989cc24686463d528e0e78b0eab3ed7 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 18 Sep 2013 15:21:35 +1000 Subject: [PATCH 01/11] adds a comment --- src/Umbraco.Tests/Services/ContentServiceTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 089fc25ae2..3ea7bdef50 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -842,6 +842,7 @@ namespace Umbraco.Tests.Services Assert.That(sut.GetValue("multilineText"), Is.EqualTo("Multiple lines \n in one box")); Assert.That(sut.GetValue("upload"), Is.EqualTo("/media/1234/koala.jpg")); Assert.That(sut.GetValue("label"), Is.EqualTo("Non-editable label")); + //SD: This is failing because the 'content' call to GetValue always has empty milliseconds Assert.That(sut.GetValue("dateTime"), Is.EqualTo(content.GetValue("dateTime"))); Assert.That(sut.GetValue("colorPicker"), Is.EqualTo("black")); Assert.That(sut.GetValue("folderBrowser"), Is.Empty); From a027c3cb22293c585594a42d8fccdd57cb743340 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 18 Sep 2013 16:08:15 +1000 Subject: [PATCH 02/11] Found out if we don't do ToArray here and if we were caching we'd end up with null values in the cache, do to yield return i think. --- src/Umbraco.Core/Persistence/Repositories/UserRepository.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 0acad10383..f61ae16e2f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -61,7 +61,8 @@ namespace Umbraco.Core.Persistence.Repositories var sql = GetBaseQuery(false); - return ConvertFromDtos(Database.Fetch(new UserSectionRelator().Map, sql)); + return ConvertFromDtos(Database.Fetch(new UserSectionRelator().Map, sql)) + .ToArray(); // important so we don't iterate twice, if we don't do thsi we can end up with null vals in cache if we were caching. } private IEnumerable PerformGetAllOnIds(params int[] ids) From 95df28bf642d3000422a35fed5b1345b29b0e1b2 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 18 Sep 2013 17:44:25 +1000 Subject: [PATCH 03/11] Fixes issue with runtime cache provider when retrieving all items of a certain type, we were sending back an array of nulls if the cache was expired. --- .../Caching/RuntimeCacheProvider.cs | 72 ++++++++++++++----- .../Caching/RuntimeCacheProviderTest.cs | 20 ++++++ 2 files changed, 73 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs index 06a624a771..60e176096f 100644 --- a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs @@ -59,23 +59,41 @@ namespace Umbraco.Core.Persistence.Caching var item = _memoryCache != null ? _memoryCache.Get(key) : HttpRuntime.Cache.Get(key); - return item as IEntity; + var result = item as IEntity; + if (result == null) + { + //ensure the key doesn't exist anymore in the tracker + _keyTracker.Remove(key); + } + return result; } public IEnumerable GetByIds(Type type, List ids) { + var collection = new List(); foreach (var guid in ids) { + var key = GetCompositeId(type, guid); var item = _memoryCache != null - ? _memoryCache.Get(GetCompositeId(type, guid)) - : HttpRuntime.Cache.Get(GetCompositeId(type, guid)); - - yield return item as IEntity; + ? _memoryCache.Get(key) + : HttpRuntime.Cache.Get(key); + var result = item as IEntity; + if (result == null) + { + //ensure the key doesn't exist anymore in the tracker + _keyTracker.Remove(key); + } + else + { + collection.Add(result); + } } + return collection; } public IEnumerable GetAllByType(Type type) { + var collection = new List(); foreach (var key in _keyTracker) { if (key.StartsWith(string.Format("{0}{1}-", CacheItemPrefix, type.Name))) @@ -84,9 +102,19 @@ namespace Umbraco.Core.Persistence.Caching ? _memoryCache.Get(key) : HttpRuntime.Cache.Get(key); - yield return item as IEntity; + var result = item as IEntity; + if (result == null) + { + //ensure the key doesn't exist anymore in the tracker + _keyTracker.Remove(key); + } + else + { + collection.Add(result); + } } } + return collection; } public void Save(Type type, IEntity entity) @@ -159,21 +187,27 @@ namespace Umbraco.Core.Persistence.Caching { _keyTracker.Clear(); - if (_memoryCache != null) + ClearDataCache(); + } + } + + //DO not call this unless it's for testing since it clears the data cached but not the keys + internal void ClearDataCache() + { + if (_memoryCache != null) + { + _memoryCache.DisposeIfDisposable(); + _memoryCache = new MemoryCache("in-memory"); + } + else + { + foreach (DictionaryEntry c in HttpRuntime.Cache) { - _memoryCache.DisposeIfDisposable(); - _memoryCache = new MemoryCache("in-memory"); - } - else - { - foreach (DictionaryEntry c in HttpRuntime.Cache) + if (c.Key is string && ((string)c.Key).InvariantStartsWith(CacheItemPrefix)) { - if (c.Key is string && ((string)c.Key).InvariantStartsWith(CacheItemPrefix)) - { - if (HttpRuntime.Cache[(string)c.Key] == null) return; - HttpRuntime.Cache.Remove((string)c.Key); - } - } + if (HttpRuntime.Cache[(string)c.Key] == null) return; + HttpRuntime.Cache.Remove((string)c.Key); + } } } } diff --git a/src/Umbraco.Tests/Persistence/Caching/RuntimeCacheProviderTest.cs b/src/Umbraco.Tests/Persistence/Caching/RuntimeCacheProviderTest.cs index 8c176df515..bbf4bc381a 100644 --- a/src/Umbraco.Tests/Persistence/Caching/RuntimeCacheProviderTest.cs +++ b/src/Umbraco.Tests/Persistence/Caching/RuntimeCacheProviderTest.cs @@ -34,6 +34,26 @@ namespace Umbraco.Tests.Persistence.Caching _registry.Save(typeof(MockedEntity), entity6); } + [Test] + public void Tracked_Keys_Removed_When_Cache_Removed() + { + _registry = RuntimeCacheProvider.Current; + + //Fill the registry with random entities + var entity1 = new MockedEntity { Id = 1, Key = 1.ToGuid(), Alias = "mocked1", Name = "Mocked1", Value = Guid.NewGuid().ToString("n") }; + var entity2 = new MockedEntity { Id = 2, Key = 2.ToGuid(), Alias = "mocked2", Name = "Mocked2", Value = Guid.NewGuid().ToString("n") }; + var entity3 = new MockedEntity { Id = 3, Key = 3.ToGuid(), Alias = "mocked3", Name = "Mocked3", Value = Guid.NewGuid().ToString("n") }; + + _registry.Save(typeof(MockedEntity), entity1); + _registry.Save(typeof(MockedEntity), entity2); + _registry.Save(typeof(MockedEntity), entity3); + + //now clear the runtime cache internally + ((RuntimeCacheProvider)_registry).ClearDataCache(); + + Assert.AreEqual(0, _registry.GetAllByType(typeof (MockedEntity)).Count()); + } + [Test] public void Can_Clear_By_Type() { From dc1a67bfa2b97de2494d8a58d2691090d9394ef1 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Wed, 18 Sep 2013 10:06:08 +0200 Subject: [PATCH 04/11] Fixes U4-2822 Datepicker throws "Specified cast is not valid" when saving through API --- src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs | 4 ++-- src/umbraco.editorControls/datepicker/DateData.cs | 15 +++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs index 4b50d835fe..29e4c472b7 100644 --- a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs @@ -65,12 +65,12 @@ namespace Umbraco.Core.Models.Rdbms return Date.Value; } - if(!string.IsNullOrEmpty(VarChar)) + if(string.IsNullOrEmpty(VarChar) == false) { return VarChar; } - if(!string.IsNullOrEmpty(Text)) + if(string.IsNullOrEmpty(Text) == false) { return Text; } diff --git a/src/umbraco.editorControls/datepicker/DateData.cs b/src/umbraco.editorControls/datepicker/DateData.cs index fe1edc7be4..d7e29bef8e 100644 --- a/src/umbraco.editorControls/datepicker/DateData.cs +++ b/src/umbraco.editorControls/datepicker/DateData.cs @@ -11,10 +11,17 @@ namespace umbraco.editorControls.datepicker public override System.Xml.XmlNode ToXMl(System.Xml.XmlDocument d) { - if (Value != null && Value.ToString() != "") - return d.CreateTextNode(((DateTime) Value).ToString("s")); - else - return d.CreateTextNode(""); + if (Value != null && Value.ToString() != "") + { + if(Value is DateTime) + return d.CreateTextNode(((DateTime) Value).ToString("s")); + + DateTime convertedDate; + if (DateTime.TryParse(Value.ToString(), out convertedDate)) + return d.CreateTextNode(convertedDate.ToString("s")); + } + + return d.CreateTextNode(""); } public override void MakeNew(int PropertyId) From 761354dbf5b03421302e2016e7f716fae08036b7 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 18 Sep 2013 10:05:44 +0200 Subject: [PATCH 05/11] Core.Cache - add new method to remove items from cache --- src/Umbraco.Core/Cache/CacheProviderBase.cs | 1 + .../Cache/HttpRuntimeCacheProvider.cs | 27 ++++++++++++ src/Umbraco.Core/Cache/NullCacheProvider.cs | 4 ++ src/Umbraco.Core/Cache/StaticCacheProvider.cs | 41 ++++--------------- src/Umbraco.Core/CacheHelper.cs | 8 ++++ 5 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/Umbraco.Core/Cache/CacheProviderBase.cs b/src/Umbraco.Core/Cache/CacheProviderBase.cs index 026f6f9dbc..012d8b23b2 100644 --- a/src/Umbraco.Core/Cache/CacheProviderBase.cs +++ b/src/Umbraco.Core/Cache/CacheProviderBase.cs @@ -15,6 +15,7 @@ namespace Umbraco.Core.Cache public abstract void ClearCacheItem(string key); public abstract void ClearCacheObjectTypes(string typeName); public abstract void ClearCacheObjectTypes(); + public abstract void ClearCacheObjectTypes(Func predicate); public abstract void ClearCacheByKeySearch(string keyStartsWith); public abstract void ClearCacheByKeyExpression(string regexString); public abstract IEnumerable GetCacheItemsByKeySearch(string keyStartsWith); diff --git a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs index e574f38454..f5fc0e237f 100644 --- a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs @@ -103,6 +103,33 @@ namespace Umbraco.Core.Cache } } + /// + /// Clears all objects in the System.Web.Cache with the System.Type specified that satisfy the predicate + /// + public override void ClearCacheObjectTypes(Func predicate) + { + try + { + lock (Locker) + { + foreach (DictionaryEntry c in _cache) + { + var key = c.Key.ToString(); + if (_cache[key] != null + && _cache[key] is T + && predicate(key, (T)_cache[key])) + { + _cache.Remove(c.Key.ToString()); + } + } + } + } + catch (Exception e) + { + LogHelper.Error("Cache clearing error", e); + } + } + /// /// Clears all cache items that starts with the key passed. /// diff --git a/src/Umbraco.Core/Cache/NullCacheProvider.cs b/src/Umbraco.Core/Cache/NullCacheProvider.cs index 3d1bbaa79d..0ffecf59e3 100644 --- a/src/Umbraco.Core/Cache/NullCacheProvider.cs +++ b/src/Umbraco.Core/Cache/NullCacheProvider.cs @@ -23,6 +23,10 @@ namespace Umbraco.Core.Cache { } + public override void ClearCacheObjectTypes(Func predicate) + { + } + public override void ClearCacheByKeySearch(string keyStartsWith) { } diff --git a/src/Umbraco.Core/Cache/StaticCacheProvider.cs b/src/Umbraco.Core/Cache/StaticCacheProvider.cs index 0d0d2f288c..e3ca529361 100644 --- a/src/Umbraco.Core/Cache/StaticCacheProvider.cs +++ b/src/Umbraco.Core/Cache/StaticCacheProvider.cs @@ -27,50 +27,27 @@ namespace Umbraco.Core.Cache public override void ClearCacheObjectTypes(string typeName) { - foreach (var key in _staticCache.Keys) - { - if (_staticCache[key] != null - && _staticCache[key].GetType().ToString().InvariantEquals(typeName)) - { - object val; - _staticCache.TryRemove(key, out val); - } - } + _staticCache.RemoveAll(kvp => kvp.Value.GetType().ToString().InvariantEquals(typeName)); } public override void ClearCacheObjectTypes() { - foreach (var key in _staticCache.Keys) - { - if (_staticCache[key] != null - && _staticCache[key].GetType() == typeof(T)) - { - object val; - _staticCache.TryRemove(key, out val); - } - } + _staticCache.RemoveAll(kvp => kvp.Value is T); + } + + public override void ClearCacheObjectTypes(Func predicate) + { + _staticCache.RemoveAll(kvp => kvp.Value is T && predicate(kvp.Key, (T)kvp.Value)); } public override void ClearCacheByKeySearch(string keyStartsWith) { - foreach (var key in _staticCache.Keys) - { - if (key.InvariantStartsWith(keyStartsWith)) - { - ClearCacheItem(key); - } - } + _staticCache.RemoveAll(kvp => kvp.Key.InvariantStartsWith(keyStartsWith)); } public override void ClearCacheByKeyExpression(string regexString) { - foreach (var key in _staticCache.Keys) - { - if (Regex.IsMatch(key, regexString)) - { - ClearCacheItem(key); - } - } + _staticCache.RemoveAll(kvp => Regex.IsMatch(kvp.Key, regexString)); } public override IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) diff --git a/src/Umbraco.Core/CacheHelper.cs b/src/Umbraco.Core/CacheHelper.cs index 96b3d99de9..8f7edeb777 100644 --- a/src/Umbraco.Core/CacheHelper.cs +++ b/src/Umbraco.Core/CacheHelper.cs @@ -110,6 +110,14 @@ namespace Umbraco.Core } } + internal void ClearStaticCacheObjectTypes(Func predicate) + { + if (_enableCache) + _staticCache.ClearCacheObjectTypes(predicate); + else + _nullStaticCache.ClearCacheObjectTypes(predicate); + } + /// /// Clears all static cache items that starts with the key passed. /// From d1f36346def179ca3e109abaf307a9237f72870a Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 18 Sep 2013 10:46:34 +0200 Subject: [PATCH 06/11] Core.Cache - fix bug in StaticCacheProvider --- src/Umbraco.Core/Cache/StaticCacheProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Cache/StaticCacheProvider.cs b/src/Umbraco.Core/Cache/StaticCacheProvider.cs index e3ca529361..4f3bb1108c 100644 --- a/src/Umbraco.Core/Cache/StaticCacheProvider.cs +++ b/src/Umbraco.Core/Cache/StaticCacheProvider.cs @@ -72,7 +72,7 @@ namespace Umbraco.Core.Cache public override T GetCacheItem(string cacheKey, Func getCacheItem) { - return (T)_staticCache.GetOrAdd(cacheKey, getCacheItem); + return (T)_staticCache.GetOrAdd(cacheKey, key => getCacheItem()); } } From d2647ea6070753c8d91e2a6956205481bece070b Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 18 Sep 2013 11:33:26 +0200 Subject: [PATCH 07/11] Core.Cache - fix issue introduced in 761354d --- src/Umbraco.Core/Cache/StaticCacheProvider.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Cache/StaticCacheProvider.cs b/src/Umbraco.Core/Cache/StaticCacheProvider.cs index 4f3bb1108c..07bf9fb959 100644 --- a/src/Umbraco.Core/Cache/StaticCacheProvider.cs +++ b/src/Umbraco.Core/Cache/StaticCacheProvider.cs @@ -32,12 +32,14 @@ namespace Umbraco.Core.Cache public override void ClearCacheObjectTypes() { - _staticCache.RemoveAll(kvp => kvp.Value is T); + var typeOfT = typeof (T); + _staticCache.RemoveAll(kvp => kvp.Value.GetType() == typeOfT); } public override void ClearCacheObjectTypes(Func predicate) { - _staticCache.RemoveAll(kvp => kvp.Value is T && predicate(kvp.Key, (T)kvp.Value)); + var typeOfT = typeof(T); + _staticCache.RemoveAll(kvp => kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value)); } public override void ClearCacheByKeySearch(string keyStartsWith) From cb4eef54a4752c7d76d191488242b2e0b6e843fd Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 18 Sep 2013 11:47:37 +0200 Subject: [PATCH 08/11] Core.Cache - fix issue introduced in 761354d --- src/Umbraco.Core/Cache/StaticCacheProvider.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Cache/StaticCacheProvider.cs b/src/Umbraco.Core/Cache/StaticCacheProvider.cs index 07bf9fb959..765e84fd7c 100644 --- a/src/Umbraco.Core/Cache/StaticCacheProvider.cs +++ b/src/Umbraco.Core/Cache/StaticCacheProvider.cs @@ -27,19 +27,19 @@ namespace Umbraco.Core.Cache public override void ClearCacheObjectTypes(string typeName) { - _staticCache.RemoveAll(kvp => kvp.Value.GetType().ToString().InvariantEquals(typeName)); + _staticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType().ToString().InvariantEquals(typeName)); } public override void ClearCacheObjectTypes() { var typeOfT = typeof (T); - _staticCache.RemoveAll(kvp => kvp.Value.GetType() == typeOfT); + _staticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT); } public override void ClearCacheObjectTypes(Func predicate) { var typeOfT = typeof(T); - _staticCache.RemoveAll(kvp => kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value)); + _staticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value)); } public override void ClearCacheByKeySearch(string keyStartsWith) From 5cf07ba6bec68a46ef426926b3d49b407a2666c9 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Wed, 18 Sep 2013 11:49:36 +0200 Subject: [PATCH 09/11] Refactoring how DeleteVersion and DeleteVersions is used in the Content- and MediaRepository, so we ensure that the latest version is never deleted. Worse case would have been an incomplete content node, which we want to avoid. So better to have a fail safe way of deleting versions, and use the regular Delete method for deleting the latest version. --- .../Repositories/ContentRepository.cs | 25 ++++++++++++++++++- .../Repositories/VersionableRepositoryBase.cs | 12 ++++++++- src/Umbraco.Core/Services/ContentService.cs | 12 +++------ src/Umbraco.Core/Services/MediaService.cs | 8 +++--- .../Services/ContentServiceTests.cs | 5 ++-- 5 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 4c93fa70b2..6cd96204f2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -177,7 +177,7 @@ namespace Umbraco.Core.Persistence.Repositories .From() .InnerJoin().On(left => left.VersionId, right => right.VersionId) .Where(x => x.VersionId == versionId) - .Where(x => x.Newest == true); + .Where(x => x.Newest != true); var dto = Database.Fetch(sql).FirstOrDefault(); if(dto == null) return; @@ -190,6 +190,29 @@ namespace Umbraco.Core.Persistence.Repositories } } + public override void DeleteVersions(int id, DateTime versionDate) + { + var sql = new Sql() + .Select("*") + .From() + .InnerJoin().On(left => left.VersionId, right => right.VersionId) + .Where(x => x.NodeId == id) + .Where(x => x.VersionDate < versionDate) + .Where(x => x.Newest != true); + var list = Database.Fetch(sql); + if (list.Any() == false) return; + + using (var transaction = Database.GetTransaction()) + { + foreach (var dto in list) + { + PerformDeleteVersion(id, dto.VersionId); + } + + transaction.Complete(); + } + } + protected override void PerformDeleteVersion(int id, Guid versionId) { Database.Delete("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId }); diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs index 3cbcfc6a14..d6a4d3843e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -46,6 +46,11 @@ namespace Umbraco.Core.Persistence.Repositories var dto = Database.FirstOrDefault("WHERE versionId = @VersionId", new { VersionId = versionId }); if(dto == null) return; + //Ensure that the lastest version is not deleted + var latestVersionDto = Database.FirstOrDefault("WHERE ContentId = @Id ORDER BY VersionDate DESC", new { Id = dto.NodeId }); + if(latestVersionDto.VersionId == dto.VersionId) + return; + using (var transaction = Database.GetTransaction()) { PerformDeleteVersion(dto.NodeId, versionId); @@ -56,7 +61,12 @@ namespace Umbraco.Core.Persistence.Repositories public virtual void DeleteVersions(int id, DateTime versionDate) { - var list = Database.Fetch("WHERE ContentId = @Id AND VersionDate < @VersionDate", new { Id = id, VersionDate = versionDate }); + //Ensure that the latest version is not part of the versions being deleted + var latestVersionDto = Database.FirstOrDefault("WHERE ContentId = @Id ORDER BY VersionDate DESC", new { Id = id }); + var list = + Database.Fetch( + "WHERE versionId <> @VersionId AND (ContentId = @Id AND VersionDate < @VersionDate)", + new {VersionId = latestVersionDto.VersionId, Id = id, VersionDate = versionDate}); if (list.Any() == false) return; using (var transaction = Database.GetTransaction()) diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 76ee81f369..d50c83e06b 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -800,15 +800,13 @@ namespace Umbraco.Core.Services /// /// Permanently deletes versions from an object prior to a specific date. + /// This method will never delete the latest version of a content item. /// /// Id of the object to delete versions from /// Latest version date /// Optional Id of the User deleting versions of a Content object public void DeleteVersions(int id, DateTime versionDate, int userId = 0) { - //TODO: We should check if we are going to delete the most recent version because if that happens it means the - // entity is completely deleted and we should raise the normal Deleting/Deleted event - if (DeletingVersions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, dateToRetain: versionDate), this)) return; @@ -826,6 +824,7 @@ namespace Umbraco.Core.Services /// /// Permanently deletes specific version(s) from an object. + /// This method will never delete the latest version of a content item. /// /// Id of the object to delete a version from /// Id of the version to delete @@ -835,8 +834,8 @@ namespace Umbraco.Core.Services { using (new WriteLock(Locker)) { - //TODO: We should check if we are going to delete the most recent version because if that happens it means the - // entity is completely deleted and we should raise the normal Deleting/Deleted event + if (DeletingVersions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, specificVersion: versionId), this)) + return; if (deletePriorVersions) { @@ -844,9 +843,6 @@ namespace Umbraco.Core.Services DeleteVersions(id, content.UpdateDate, userId); } - if (DeletingVersions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, specificVersion: versionId), this)) - return; - var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateContentRepository(uow)) { diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index 68b4253f77..d9eaa5645b 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -641,6 +641,7 @@ namespace Umbraco.Core.Services /// /// Permanently deletes versions from an object prior to a specific date. + /// This method will never delete the latest version of a content item. /// /// Id of the object to delete versions from /// Latest version date @@ -664,6 +665,7 @@ namespace Umbraco.Core.Services /// /// Permanently deletes specific version(s) from an object. + /// This method will never delete the latest version of a content item. /// /// Id of the object to delete a version from /// Id of the version to delete @@ -671,15 +673,15 @@ namespace Umbraco.Core.Services /// Optional Id of the User deleting versions of a Content object public void DeleteVersion(int id, Guid versionId, bool deletePriorVersions, int userId = 0) { + if (DeletingVersions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, specificVersion: versionId), this)) + return; + if (deletePriorVersions) { var content = GetByVersion(versionId); DeleteVersions(id, content.UpdateDate, userId); } - if (DeletingVersions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, specificVersion:versionId), this)) - return; - var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateMediaRepository(uow)) { diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 3ea7bdef50..e15f05817d 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -843,12 +843,13 @@ namespace Umbraco.Tests.Services Assert.That(sut.GetValue("upload"), Is.EqualTo("/media/1234/koala.jpg")); Assert.That(sut.GetValue("label"), Is.EqualTo("Non-editable label")); //SD: This is failing because the 'content' call to GetValue always has empty milliseconds - Assert.That(sut.GetValue("dateTime"), Is.EqualTo(content.GetValue("dateTime"))); + //MCH: I'm guessing this is an issue because of the format the date is actually stored as, right? Cause we don't do any formatting when saving or loading + Assert.That(sut.GetValue("dateTime").ToString("G"), Is.EqualTo(content.GetValue("dateTime").ToString("G"))); Assert.That(sut.GetValue("colorPicker"), Is.EqualTo("black")); Assert.That(sut.GetValue("folderBrowser"), Is.Empty); Assert.That(sut.GetValue("ddlMultiple"), Is.EqualTo("1234,1235")); Assert.That(sut.GetValue("rbList"), Is.EqualTo("random")); - Assert.That(sut.GetValue("date"), Is.EqualTo(content.GetValue("date"))); + Assert.That(sut.GetValue("date").ToString("G"), Is.EqualTo(content.GetValue("date").ToString("G"))); Assert.That(sut.GetValue("ddl"), Is.EqualTo("1234")); Assert.That(sut.GetValue("chklist"), Is.EqualTo("randomc")); Assert.That(sut.GetValue("contentPicker"), Is.EqualTo(1090)); From f4b8d9bd6e00e0ade9de272636430655143f92a8 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Wed, 18 Sep 2013 11:50:52 +0200 Subject: [PATCH 10/11] Fixing broken Publishing test after the service and repository tests are probably setup. --- .../Publishing/PublishingStrategyTests.cs | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs b/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs index 85b6ec9fd0..fdac3513ab 100644 --- a/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs +++ b/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs @@ -150,6 +150,8 @@ namespace Umbraco.Tests.Publishing var result1 = strategy.Publish(_homePage, 0); Assert.IsTrue(result1); Assert.IsTrue(_homePage.Published); + + //NOTE (MCH) This isn't persisted, so not really a good test as it will look like the result should be something else. foreach (var c in ServiceContext.ContentService.GetChildren(_homePage.Id)) { var r = strategy.Publish(c, 0); @@ -157,15 +159,20 @@ namespace Umbraco.Tests.Publishing Assert.IsTrue(c.Published); } - //ok, all are published except the deepest descendant, we will pass in a flag to include it to - //be published - var result = strategy.PublishWithChildrenInternal( - ServiceContext.ContentService.GetDescendants(_homePage).Concat(new[] { _homePage }), 0, true); - //there will be 4 here but only one "Success" the rest will be "SuccessAlreadyPublished" - Assert.AreEqual(1, result.Count(x => x.Result.StatusType == PublishStatusType.Success)); - Assert.AreEqual(3, result.Count(x => x.Result.StatusType == PublishStatusType.SuccessAlreadyPublished)); - Assert.IsTrue(result.Single(x => x.Result.StatusType == PublishStatusType.Success).Success); - Assert.IsTrue(result.Single(x => x.Result.StatusType == PublishStatusType.Success).Result.ContentItem.Published); + //NOTE (MCH) when doing the test like this the Publish status will not actually have been persisted + //since its only updating a property. The actual persistence and publishing is done through the ContentService. + //So when descendants are fetched from the ContentService the Publish status will be "reset", which + //means the result will be 1 'SuccessAlreadyPublished' and 3 'Success' because the Homepage is + //inserted in the list and since that item has the status of already being Published it will be the one item + //with 'SuccessAlreadyPublished' + + var descendants = ServiceContext.ContentService.GetDescendants(_homePage).Concat(new[] {_homePage}); + var result = strategy.PublishWithChildrenInternal(descendants, 0, true); + + Assert.AreEqual(3, result.Count(x => x.Result.StatusType == PublishStatusType.Success)); + Assert.AreEqual(1, result.Count(x => x.Result.StatusType == PublishStatusType.SuccessAlreadyPublished)); + Assert.IsTrue(result.First(x => x.Result.StatusType == PublishStatusType.Success).Success); + Assert.IsTrue(result.First(x => x.Result.StatusType == PublishStatusType.Success).Result.ContentItem.Published); } [NUnit.Framework.Ignore] From 099c1ff2ea43b3359e6ca92408ac30dd6a0e245f Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 Sep 2013 10:39:47 +1000 Subject: [PATCH 11/11] Fixes: U4-2914 Examine doesn't index newly created items --- src/Umbraco.Web/Search/ExamineEvents.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Umbraco.Web/Search/ExamineEvents.cs b/src/Umbraco.Web/Search/ExamineEvents.cs index de120db8ac..fc1d1c77d0 100644 --- a/src/Umbraco.Web/Search/ExamineEvents.cs +++ b/src/Umbraco.Web/Search/ExamineEvents.cs @@ -50,10 +50,13 @@ namespace Umbraco.Web.Search if (registeredProviders == 0) return; + MediaService.Created += MediaServiceCreated; MediaService.Saved += MediaServiceSaved; MediaService.Deleted += MediaServiceDeleted; MediaService.Moved += MediaServiceMoved; MediaService.Trashed += MediaServiceTrashed; + + ContentService.Created += ContentServiceCreated; ContentService.Saved += ContentServiceSaved; ContentService.Deleted += ContentServiceDeleted; ContentService.Moved += ContentServiceMoved; @@ -78,6 +81,18 @@ namespace Umbraco.Web.Search } } + [SecuritySafeCritical] + static void ContentServiceCreated(IContentService sender, Core.Events.NewEventArgs e) + { + IndexConent(e.Entity); + } + + [SecuritySafeCritical] + static void MediaServiceCreated(IMediaService sender, Core.Events.NewEventArgs e) + { + IndexMedia(e.Entity); + } + [SecuritySafeCritical] static void ContentServiceTrashed(IContentService sender, Core.Events.MoveEventArgs e) {