From 4093fd9b7942718330ea24325b9e5cd3836caf27 Mon Sep 17 00:00:00 2001 From: Shannon Date: Sat, 18 Mar 2017 16:52:30 +1100 Subject: [PATCH 01/18] U4-9637 addDashboardSection package action shouldn't add the dashboard if it already exists --- .../PackageActions/addDashboardSection.cs | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/umbraco.cms/businesslogic/Packager/PackageActions/addDashboardSection.cs b/src/umbraco.cms/businesslogic/Packager/PackageActions/addDashboardSection.cs index 6025123bc4..ceca7c21ff 100644 --- a/src/umbraco.cms/businesslogic/Packager/PackageActions/addDashboardSection.cs +++ b/src/umbraco.cms/businesslogic/Packager/PackageActions/addDashboardSection.cs @@ -43,23 +43,28 @@ namespace umbraco.cms.businesslogic.packager.standardPackageActions if (xmlData.HasChildNodes) { - string sectionAlias = xmlData.Attributes["dashboardAlias"].Value; - string dbConfig = SystemFiles.DashboardConfig; + string sectionAlias = xmlData.Attributes["dashboardAlias"].Value; + string dbConfig = SystemFiles.DashboardConfig; - XmlNode section = xmlData.SelectSingleNode("./section"); - XmlDocument dashboardFile = XmlHelper.OpenAsXmlDocument(dbConfig); + XmlNode section = xmlData.SelectSingleNode("./section"); + XmlDocument dashboardFile = XmlHelper.OpenAsXmlDocument(dbConfig); - XmlNode importedSection = dashboardFile.ImportNode(section, true); + //don't continue if it already exists + var found = dashboardFile.SelectNodes("//section[@alias='" + sectionAlias + "']"); + if (found == null || found.Count <= 0) + { + XmlNode importedSection = dashboardFile.ImportNode(section, true); - XmlAttribute alias = XmlHelper.AddAttribute(dashboardFile, "alias", sectionAlias); - importedSection.Attributes.Append(alias); + XmlAttribute alias = XmlHelper.AddAttribute(dashboardFile, "alias", sectionAlias); + importedSection.Attributes.Append(alias); - dashboardFile.DocumentElement.AppendChild(importedSection); + dashboardFile.DocumentElement.AppendChild(importedSection); - dashboardFile.Save(IOHelper.MapPath(dbConfig)); + dashboardFile.Save(IOHelper.MapPath(dbConfig)); + } - return true; - } + return true; + } return false; } From c8fe9030c753c4788c846d87488f8f2fb065dfff Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 23 Mar 2017 10:39:28 +0100 Subject: [PATCH 02/18] U4-9323 - fix DataTypeDefinitionRepository prevalues cache --- .../DataTypeDefinitionRepository.cs | 91 ++++++------------- .../DataTypeDefinitionRepositoryTest.cs | 21 +---- .../Cache/DataTypeCacheRefresher.cs | 6 +- 3 files changed, 35 insertions(+), 83 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index a4d71885f8..22b99ac732 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -34,7 +34,7 @@ namespace Umbraco.Core.Persistence.Repositories : base(work, cache, logger, sqlSyntax) { _contentTypeRepository = contentTypeRepository; - _preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax); + _preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax); } #region Overrides of RepositoryBase @@ -276,47 +276,28 @@ AND umbracoNode.id <> @id", public PreValueCollection GetPreValuesCollectionByDataTypeId(int dataTypeId) { - var cached = RuntimeCache.GetCacheItemsByKeySearch(GetPrefixedCacheKey(dataTypeId)); - if (cached != null && cached.Any()) - { - //return from the cache, ensure it's a cloned result - return (PreValueCollection)cached.First().DeepClone(); - } - - return GetAndCachePreValueCollection(dataTypeId); - } - - internal static string GetCacheKeyRegex(int preValueId) - { - return CacheKeys.DataTypePreValuesCacheKey + @"[-\d]+-([\d]*,)*" + preValueId + @"(?!\d)[,\d$]*"; + var collection = GetCachedPreValueCollection(dataTypeId); + return collection == null ? null : (PreValueCollection) collection.DeepClone(); } public string GetPreValueAsString(int preValueId) { - //We need to see if we can find the cached PreValueCollection based on the cache key above + var collections = RuntimeCache.GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + "_"); - var cached = RuntimeCache.GetCacheItemsByKeyExpression(GetCacheKeyRegex(preValueId)); - if (cached != null && cached.Any()) - { - //return from the cache - var collection = cached.First(); - var preVal = collection.FormatAsDictionary().Single(x => x.Value.Id == preValueId); - return preVal.Value.Value; - } + var preValue = collections.SelectMany(x => x.FormatAsDictionary().Values).FirstOrDefault(x => x.Id == preValueId); + if (preValue != null) + return preValue.Value; - //go and find the data type id for the pre val id passed in - - var dto = Database.FirstOrDefault("WHERE id = @preValueId", new { preValueId = preValueId }); + var dto = Database.FirstOrDefault("WHERE id = @preValueId", new { preValueId }); if (dto == null) - { return string.Empty; - } - // go cache the collection - var preVals = GetAndCachePreValueCollection(dto.DataTypeNodeId); - //return the single value for this id - var pv = preVals.FormatAsDictionary().Single(x => x.Value.Id == preValueId); - return pv.Value.Value; + var collection = GetCachedPreValueCollection(dto.DataTypeNodeId); + if (collection == null) + return string.Empty; + + preValue = collection.FormatAsDictionary().Values.FirstOrDefault(x => x.Id == preValueId); + return preValue == null ? string.Empty : preValue.Value; } public void AddOrUpdatePreValues(int dataTypeId, IDictionary values) @@ -441,40 +422,28 @@ AND umbracoNode.id <> @id", sortOrder++; } - } - private string GetPrefixedCacheKey(int dataTypeId) + private static string GetPrefixedCacheKey(int dataTypeId) { - return CacheKeys.DataTypePreValuesCacheKey + dataTypeId + "-"; + return CacheKeys.DataTypePreValuesCacheKey + "_" + dataTypeId; } - private PreValueCollection GetAndCachePreValueCollection(int dataTypeId) + private PreValueCollection GetCachedPreValueCollection(int datetypeId) { - //go get the data - var dtos = Database.Fetch("WHERE datatypeNodeId = @Id", new { Id = dataTypeId }); - var list = dtos.Select(x => new Tuple(new PreValue(x.Id, x.Value, x.SortOrder), x.Alias, x.SortOrder)).ToList(); - var collection = PreValueConverter.ConvertToPreValuesCollection(list); - - //now create the cache key, this needs to include all pre-value ids so that we can use this cached item in the GetPreValuesAsString method - //the key will be: "UmbracoPreValDATATYPEID-CSVOFPREVALIDS - - var key = GetPrefixedCacheKey(dataTypeId) - + string.Join(",", collection.FormatAsDictionary().Select(x => x.Value.Id).ToArray()); - - //store into cache - RuntimeCache.InsertCacheItem(key, () => collection, - //30 mins - new TimeSpan(0, 0, 30), - //sliding is true - true); - - return collection; + var key = GetPrefixedCacheKey(datetypeId); + return RuntimeCache.GetCacheItem(key, () => + { + var dtos = Database.Fetch("WHERE datatypeNodeId = @Id", new { Id = datetypeId }); + var list = dtos.Select(x => new Tuple(new PreValue(x.Id, x.Value, x.SortOrder), x.Alias, x.SortOrder)).ToList(); + var collection = PreValueConverter.ConvertToPreValuesCollection(list); + return collection; + }, TimeSpan.FromMinutes(20), isSliding: true); } private string EnsureUniqueNodeName(string nodeName, int id = 0) { - + var sql = new Sql(); sql.Select("*") @@ -576,7 +545,7 @@ AND umbracoNode.id <> @id", } //NOTE: We used to check that the Alias was unique for the given DataTypeNodeId prevalues list, BUT - // in reality there is no need to check the uniqueness of this alias because the only way that this code executes is + // in reality there is no need to check the uniqueness of this alias because the only way that this code executes is // based on an IDictionary dictionary being passed to this repository and a dictionary // must have unique aliases by definition, so there is no need for this additional check @@ -596,10 +565,10 @@ AND umbracoNode.id <> @id", { throw new InvalidOperationException("Cannot update a pre value for a data type that has no identity"); } - + //NOTE: We used to check that the Alias was unique for the given DataTypeNodeId prevalues list, BUT // this causes issues when sorting the pre-values (http://issues.umbraco.org/issue/U4-5670) but in reality - // there is no need to check the uniqueness of this alias because the only way that this code executes is + // there is no need to check the uniqueness of this alias because the only way that this code executes is // based on an IDictionary dictionary being passed to this repository and a dictionary // must have unique aliases by definition, so there is no need for this additional check @@ -614,7 +583,7 @@ AND umbracoNode.id <> @id", Database.Update(dto); } - + } internal static class PreValueConverter diff --git a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs index 4db63f6085..9f7231037e 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs @@ -46,23 +46,6 @@ namespace Umbraco.Tests.Persistence.Repositories return new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, Constants.ObjectTypes.DataTypeContainerGuid); } - [TestCase("UmbracoPreVal87-21,3,48", 3, true)] - [TestCase("UmbracoPreVal87-21,33,48", 3, false)] - [TestCase("UmbracoPreVal87-21,33,48", 33, true)] - [TestCase("UmbracoPreVal87-21,3,48", 33, false)] - [TestCase("UmbracoPreVal87-21,3,48", 21, true)] - [TestCase("UmbracoPreVal87-21,3,48", 48, true)] - [TestCase("UmbracoPreVal87-22,33,48", 2, false)] - [TestCase("UmbracoPreVal87-22,33,48", 22, true)] - [TestCase("UmbracoPreVal87-22,33,44", 4, false)] - [TestCase("UmbracoPreVal87-22,33,44", 44, true)] - [TestCase("UmbracoPreVal87-22,333,44", 33, false)] - [TestCase("UmbracoPreVal87-22,333,44", 333, true)] - public void Pre_Value_Cache_Key_Tests(string cacheKey, int preValueId, bool outcome) - { - Assert.AreEqual(outcome, Regex.IsMatch(cacheKey, DataTypeDefinitionRepository.GetCacheKeyRegex(preValueId))); - } - [Test] public void Can_Move() { @@ -537,7 +520,7 @@ namespace Umbraco.Tests.Persistence.Repositories } var cached = cache.IsolatedRuntimeCache.GetCache().Result - .GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + dtd.Id + "-"); + .GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + "_" + dtd.Id); Assert.IsNotNull(cached); Assert.AreEqual(1, cached.Count()); @@ -581,7 +564,7 @@ namespace Umbraco.Tests.Persistence.Repositories } var cached = cache.IsolatedRuntimeCache.GetCache().Result - .GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + dtd.Id + "-"); + .GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + "_" + dtd.Id); Assert.IsNotNull(cached); Assert.AreEqual(1, cached.Count()); diff --git a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs index 11b3ab6294..2300082ece 100644 --- a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs @@ -101,13 +101,13 @@ namespace Umbraco.Web.Cache ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.IdToKeyCacheKey); ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.KeyToIdCacheKey); + var dataTypeCache = ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); payloads.ForEach(payload => { //clears the prevalue cache - var dataTypeCache = ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); if (dataTypeCache) - dataTypeCache.Result.ClearCacheByKeySearch(string.Format("{0}{1}", CacheKeys.DataTypePreValuesCacheKey, payload.Id)); - + dataTypeCache.Result.ClearCacheByKeySearch(string.Format("{0}_{1}", CacheKeys.DataTypePreValuesCacheKey, payload.Id)); + PublishedContentType.ClearDataType(payload.Id); }); From fe11d4770d7d78c994e819c73c2d96a5b329a968 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 23 Mar 2017 14:07:45 +0100 Subject: [PATCH 03/18] U4-9615 - implement way to find listviews in path fast --- .../Repositories/ContentTypeBaseRepository.cs | 9 +++++ .../Services/ContentTypeServiceBase.cs | 11 ++++++ .../Models/Mapping/ContentModelMapper.cs | 28 ++------------- .../Models/Mapping/MediaModelMapper.cs | 34 ++++--------------- 4 files changed, 29 insertions(+), 53 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index d86f77168e..4d401ce6f8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -1250,5 +1250,14 @@ WHERE cmsContentType." + aliasColumn + @" LIKE @pattern", while (aliases.Contains(test = alias + i)) i++; return test; } + + internal bool HasContainerInPath(string contentPath) + { + var ids = contentPath.Split(',').Select(int.Parse); + var sql = new Sql(@"SELECT COUNT(pk) FROM cmsContentType +INNER JOIN cmsContent ON cmsContentType.nodeId=cmsContent.contentType +WHERE cmsContent.nodeId IN (@ids) AND cmsContentType.isContainer=@isContainer", new { ids, isContainer = true }); + return Database.ExecuteScalar(sql) > 0; + } } } diff --git a/src/Umbraco.Core/Services/ContentTypeServiceBase.cs b/src/Umbraco.Core/Services/ContentTypeServiceBase.cs index df29012b90..231186b99f 100644 --- a/src/Umbraco.Core/Services/ContentTypeServiceBase.cs +++ b/src/Umbraco.Core/Services/ContentTypeServiceBase.cs @@ -5,6 +5,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services @@ -70,5 +71,15 @@ namespace Umbraco.Core.Services return toUpdate; } + + internal bool HasContainerInPath(string contentPath) + { + using (var uow = UowProvider.GetUnitOfWork()) + { + // can use same repo for both content and media + var repository = (ContentTypeRepository) RepositoryFactory.CreateContentTypeRepository(uow); + return repository.HasContainerInPath(contentPath); + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index e179159e7c..2fd57b6290 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -86,31 +86,9 @@ namespace Umbraco.Web.Models.Mapping private static void AfterMap(IContent content, ContentItemDisplay display, IDataTypeService dataTypeService, ILocalizedTextService localizedText, IContentTypeService contentTypeService) { - //map the IsChildOfListView (this is actually if it is a descendant of a list view!) - //TODO: Fix this shorthand .Ancestors() lookup, at least have an overload to use the current - if (content.HasIdentity) - { - var ancesctorListView = content.Ancestors().FirstOrDefault(x => x.ContentType.IsContainer); - display.IsChildOfListView = ancesctorListView != null; - } - else - { - //it's new so it doesn't have a path, so we need to look this up by it's parent + ancestors - var parent = content.Parent(); - if (parent == null) - { - display.IsChildOfListView = false; - } - else if (parent.ContentType.IsContainer) - { - display.IsChildOfListView = true; - } - else - { - var ancesctorListView = parent.Ancestors().FirstOrDefault(x => x.ContentType.IsContainer); - display.IsChildOfListView = ancesctorListView != null; - } - } + // map the IsChildOfListView (this is actually if it is a descendant of a list view!) + var parent = content.Parent(); + display.IsChildOfListView = parent != null && (parent.ContentType.IsContainer || ((ContentTypeService) contentTypeService).HasContainerInPath(parent.Path)); //map the tree node url if (HttpContext.Current != null) diff --git a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs index c3f9412401..cfc254b085 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs @@ -41,7 +41,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(display => display.IsContainer, expression => expression.Ignore()) .ForMember(display => display.HasPublishedVersion, expression => expression.Ignore()) .ForMember(display => display.Tabs, expression => expression.ResolveUsing(new TabsAndPropertiesResolver(applicationContext.Services.TextService))) - .AfterMap((media, display) => AfterMap(media, display, applicationContext.Services.DataTypeService, applicationContext.Services.TextService, applicationContext.ProfilingLogger.Logger)); + .AfterMap((media, display) => AfterMap(media, display, applicationContext.Services.DataTypeService, applicationContext.Services.TextService, applicationContext.Services.ContentTypeService, applicationContext.ProfilingLogger.Logger)); //FROM IMedia TO ContentItemBasic config.CreateMap>() @@ -64,34 +64,12 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dto => dto.HasPublishedVersion, expression => expression.Ignore()); } - private static void AfterMap(IMedia media, MediaItemDisplay display, IDataTypeService dataTypeService, ILocalizedTextService localizedText, ILogger logger) + private static void AfterMap(IMedia media, MediaItemDisplay display, IDataTypeService dataTypeService, ILocalizedTextService localizedText, IContentTypeService contentTypeService, ILogger logger) { - // Adapted from ContentModelMapper - //map the IsChildOfListView (this is actually if it is a descendant of a list view!) - //TODO: Fix this shorthand .Ancestors() lookup, at least have an overload to use the current - if (media.HasIdentity) - { - var ancesctorListView = media.Ancestors().FirstOrDefault(x => x.ContentType.IsContainer); - display.IsChildOfListView = ancesctorListView != null; - } - else - { - //it's new so it doesn't have a path, so we need to look this up by it's parent + ancestors - var parent = media.Parent(); - if (parent == null) - { - display.IsChildOfListView = false; - } - else if (parent.ContentType.IsContainer) - { - display.IsChildOfListView = true; - } - else - { - var ancesctorListView = parent.Ancestors().FirstOrDefault(x => x.ContentType.IsContainer); - display.IsChildOfListView = ancesctorListView != null; - } - } + // Adapted from ContentModelMapper + //map the IsChildOfListView (this is actually if it is a descendant of a list view!) + var parent = media.Parent(); + display.IsChildOfListView = parent != null && (parent.ContentType.IsContainer || ((ContentTypeService) contentTypeService).HasContainerInPath(parent.Path)); //map the tree node url if (HttpContext.Current != null) From 870657affa87b773c94c25431388295681783b3e Mon Sep 17 00:00:00 2001 From: andyneil Date: Thu, 23 Mar 2017 13:41:23 +0000 Subject: [PATCH 04/18] Update wording to be more grammatically correct --- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index f7ac08d837..ad30f0c62f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -972,7 +972,7 @@ To manage your website, simply open the Umbraco back office and start adding con Dictionary item saved Publishing failed because the parent page isn't published Content published - and visible at the website + and visible on the website Content saved Remember to publish to make changes visible Sent For Approval From cf7cafeb4cf26ac8eacaf8b1ad6f4bf664c20983 Mon Sep 17 00:00:00 2001 From: andyneil Date: Thu, 23 Mar 2017 13:41:48 +0000 Subject: [PATCH 05/18] Update wording to be more grammatically correct --- src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index f68359b0af..0aedd2b7ca 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -972,7 +972,7 @@ To manage your website, simply open the Umbraco back office and start adding con Dictionary item saved Publishing failed because the parent page isn't published Content published - and visible at the website + and visible on the website Content saved Remember to publish to make changes visible Sent For Approval From 085a678266b87dc92b20cbf845d1414db22916ec Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 23 Mar 2017 17:44:07 +0100 Subject: [PATCH 06/18] U4-9617 - perf for published date --- src/Umbraco.Core/Models/Content.cs | 3 +++ .../Rdbms/DocumentPublishedReadOnlyDto.cs | 3 +++ .../Persistence/Factories/ContentFactory.cs | 3 +++ .../Repositories/ContentRepository.cs | 7 ++++-- .../Models/Mapping/ContentModelMapper.cs | 22 +------------------ 5 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index ec73e1ff5e..cef06ec4f7 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -274,6 +274,9 @@ namespace Umbraco.Core.Models /// public bool HasPublishedVersion { get { return PublishedVersionGuid != default(Guid); } } + [IgnoreDataMember] + internal DateTime PublishedDate { get; set; } + /// /// Changes the Trashed state of the content object /// diff --git a/src/Umbraco.Core/Models/Rdbms/DocumentPublishedReadOnlyDto.cs b/src/Umbraco.Core/Models/Rdbms/DocumentPublishedReadOnlyDto.cs index 4a7e359d91..7c6507f499 100644 --- a/src/Umbraco.Core/Models/Rdbms/DocumentPublishedReadOnlyDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/DocumentPublishedReadOnlyDto.cs @@ -19,5 +19,8 @@ namespace Umbraco.Core.Models.Rdbms [Column("newest")] public bool Newest { get; set; } + + [Column("updateDate")] + public DateTime VersionDate { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs index 0532eab6b1..512f02e8b5 100644 --- a/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs @@ -72,6 +72,9 @@ namespace Umbraco.Core.Persistence.Factories content.PublishedVersionGuid = publishedDto == null ? (dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId) : publishedDto.VersionId; + content.PublishedDate = publishedDto == null + ? (dto.DocumentPublishedReadOnlyDto == null ? default(DateTime) : dto.DocumentPublishedReadOnlyDto.VersionDate) + : publishedDto.VersionDate; //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index f95c295243..b5d3fb6986 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -513,9 +513,10 @@ namespace Umbraco.Core.Persistence.Repositories dto.DocumentPublishedReadOnlyDto = new DocumentPublishedReadOnlyDto { VersionId = dto.VersionId, + VersionDate = dto.UpdateDate, Newest = true, NodeId = dto.NodeId, - Published = true + Published = true }; ((Content)entity).PublishedVersionGuid = dto.VersionId; } @@ -687,6 +688,7 @@ namespace Umbraco.Core.Persistence.Repositories dto.DocumentPublishedReadOnlyDto = new DocumentPublishedReadOnlyDto { VersionId = dto.VersionId, + VersionDate = dto.UpdateDate, Newest = true, NodeId = dto.NodeId, Published = true @@ -698,6 +700,7 @@ namespace Umbraco.Core.Persistence.Repositories dto.DocumentPublishedReadOnlyDto = new DocumentPublishedReadOnlyDto { VersionId = default(Guid), + VersionDate = default(DateTime), Newest = false, NodeId = dto.NodeId, Published = false @@ -974,7 +977,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder", } //order by update date DESC, if there is corrupted published flags we only want the latest! - var publishedSql = new Sql(@"SELECT cmsDocument.nodeId, cmsDocument.published, cmsDocument.versionId, cmsDocument.newest + var publishedSql = new Sql(@"SELECT cmsDocument.nodeId, cmsDocument.published, cmsDocument.versionId, cmsDocument.updateDate, cmsDocument.newest FROM cmsDocument INNER JOIN cmsContentVersion ON cmsContentVersion.VersionId = cmsDocument.versionId WHERE cmsDocument.published = 1 AND cmsDocument.nodeId IN (" + parsedOriginalSql + @") diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index e179159e7c..b95fefe6f9 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -36,7 +36,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(display => display.IsContainer, expression => expression.MapFrom(content => content.ContentType.IsContainer)) .ForMember(display => display.IsChildOfListView, expression => expression.Ignore()) .ForMember(display => display.Trashed, expression => expression.MapFrom(content => content.Trashed)) - .ForMember(display => display.PublishDate, expression => expression.MapFrom(content => GetPublishedDate(content, applicationContext))) + .ForMember(display => display.PublishDate, expression => expression.MapFrom(content => ((Content)content).PublishedDate)) .ForMember(display => display.TemplateAlias, expression => expression.MapFrom(content => content.Template.Alias)) .ForMember(display => display.HasPublishedVersion, expression => expression.MapFrom(content => content.HasPublishedVersion)) .ForMember(display => display.Urls, @@ -222,26 +222,6 @@ namespace Umbraco.Web.Models.Mapping }); } - /// - /// Gets the published date value for the IContent object - /// - /// - /// - /// - private static DateTime? GetPublishedDate(IContent content, ApplicationContext applicationContext) - { - if (content.Published) - { - return content.UpdateDate; - } - if (content.HasPublishedVersion) - { - var published = applicationContext.Services.ContentService.GetPublishedVersion(content.Id); - return published.UpdateDate; - } - return null; - } - /// /// Creates the list of action buttons allowed for this user - Publish, Send to publish, save, unpublish returned as the button's 'letter' /// From a23c1507383de522156a1b9c085639fb41207fc3 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 23 Mar 2017 17:51:30 +0100 Subject: [PATCH 07/18] U4-9617 - fix --- src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index b95fefe6f9..737d38096e 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -36,7 +36,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(display => display.IsContainer, expression => expression.MapFrom(content => content.ContentType.IsContainer)) .ForMember(display => display.IsChildOfListView, expression => expression.Ignore()) .ForMember(display => display.Trashed, expression => expression.MapFrom(content => content.Trashed)) - .ForMember(display => display.PublishDate, expression => expression.MapFrom(content => ((Content)content).PublishedDate)) + .ForMember(display => display.PublishDate, expression => expression.MapFrom(content => GetPublishedDate(content))) .ForMember(display => display.TemplateAlias, expression => expression.MapFrom(content => content.Template.Alias)) .ForMember(display => display.HasPublishedVersion, expression => expression.MapFrom(content => content.HasPublishedVersion)) .ForMember(display => display.Urls, @@ -75,6 +75,12 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dto => dto.Alias, expression => expression.Ignore()); } + private static DateTime? GetPublishedDate(IContent content) + { + var date = ((Content) content).PublishedDate; + return date == default (DateTime) ? (DateTime?) null : date; + } + /// /// Maps the generic tab with custom properties for content /// From af10ae9e343a02e46f229cfbf3e208822dc87698 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 23 Mar 2017 18:00:58 +0100 Subject: [PATCH 08/18] U4-9617 - fix --- .../Persistence/Repositories/ContentRepository.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index b5d3fb6986..ed55bbeeb1 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -518,7 +518,8 @@ namespace Umbraco.Core.Persistence.Repositories NodeId = dto.NodeId, Published = true }; - ((Content)entity).PublishedVersionGuid = dto.VersionId; + ((Content) entity).PublishedVersionGuid = dto.VersionId; + ((Content) entity).PublishedDate = dto.UpdateDate; } entity.ResetDirtyProperties(); @@ -693,19 +694,21 @@ namespace Umbraco.Core.Persistence.Repositories NodeId = dto.NodeId, Published = true }; - ((Content)entity).PublishedVersionGuid = dto.VersionId; + ((Content) entity).PublishedVersionGuid = dto.VersionId; + ((Content) entity).PublishedDate = dto.UpdateDate; } else if (publishedStateChanged) { dto.DocumentPublishedReadOnlyDto = new DocumentPublishedReadOnlyDto { - VersionId = default(Guid), - VersionDate = default(DateTime), + VersionId = default (Guid), + VersionDate = default (DateTime), Newest = false, NodeId = dto.NodeId, Published = false }; - ((Content)entity).PublishedVersionGuid = default(Guid); + ((Content) entity).PublishedVersionGuid = default(Guid); + ((Content) entity).PublishedDate = default (DateTime); } entity.ResetDirtyProperties(); From 606ebcae928f02e41a79f8414e34bd42f8f69f7f Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 24 Mar 2017 11:07:15 +0100 Subject: [PATCH 09/18] U4-9604 - fix current HttpContext in ManyObjectsResolverBase --- .../ManyObjectsResolverBase.cs | 32 ++++++++----------- .../Checks/Security/ClickJackingCheck.cs | 3 +- .../Checks/Security/ExcessiveHeadersCheck.cs | 3 +- .../HealthCheck/HealthCheckController.cs | 12 ++++++- .../HealthCheck/HealthCheckResolver.cs | 4 +-- 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs index 5e170f47f4..e54e6f550b 100644 --- a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs +++ b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs @@ -22,6 +22,7 @@ namespace Umbraco.Core.ObjectResolution private readonly string _httpContextKey; private readonly List _instanceTypes = new List(); private IEnumerable _sortedValues; + private readonly Func _httpContextGetter; private int _defaultPluginWeight = 100; @@ -42,12 +43,7 @@ namespace Umbraco.Core.ObjectResolution if (logger == null) throw new ArgumentNullException("logger"); CanResolveBeforeFrozen = false; if (scope == ObjectLifetimeScope.HttpRequest) - { - if (HttpContext.Current == null) - throw new InvalidOperationException("Use alternative constructor accepting a HttpContextBase object in order to set the lifetime scope to HttpRequest when HttpContext.Current is null"); - - CurrentHttpContext = new HttpContextWrapper(HttpContext.Current); - } + _httpContextGetter = () => new HttpContextWrapper(HttpContext.Current); ServiceProvider = serviceProvider; Logger = logger; @@ -84,7 +80,7 @@ namespace Umbraco.Core.ObjectResolution LifetimeScope = ObjectLifetimeScope.HttpRequest; _httpContextKey = GetType().FullName; ServiceProvider = serviceProvider; - CurrentHttpContext = httpContext; + _httpContextGetter = () => httpContext; _instanceTypes = new List(); InitializeAppInstances(); @@ -160,7 +156,7 @@ namespace Umbraco.Core.ObjectResolution /// Gets or sets the used to initialize this object, if any. /// /// If not null, then LifetimeScope will be ObjectLifetimeScope.HttpRequest. - protected HttpContextBase CurrentHttpContext { get; private set; } + protected HttpContextBase CurrentHttpContext { get { return _httpContextGetter == null ? null : _httpContextGetter(); } } /// /// Returns the service provider used to instantiate objects @@ -196,7 +192,7 @@ namespace Umbraco.Core.ObjectResolution /// /// Gets or sets the default type weight. /// - /// Determines the weight of types that do not have a WeightAttribute set on + /// Determines the weight of types that do not have a WeightAttribute set on /// them, when calling GetSortedValues. protected virtual int DefaultPluginWeight { @@ -276,7 +272,7 @@ namespace Umbraco.Core.ObjectResolution /// Removes a type. /// /// The type to remove. - /// the resolver does not support removing types, or + /// the resolver does not support removing types, or /// the type is not a valid type for the resolver. public virtual void RemoveType(Type value) { @@ -296,7 +292,7 @@ namespace Umbraco.Core.ObjectResolution /// Removes a type. /// /// The type to remove. - /// the resolver does not support removing types, or + /// the resolver does not support removing types, or /// the type is not a valid type for the resolver. public void RemoveType() where T : TResolved @@ -309,7 +305,7 @@ namespace Umbraco.Core.ObjectResolution /// /// The types to add. /// The types are appended at the end of the list. - /// the resolver does not support adding types, or + /// the resolver does not support adding types, or /// a type is not a valid type for the resolver, or a type is already in the collection of types. protected void AddTypes(IEnumerable types) { @@ -336,7 +332,7 @@ namespace Umbraco.Core.ObjectResolution /// /// The type to add. /// The type is appended at the end of the list. - /// the resolver does not support adding types, or + /// the resolver does not support adding types, or /// the type is not a valid type for the resolver, or the type is already in the collection of types. public virtual void AddType(Type value) { @@ -362,7 +358,7 @@ namespace Umbraco.Core.ObjectResolution /// /// The type to add. /// The type is appended at the end of the list. - /// the resolver does not support adding types, or + /// the resolver does not support adding types, or /// the type is not a valid type for the resolver, or the type is already in the collection of types. public void AddType() where T : TResolved @@ -404,7 +400,7 @@ namespace Umbraco.Core.ObjectResolution /// /// The zero-based index at which the type should be inserted. /// The type to insert. - /// the resolver does not support inserting types, or + /// the resolver does not support inserting types, or /// the type is not a valid type for the resolver, or the type is already in the collection of types. /// is out of range. public virtual void InsertType(int index, Type value) @@ -430,7 +426,7 @@ namespace Umbraco.Core.ObjectResolution /// Inserts a type at the beginning of the list. /// /// The type to insert. - /// the resolver does not support inserting types, or + /// the resolver does not support inserting types, or /// the type is not a valid type for the resolver, or the type is already in the collection of types. public virtual void InsertType(Type value) { @@ -464,7 +460,7 @@ namespace Umbraco.Core.ObjectResolution /// /// The existing type before which to insert. /// The type to insert. - /// the resolver does not support inserting types, or + /// the resolver does not support inserting types, or /// one of the types is not a valid type for the resolver, or the existing type is not in the collection, /// or the new type is already in the collection of types. public virtual void InsertTypeBefore(Type existingType, Type value) @@ -498,7 +494,7 @@ namespace Umbraco.Core.ObjectResolution /// /// The existing type before which to insert. /// The type to insert. - /// the resolver does not support inserting types, or + /// the resolver does not support inserting types, or /// one of the types is not a valid type for the resolver, or the existing type is not in the collection, /// or the new type is already in the collection of types. public void InsertTypeBefore() diff --git a/src/Umbraco.Web/HealthCheck/Checks/Security/ClickJackingCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Security/ClickJackingCheck.cs index bdfd504d73..54f72dc88a 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Security/ClickJackingCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Security/ClickJackingCheck.cs @@ -64,7 +64,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Security var url = HealthCheckContext.HttpContext.Request.Url; // Access the site home page and check for the click-jack protection header or meta tag - var useSsl = GlobalSettings.UseSSL || HealthCheckContext.HttpContext.Request.ServerVariables["SERVER_PORT"] == "443"; + var serverVariables = HealthCheckContext.HttpContext.Request.ServerVariables; + var useSsl = GlobalSettings.UseSSL || serverVariables["SERVER_PORT"] == "443"; var address = string.Format("http{0}://{1}:{2}", useSsl ? "s" : "", url.Host.ToLower(), url.Port); var request = WebRequest.Create(address); request.Method = "GET"; diff --git a/src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs index cf279bf3f8..2ca63662d8 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs @@ -49,7 +49,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Security var url = HealthCheckContext.HttpContext.Request.Url; // Access the site home page and check for the headers - var useSsl = GlobalSettings.UseSSL || HealthCheckContext.HttpContext.Request.ServerVariables["SERVER_PORT"] == "443"; + var serverVariables = HealthCheckContext.HttpContext.Request.ServerVariables; + var useSsl = GlobalSettings.UseSSL || serverVariables["SERVER_PORT"] == "443"; var address = string.Format("http{0}://{1}:{2}", useSsl ? "s" : "", url.Host.ToLower(), url.Port); var request = WebRequest.Create(address); request.Method = "HEAD"; diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckController.cs b/src/Umbraco.Web/HealthCheck/HealthCheckController.cs index 919df88962..14bfaaea9f 100644 --- a/src/Umbraco.Web/HealthCheck/HealthCheckController.cs +++ b/src/Umbraco.Web/HealthCheck/HealthCheckController.cs @@ -48,12 +48,22 @@ namespace Umbraco.Web.HealthCheck return healthCheckGroups; } + [HttpGet] public object GetStatus(Guid id) { var check = _healthCheckResolver.HealthChecks.FirstOrDefault(x => x.Id == id); if (check == null) throw new InvalidOperationException("No health check found with ID " + id); - return check.GetStatus(); + try + { + //Core.Logging.LogHelper.Debug("Running health check: " + check.Name); + return check.GetStatus(); + } + catch (Exception e) + { + Core.Logging.LogHelper.Error("Exception in health check: " + check.Name, e); + throw; + } } [HttpPost] diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckResolver.cs b/src/Umbraco.Web/HealthCheck/HealthCheckResolver.cs index dfe5b792a5..7ae302fa49 100644 --- a/src/Umbraco.Web/HealthCheck/HealthCheckResolver.cs +++ b/src/Umbraco.Web/HealthCheck/HealthCheckResolver.cs @@ -19,7 +19,7 @@ namespace Umbraco.Web.HealthCheck /// internal class HealthCheckResolver : LazyManyObjectsResolverBase, IHealthCheckResolver { - public HealthCheckResolver(ILogger logger, Func> lazyTypeList) + public HealthCheckResolver(ILogger logger, Func> lazyTypeList) : base(new HealthCheckServiceProvider(), logger, lazyTypeList, ObjectLifetimeScope.HttpRequest) { } @@ -51,7 +51,7 @@ namespace Umbraco.Web.HealthCheck new HealthCheckContext(new HttpContextWrapper(HttpContext.Current), UmbracoContext.Current) }); } - + //use normal ctor return Activator.CreateInstance(serviceType); } From 879a1320775f98003bf17a739e980a126d04e92d Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 24 Mar 2017 13:05:06 +0100 Subject: [PATCH 10/18] U4-9598 - fix paging in UmbracoContentIndexer --- src/UmbracoExamine/UmbracoContentIndexer.cs | 41 +++++++++++---------- src/UmbracoExamine/UmbracoMemberIndexer.cs | 9 +++-- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/UmbracoExamine/UmbracoContentIndexer.cs index 37382d2647..0cd8f2ee33 100644 --- a/src/UmbracoExamine/UmbracoContentIndexer.cs +++ b/src/UmbracoExamine/UmbracoContentIndexer.cs @@ -444,6 +444,7 @@ namespace UmbracoExamine //sorted by: umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder var result = _contentService.GetPagedXmlEntries(path, pIndex, pSize, out totalContent).ToArray(); + var more = result.Length == pSize; //then like we do in the ContentRepository.BuildXmlCache we need to track what Parents have been processed // already so that we can then exclude implicitly unpublished content items @@ -479,7 +480,7 @@ namespace UmbracoExamine filtered.Add(xml); } - return new Tuple(totalContent, filtered.ToArray()); + return Tuple.Create(filtered.ToArray(), more); }, i => _contentService.GetById(i)); } @@ -545,7 +546,8 @@ namespace UmbracoExamine { long totalMedia; var result = _mediaService.GetPagedXmlEntries(path, pIndex, pSize, out totalMedia).ToArray(); - return new Tuple(totalMedia, result); + var more = result.Length == pSize; + return Tuple.Create(result, more); }, i => _mediaService.GetById(i)); @@ -573,39 +575,38 @@ namespace UmbracoExamine string type, int parentId, Func getContentTypes, - Func> getPagedXmlEntries, + Func> getPagedXmlEntries, Func getContent) where TContentType: IContentTypeComposition { const int pageSize = 10000; - var pageIndex = 0; - - XElement[] xElements; + var pageIndex = 0; var contentTypes = getContentTypes(); var icons = contentTypes.ToDictionary(x => x.Id, y => y.Icon); + var parent = parentId == -1 ? null : getContent(parentId); + bool more; do { - long total; + XElement[] xElements; + if (parentId == -1) { var pagedElements = getPagedXmlEntries("-1", pageIndex, pageSize); - total = pagedElements.Item1; - xElements = pagedElements.Item2; + xElements = pagedElements.Item1; + more = pagedElements.Item2; + } + else if (parent == null) + { + xElements = new XElement[0]; + more = false; } else { - //Get the parent - var parent = getContent(parentId); - if (parent == null) - xElements = new XElement[0]; - else - { - var pagedElements = getPagedXmlEntries(parent.Path, pageIndex, pageSize); - total = pagedElements.Item1; - xElements = pagedElements.Item2; - } + var pagedElements = getPagedXmlEntries(parent.Path, pageIndex, pageSize); + xElements = pagedElements.Item1; + more = pagedElements.Item2; } //if specific types are declared we need to post filter them @@ -626,7 +627,7 @@ namespace UmbracoExamine AddNodesToIndex(xElements, type); pageIndex++; - } while (xElements.Length == pageSize); + } while (more); } internal static IEnumerable GetSerializedContent( diff --git a/src/UmbracoExamine/UmbracoMemberIndexer.cs b/src/UmbracoExamine/UmbracoMemberIndexer.cs index ad393932be..3629265f94 100644 --- a/src/UmbracoExamine/UmbracoMemberIndexer.cs +++ b/src/UmbracoExamine/UmbracoMemberIndexer.cs @@ -117,7 +117,7 @@ namespace UmbracoExamine if (indexerData.UserFields.Any(x => x.Name == "_searchEmail") == false) { var field = new IndexField { Name = "_searchEmail" }; - + StaticField policy; if (IndexFieldPolicies.TryGetValue("_searchEmail", out policy)) { @@ -173,7 +173,8 @@ namespace UmbracoExamine { long totalContent; var result = _memberService.GetPagedXmlEntries(pIndex, pSize, out totalContent).ToArray(); - return new Tuple(totalContent, result); + var more = result.Length == pSize; + return Tuple.Create(result, more); }, i => _memberService.GetById(i)); } @@ -219,7 +220,7 @@ namespace UmbracoExamine { stopwatch.Stop(); } - + DataService.LogService.AddInfoLog(-1, string.Format("PerformIndexAll - End data queries - {0}, took {1}ms", type, stopwatch.ElapsedMilliseconds)); } @@ -247,7 +248,7 @@ namespace UmbracoExamine fields.Add("__key", valuesForIndexing); } } - + return fields; From 21963c03a213abdcd6547916e370a3d0a266f5d5 Mon Sep 17 00:00:00 2001 From: Alexander Bryukhov Date: Fri, 24 Mar 2017 21:59:02 +0700 Subject: [PATCH 11/18] Adding missing keys before v7.6 --- src/Umbraco.Web.UI/umbraco/config/lang/ru.xml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml index c6f94949d2..82e5626bac 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml @@ -123,7 +123,6 @@ Сохранить список Выбрать Выбрать текущую папку - выбранные Предварительный просмотр Предварительный просмотр запрещен, так как документу не сопоставлен шаблон Другие действия @@ -993,6 +992,7 @@ Установка... Перезапуск, подождите, пожалуйста... Все готово, сейчас браузер перезагрузит страницу, подождите, пожалуйста... + Пожалуйста, нажмите кнопку 'finish' для завершения установки и перезагрузки страницы. Вставить, полностью сохранив форматирование (не рекомендуется) @@ -1119,9 +1119,9 @@ Статистика Перевод Пользователи - Добавить значок + Добавить значок в качестве родительского типа. Вкладки родительского типа не показаны и могут быть изменены непосредственно в родительском типе Родительский тип контента разрешен Данный тип контента использует @@ -1178,6 +1178,7 @@ Вкладка с идентификатором (id): %0% удалена Документ скрыт (публикация отменена) Стиль CSS не сохранен + При сохранении файла произошла ошибка. Стиль CSS сохранен Стиль CSS сохранен без ошибок Тип данных сохранен @@ -1202,9 +1203,13 @@ Файл не может быть сохранен. Пожалуйста, проверьте установки файловых разрешений Файл сохранен Файл сохранен без ошибок + У текущего пользователя недостаточно прав, невозможно завершить операцию Язык сохранен Тип медиа сохранен Тип участника сохранен + Отменено + Операция отменена установленным сторонним расширением или блоком кода + Ошибка Представление не сохранено Произошла ошибка при сохранении файла Представление сохранено @@ -1213,10 +1218,16 @@ Cкрипт Python не может быть сохранен в связи с ошибками Cкрипт Python сохранен Cкрипт Python сохранен без ошибок + Скрипт не сохранен + При сохранении файла скрипта произошла ошибка + Скрипт сохранен + Файл скрипта сохранен без ошибок Шаблон не сохранен Пожалуйста, проверьте, что нет двух шаблонов с одним и тем же алиасом (названием) Шаблон сохранен Шаблон сохранен без ошибок + Проверка значений + Ошибки, найденные при проверке значений, должны быть исправлены, чтобы было возможно сохранить документ XSLT-документ не сохранен XSLT-документ содержит одну или несколько ошибок XSLT-документ не может быть сохранен, проверьте установки файловых разрешений @@ -1343,6 +1354,7 @@ Пакеты дополнений Пакеты дополнений Скрипты Python + Типы связей Установить из репозитория Установить Runway Модули Runway @@ -1361,6 +1373,7 @@ Администратор Поле категории + Изменить Изменить пароль Вы можете сменить свой пароль для доступа к административной панели Umbraco, заполнив нижеследующие поля и нажав на кнопку 'Изменить пароль' Подтверждение нового пароля From 68702b98f5de4bb7b07993537791f9a859685b8f Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 27 Mar 2017 08:52:10 +0200 Subject: [PATCH 12/18] U4-9604 - add nullcheck --- .../ObjectResolution/ManyObjectsResolverBase.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs index e54e6f550b..ad35b81ffb 100644 --- a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs +++ b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs @@ -156,7 +156,16 @@ namespace Umbraco.Core.ObjectResolution /// Gets or sets the used to initialize this object, if any. /// /// If not null, then LifetimeScope will be ObjectLifetimeScope.HttpRequest. - protected HttpContextBase CurrentHttpContext { get { return _httpContextGetter == null ? null : _httpContextGetter(); } } + protected HttpContextBase CurrentHttpContext + { + get + { + var context = _httpContextGetter == null ? null : _httpContextGetter(); + if (context == null) + throw new InvalidOperationException("Cannot use this resolver with lifetime 'HttpRequest' when there is no current HttpContext. Either use the ctor accepting an HttpContextBase, or use the resolver from within a request exclusively."); + return context; + } + } /// /// Returns the service provider used to instantiate objects From 8f6b728534c4a4a288e08db1396d3184b3d6d0e1 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 28 Mar 2017 13:37:10 +1100 Subject: [PATCH 13/18] Moves paging value to a constant --- src/UmbracoExamine/UmbracoContentIndexer.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/UmbracoExamine/UmbracoContentIndexer.cs index 0cd8f2ee33..01025ed75d 100644 --- a/src/UmbracoExamine/UmbracoContentIndexer.cs +++ b/src/UmbracoExamine/UmbracoContentIndexer.cs @@ -36,6 +36,7 @@ namespace UmbracoExamine private readonly IUserService _userService; private readonly IContentTypeService _contentTypeService; private readonly EntityXmlSerializer _serializer = new EntityXmlSerializer(); + private const int PageSize = 2; #region Constructors @@ -407,8 +408,7 @@ namespace UmbracoExamine { if (SupportedTypes.Contains(type) == false) return; - - const int pageSize = 10000; + var pageIndex = 0; DataService.LogService.AddInfoLog(-1, string.Format("PerformIndexAll - Start data queries - {0}", type)); @@ -498,13 +498,13 @@ namespace UmbracoExamine IContent[] descendants; if (SupportUnpublishedContent) { - descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "umbracoNode.id").ToArray(); + descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, PageSize, out total, "umbracoNode.id").ToArray(); } else { //get all paged records but order by level ascending, we need to do this because we need to track which nodes are not published so that we can determine // which descendent nodes are implicitly not published - descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "level", Direction.Ascending, true, (string)null).ToArray(); + descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, PageSize, out total, "level", Direction.Ascending, true, (string)null).ToArray(); } // need to store decendants count before filtering, in order for loop to work correctly @@ -528,7 +528,7 @@ namespace UmbracoExamine content, notPublished).WhereNotNull(), type); pageIndex++; - } while (currentPageSize == pageSize); + } while (currentPageSize == PageSize); } break; @@ -579,7 +579,6 @@ namespace UmbracoExamine Func getContent) where TContentType: IContentTypeComposition { - const int pageSize = 10000; var pageIndex = 0; var contentTypes = getContentTypes(); @@ -593,7 +592,7 @@ namespace UmbracoExamine if (parentId == -1) { - var pagedElements = getPagedXmlEntries("-1", pageIndex, pageSize); + var pagedElements = getPagedXmlEntries("-1", pageIndex, PageSize); xElements = pagedElements.Item1; more = pagedElements.Item2; } @@ -604,7 +603,7 @@ namespace UmbracoExamine } else { - var pagedElements = getPagedXmlEntries(parent.Path, pageIndex, pageSize); + var pagedElements = getPagedXmlEntries(parent.Path, pageIndex, PageSize); xElements = pagedElements.Item1; more = pagedElements.Item2; } From 9eb415d398b6f63b1d7e1b2f2c296fea513e0f6c Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 28 Mar 2017 15:41:55 +1100 Subject: [PATCH 14/18] U4-9682 Repository's have a double nested DeepCloneRuntimeCacheProvider set which means all entities are double deep cloned in and out of the cache and ensures that PreValueCollections are not also manually deep cloned. --- src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs | 5 +++++ .../Repositories/DataTypeDefinitionRepository.cs | 2 +- src/Umbraco.Core/Persistence/RepositoryFactory.cs | 7 ++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs index 0ae721943d..14fef80f0d 100644 --- a/src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs @@ -26,6 +26,9 @@ namespace Umbraco.Core.Cache public DeepCloneRuntimeCacheProvider(IRuntimeCacheProvider innerProvider) { + if (innerProvider.GetType() == typeof(DeepCloneRuntimeCacheProvider)) + throw new InvalidOperationException("A " + typeof(DeepCloneRuntimeCacheProvider) + " cannot wrap another instance of " + typeof(DeepCloneRuntimeCacheProvider)); + InnerProvider = innerProvider; } @@ -105,9 +108,11 @@ namespace Umbraco.Core.Cache var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache if (value == null) return null; // do not store null values (backward compat) + //Clone/reset to go into the cache return CheckCloneableAndTracksChanges(value); }, timeout, isSliding, priority, removedCallback, dependentFiles); + //Clone/reset to go out of the cache return CheckCloneableAndTracksChanges(cached); } diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index 22b99ac732..d40386ebbc 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -277,7 +277,7 @@ AND umbracoNode.id <> @id", public PreValueCollection GetPreValuesCollectionByDataTypeId(int dataTypeId) { var collection = GetCachedPreValueCollection(dataTypeId); - return collection == null ? null : (PreValueCollection) collection.DeepClone(); + return collection; } public string GetPreValueAsString(int preValueId) diff --git a/src/Umbraco.Core/Persistence/RepositoryFactory.cs b/src/Umbraco.Core/Persistence/RepositoryFactory.cs index ff0f9e9028..bfb92ccf1a 100644 --- a/src/Umbraco.Core/Persistence/RepositoryFactory.cs +++ b/src/Umbraco.Core/Persistence/RepositoryFactory.cs @@ -48,7 +48,12 @@ namespace Umbraco.Core.Persistence _cacheHelper.IsolatedRuntimeCache.CacheFactory = type => { var cache = origFactory(type); - return new DeepCloneRuntimeCacheProvider(cache); + + //if the result is already a DeepCloneRuntimeCacheProvider then return it, otherwise + //wrap the result with a DeepCloneRuntimeCacheProvider + return cache is DeepCloneRuntimeCacheProvider + ? cache + : new DeepCloneRuntimeCacheProvider(cache); }; } From aca1f0d1866f45c1cfb4ddb973577d9c432bd3ed Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 28 Mar 2017 15:46:33 +1100 Subject: [PATCH 15/18] Adds comments --- .../Repositories/DataTypeDefinitionRepository.cs | 5 +++++ src/Umbraco.Web/UmbracoHelper.cs | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index d40386ebbc..b8b4ac7818 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -280,6 +280,11 @@ AND umbracoNode.id <> @id", return collection; } + /// + /// Gets a specific PreValue by its Id + /// + /// Id of the PreValue to retrieve the value from + /// PreValue as a string public string GetPreValueAsString(int preValueId) { var collections = RuntimeCache.GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + "_"); diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 3c97551eaf..5befbda7cf 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -1389,10 +1389,15 @@ namespace Umbraco.Web return test ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); } - #endregion + #endregion #region Prevalues + /// + /// Gets a specific PreValue by its Id + /// + /// Id of the PreValue to retrieve the value from + /// PreValue as a string public string GetPreValueAsString(int id) { return DataTypeService.GetPreValueAsString(id); From a9fe46da0c5f14800531792f38171e2d2d7439d6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 28 Mar 2017 15:55:45 +1100 Subject: [PATCH 16/18] makes HasContainerInPath public --- .../Repositories/ContentTypeBaseRepository.cs | 7 ++++++- .../Repositories/Interfaces/IContentTypeRepository.cs | 7 +++++++ src/Umbraco.Core/Services/ContentTypeServiceBase.cs | 9 +++++++-- src/Umbraco.Core/Services/IContentTypeService.cs | 7 +++++++ src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs | 3 ++- src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs | 2 +- 6 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index 4d401ce6f8..69890baac6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -1251,7 +1251,12 @@ WHERE cmsContentType." + aliasColumn + @" LIKE @pattern", return test; } - internal bool HasContainerInPath(string contentPath) + /// + /// Given the path of a content item, this will return true if the content item exists underneath a list view content item + /// + /// + /// + public bool HasContainerInPath(string contentPath) { var ids = contentPath.Split(',').Select(int.Parse); var sql = new Sql(@"SELECT COUNT(pk) FROM cmsContentType diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs index 61d83645b3..f118be3b76 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs @@ -8,6 +8,13 @@ namespace Umbraco.Core.Persistence.Repositories { public interface IContentTypeRepository : IContentTypeCompositionRepository { + /// + /// Given the path of a content item, this will return true if the content item exists underneath a list view content item + /// + /// + /// + bool HasContainerInPath(string contentPath); + /// /// Gets all entities of the specified query /// diff --git a/src/Umbraco.Core/Services/ContentTypeServiceBase.cs b/src/Umbraco.Core/Services/ContentTypeServiceBase.cs index 231186b99f..2067de847c 100644 --- a/src/Umbraco.Core/Services/ContentTypeServiceBase.cs +++ b/src/Umbraco.Core/Services/ContentTypeServiceBase.cs @@ -72,12 +72,17 @@ namespace Umbraco.Core.Services } - internal bool HasContainerInPath(string contentPath) + /// + /// Given the path of a content item, this will return true if the content item exists underneath a list view content item + /// + /// + /// + public bool HasContainerInPath(string contentPath) { using (var uow = UowProvider.GetUnitOfWork()) { // can use same repo for both content and media - var repository = (ContentTypeRepository) RepositoryFactory.CreateContentTypeRepository(uow); + var repository = RepositoryFactory.CreateContentTypeRepository(uow); return repository.HasContainerInPath(contentPath); } } diff --git a/src/Umbraco.Core/Services/IContentTypeService.cs b/src/Umbraco.Core/Services/IContentTypeService.cs index 2dcdf01291..46ee8520c6 100644 --- a/src/Umbraco.Core/Services/IContentTypeService.cs +++ b/src/Umbraco.Core/Services/IContentTypeService.cs @@ -11,6 +11,13 @@ namespace Umbraco.Core.Services /// public interface IContentTypeService : IService { + /// + /// Given the path of a content item, this will return true if the content item exists underneath a list view content item + /// + /// + /// + bool HasContainerInPath(string contentPath); + int CountContentTypes(); int CountMediaTypes(); diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index 2fd57b6290..cc2ee79ccb 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -88,7 +88,7 @@ namespace Umbraco.Web.Models.Mapping { // map the IsChildOfListView (this is actually if it is a descendant of a list view!) var parent = content.Parent(); - display.IsChildOfListView = parent != null && (parent.ContentType.IsContainer || ((ContentTypeService) contentTypeService).HasContainerInPath(parent.Path)); + display.IsChildOfListView = parent != null && (parent.ContentType.IsContainer || contentTypeService.HasContainerInPath(parent.Path)); //map the tree node url if (HttpContext.Current != null) @@ -214,6 +214,7 @@ namespace Umbraco.Web.Models.Mapping } if (content.HasPublishedVersion) { + //TODO: This is horribly inneficient var published = applicationContext.Services.ContentService.GetPublishedVersion(content.Id); return published.UpdateDate; } diff --git a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs index cfc254b085..867ae2f00b 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs @@ -69,7 +69,7 @@ namespace Umbraco.Web.Models.Mapping // Adapted from ContentModelMapper //map the IsChildOfListView (this is actually if it is a descendant of a list view!) var parent = media.Parent(); - display.IsChildOfListView = parent != null && (parent.ContentType.IsContainer || ((ContentTypeService) contentTypeService).HasContainerInPath(parent.Path)); + display.IsChildOfListView = parent != null && (parent.ContentType.IsContainer || contentTypeService.HasContainerInPath(parent.Path)); //map the tree node url if (HttpContext.Current != null) From ddfd07bf4a1c600a78ec78da253ead9a1cc30633 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 28 Mar 2017 16:04:06 +1100 Subject: [PATCH 17/18] Fixes count query --- .../Persistence/Repositories/ContentTypeBaseRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index 69890baac6..6bd9c2602c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -1259,7 +1259,7 @@ WHERE cmsContentType." + aliasColumn + @" LIKE @pattern", public bool HasContainerInPath(string contentPath) { var ids = contentPath.Split(',').Select(int.Parse); - var sql = new Sql(@"SELECT COUNT(pk) FROM cmsContentType + var sql = new Sql(@"SELECT COUNT(*) FROM cmsContentType INNER JOIN cmsContent ON cmsContentType.nodeId=cmsContent.contentType WHERE cmsContent.nodeId IN (@ids) AND cmsContentType.isContainer=@isContainer", new { ids, isContainer = true }); return Database.ExecuteScalar(sql) > 0; From 7cdb48b6c9c16e246dd45bb20d198786237ffd29 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 29 Mar 2017 16:35:37 +1100 Subject: [PATCH 18/18] bumps version --- build/UmbracoVersion.txt | 2 +- src/SolutionInfo.cs | 4 ++-- src/Umbraco.Core/Configuration/UmbracoVersion.cs | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index 2ff5e18029..4c7a22a69d 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,2 +1,2 @@ # Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) -7.5.11 \ No newline at end of file +7.5.12 \ No newline at end of file diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 6b17f79fd3..3129dde673 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -11,5 +11,5 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyFileVersion("7.5.11")] -[assembly: AssemblyInformationalVersion("7.5.11")] \ No newline at end of file +[assembly: AssemblyFileVersion("7.5.12")] +[assembly: AssemblyInformationalVersion("7.5.12")] \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 2fb1d9ebda..927e499d22 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration { public class UmbracoVersion { - private static readonly Version Version = new Version("7.5.11"); + private static readonly Version Version = new Version("7.5.12"); /// /// Gets the current version of Umbraco. diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 15ca616975..a11692f4c5 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -2423,9 +2423,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" True True - 7511 + 7512 / - http://localhost:7511 + http://localhost:7512 False False