From ec24ebf93c67db5214c4cde2a9cb0c9508713b61 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 11 Dec 2018 17:00:43 +0100 Subject: [PATCH 01/33] Fix media type copying and move media type service unit tests to a separate class --- src/Umbraco.Core/Models/ContentType.cs | 3 + src/Umbraco.Core/Models/ContentTypeBase.cs | 4 +- src/Umbraco.Core/Models/MediaType.cs | 26 +-- ...peServiceBaseOfTRepositoryTItemTService.cs | 4 +- .../Services/ContentTypeServiceTests.cs | 116 +---------- .../Services/MediaTypeServiceTests.cs | 195 ++++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + 7 files changed, 209 insertions(+), 140 deletions(-) create mode 100644 src/Umbraco.Tests/Services/MediaTypeServiceTests.cs diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs index e6439acade..4b9831682c 100644 --- a/src/Umbraco.Core/Models/ContentType.cs +++ b/src/Umbraco.Core/Models/ContentType.cs @@ -160,5 +160,8 @@ namespace Umbraco.Core.Models return result; } + + /// + IContentType IContentType.DeepCloneWithResetIdentities(string newAlias) => (IContentType)DeepCloneWithResetIdentities(newAlias); } } diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index caa63d7526..79c2d4218b 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -492,9 +492,9 @@ namespace Umbraco.Core.Models } } - public IContentType DeepCloneWithResetIdentities(string alias) + public ContentTypeBase DeepCloneWithResetIdentities(string alias) { - var clone = (ContentType)DeepClone(); + var clone = (ContentTypeBase)DeepClone(); clone.Alias = alias; clone.Key = Guid.Empty; foreach (var propertyGroup in clone.PropertyGroups) diff --git a/src/Umbraco.Core/Models/MediaType.cs b/src/Umbraco.Core/Models/MediaType.cs index 4ae2fd190c..83e1acfbc0 100644 --- a/src/Umbraco.Core/Models/MediaType.cs +++ b/src/Umbraco.Core/Models/MediaType.cs @@ -44,29 +44,7 @@ namespace Umbraco.Core.Models /// public override bool IsPublishing => IsPublishingConst; - /// - /// Creates a deep clone of the current entity with its identity/alias and it's property identities reset - /// - /// - public new IMediaType DeepCloneWithResetIdentities(string alias) - { - var clone = (MediaType)DeepClone(); - clone.Alias = alias; - clone.Key = Guid.Empty; - foreach (var propertyGroup in clone.PropertyGroups) - { - propertyGroup.ResetIdentity(); - propertyGroup.ResetDirtyProperties(false); - } - foreach (var propertyType in clone.PropertyTypes) - { - propertyType.ResetIdentity(); - propertyType.ResetDirtyProperties(false); - } - - clone.ResetIdentity(); - clone.ResetDirtyProperties(false); - return clone; - } + /// + IMediaType IMediaType.DeepCloneWithResetIdentities(string newAlias) => (IMediaType)DeepCloneWithResetIdentities(newAlias); } } diff --git a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs index b74abc03f7..feff81978c 100644 --- a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs @@ -594,7 +594,7 @@ namespace Umbraco.Core.Services.Implement //var originalb = (ContentTypeCompositionBase)original; // but we *know* it has to be a ContentTypeCompositionBase anyways var originalb = (ContentTypeCompositionBase) (object) original; - var clone = (TItem) originalb.DeepCloneWithResetIdentities(alias); + var clone = (TItem) (object) originalb.DeepCloneWithResetIdentities(alias); clone.Name = name; @@ -645,7 +645,7 @@ namespace Umbraco.Core.Services.Implement //var copyingb = (ContentTypeCompositionBase) copying; // but we *know* it has to be a ContentTypeCompositionBase anyways var copyingb = (ContentTypeCompositionBase) (object)copying; - copy = (TItem) copyingb.DeepCloneWithResetIdentities(alias); + copy = (TItem) (object) copyingb.DeepCloneWithResetIdentities(alias); copy.Name = copy.Name + " (copy)"; // might not be unique diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index bc0854bdb7..30067b3c5a 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -376,46 +376,6 @@ namespace Umbraco.Tests.Services Assert.IsNull(doc2.GetValue("title", "en-US")); } - [Test] - public void Deleting_Media_Type_With_Hierarchy_Of_Media_Items_Moves_Orphaned_Media_To_Recycle_Bin() - { - IMediaType contentType1 = MockedContentTypes.CreateSimpleMediaType("test1", "Test1"); - ServiceContext.MediaTypeService.Save(contentType1); - IMediaType contentType2 = MockedContentTypes.CreateSimpleMediaType("test2", "Test2"); - ServiceContext.MediaTypeService.Save(contentType2); - IMediaType contentType3 = MockedContentTypes.CreateSimpleMediaType("test3", "Test3"); - ServiceContext.MediaTypeService.Save(contentType3); - - var contentTypes = new[] { contentType1, contentType2, contentType3 }; - var parentId = -1; - - var ids = new List(); - - for (int i = 0; i < 2; i++) - { - for (var index = 0; index < contentTypes.Length; index++) - { - var contentType = contentTypes[index]; - var contentItem = MockedMedia.CreateSimpleMedia(contentType, "MyName_" + index + "_" + i, parentId); - ServiceContext.MediaService.Save(contentItem); - parentId = contentItem.Id; - - ids.Add(contentItem.Id); - } - } - - //delete the first content type, all other content of different content types should be in the recycle bin - ServiceContext.MediaTypeService.Delete(contentTypes[0]); - - var found = ServiceContext.MediaService.GetByIds(ids); - - Assert.AreEqual(4, found.Count()); - foreach (var content in found) - { - Assert.IsTrue(content.Trashed); - } - } - [Test] public void Deleting_Content_Type_With_Hierarchy_Of_Content_Items_Moves_Orphaned_Content_To_Recycle_Bin() { @@ -460,60 +420,6 @@ namespace Umbraco.Tests.Services } } - [Test] - public void Deleting_Media_Types_With_Hierarchy_Of_Media_Items_Doesnt_Raise_Trashed_Event_For_Deleted_Items() - { - MediaService.Trashed += MediaServiceOnTrashed; - - try - { - IMediaType contentType1 = MockedContentTypes.CreateSimpleMediaType("test1", "Test1"); - ServiceContext.MediaTypeService.Save(contentType1); - IMediaType contentType2 = MockedContentTypes.CreateSimpleMediaType("test2", "Test2"); - ServiceContext.MediaTypeService.Save(contentType2); - IMediaType contentType3 = MockedContentTypes.CreateSimpleMediaType("test3", "Test3"); - ServiceContext.MediaTypeService.Save(contentType3); - - var contentTypes = new[] { contentType1, contentType2, contentType3 }; - var parentId = -1; - - var ids = new List(); - - for (int i = 0; i < 2; i++) - { - for (var index = 0; index < contentTypes.Length; index++) - { - var contentType = contentTypes[index]; - var contentItem = MockedMedia.CreateSimpleMedia(contentType, "MyName_" + index + "_" + i, parentId); - ServiceContext.MediaService.Save(contentItem); - parentId = contentItem.Id; - - ids.Add(contentItem.Id); - } - } - - foreach (var contentType in contentTypes.Reverse()) - { - ServiceContext.MediaTypeService.Delete(contentType); - } - } - finally - { - MediaService.Trashed -= MediaServiceOnTrashed; - } - } - - private void MediaServiceOnTrashed(IMediaService sender, MoveEventArgs e) - { - foreach (var item in e.MoveInfoCollection) - { - //if this item doesn't exist then Fail! - var exists = ServiceContext.MediaService.GetById(item.Entity.Id); - if (exists == null) - Assert.Fail("The item doesn't exist"); - } - } - [Test] public void Deleting_Content_Types_With_Hierarchy_Of_Content_Items_Doesnt_Raise_Trashed_Event_For_Deleted_Items_1() { @@ -1078,12 +984,13 @@ namespace Umbraco.Tests.Services var metaContentType = MockedContentTypes.CreateMetaContentType(); service.Save(metaContentType); - var simpleContentType = MockedContentTypes.CreateSimpleContentType("category", "Category", metaContentType); + var simpleContentType = MockedContentTypes.CreateSimpleContentType("category", "Category", metaContentType) as IContentType; service.Save(simpleContentType); var categoryId = simpleContentType.Id; // Act var sut = simpleContentType.DeepCloneWithResetIdentities("newcategory"); + Assert.IsNotNull(sut); service.Save(sut); // Assert @@ -1115,11 +1022,12 @@ namespace Umbraco.Tests.Services var parentContentType2 = MockedContentTypes.CreateSimpleContentType("parent2", "Parent2", null, true); service.Save(parentContentType2); - var simpleContentType = MockedContentTypes.CreateSimpleContentType("category", "Category", parentContentType1, true); + var simpleContentType = MockedContentTypes.CreateSimpleContentType("category", "Category", parentContentType1, true) as IContentType; service.Save(simpleContentType); // Act var clone = simpleContentType.DeepCloneWithResetIdentities("newcategory"); + Assert.IsNotNull(clone); clone.RemoveContentType("parent1"); clone.AddContentType(parentContentType2); clone.ParentId = parentContentType2.Id; @@ -2068,22 +1976,6 @@ namespace Umbraco.Tests.Services Assert.IsNull(contentType2.Description); } - [Test] - public void Empty_Description_Is_Always_Null_After_Saving_Media_Type() - { - var service = ServiceContext.MediaTypeService; - var mediaType = MockedContentTypes.CreateSimpleMediaType("mediaType", "Media Type"); - mediaType.Description = null; - service.Save(mediaType); - - var mediaType2 = MockedContentTypes.CreateSimpleMediaType("mediaType2", "Media Type 2"); - mediaType2.Description = string.Empty; - service.Save(mediaType2); - - Assert.IsNull(mediaType.Description); - Assert.IsNull(mediaType2.Description); - } - [Test] public void Variations_In_Compositions() { diff --git a/src/Umbraco.Tests/Services/MediaTypeServiceTests.cs b/src/Umbraco.Tests/Services/MediaTypeServiceTests.cs new file mode 100644 index 0000000000..915dc5ceec --- /dev/null +++ b/src/Umbraco.Tests/Services/MediaTypeServiceTests.cs @@ -0,0 +1,195 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using NUnit.Framework; +using Umbraco.Core.Events; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; +using Umbraco.Tests.TestHelpers.Entities; +using Umbraco.Tests.Testing; + +namespace Umbraco.Tests.Services +{ + [TestFixture] + [Apartment(ApartmentState.STA)] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true)] + public class MediaTypeServiceTests : TestWithSomeContentBase + { + [Test] + public void Empty_Description_Is_Always_Null_After_Saving_Media_Type() + { + var mediaType = MockedContentTypes.CreateSimpleMediaType("mediaType", "Media Type"); + mediaType.Description = null; + ServiceContext.MediaTypeService.Save(mediaType); + + var mediaType2 = MockedContentTypes.CreateSimpleMediaType("mediaType2", "Media Type 2"); + mediaType2.Description = string.Empty; + ServiceContext.MediaTypeService.Save(mediaType2); + + Assert.IsNull(mediaType.Description); + Assert.IsNull(mediaType2.Description); + } + + [Test] + public void Deleting_Media_Type_With_Hierarchy_Of_Media_Items_Moves_Orphaned_Media_To_Recycle_Bin() + { + IMediaType contentType1 = MockedContentTypes.CreateSimpleMediaType("test1", "Test1"); + ServiceContext.MediaTypeService.Save(contentType1); + IMediaType contentType2 = MockedContentTypes.CreateSimpleMediaType("test2", "Test2"); + ServiceContext.MediaTypeService.Save(contentType2); + IMediaType contentType3 = MockedContentTypes.CreateSimpleMediaType("test3", "Test3"); + ServiceContext.MediaTypeService.Save(contentType3); + + var contentTypes = new[] { contentType1, contentType2, contentType3 }; + var parentId = -1; + + var ids = new List(); + + for (int i = 0; i < 2; i++) + { + for (var index = 0; index < contentTypes.Length; index++) + { + var contentType = contentTypes[index]; + var contentItem = MockedMedia.CreateSimpleMedia(contentType, "MyName_" + index + "_" + i, parentId); + ServiceContext.MediaService.Save(contentItem); + parentId = contentItem.Id; + + ids.Add(contentItem.Id); + } + } + + //delete the first content type, all other content of different content types should be in the recycle bin + ServiceContext.MediaTypeService.Delete(contentTypes[0]); + + var found = ServiceContext.MediaService.GetByIds(ids); + + Assert.AreEqual(4, found.Count()); + foreach (var content in found) + { + Assert.IsTrue(content.Trashed); + } + } + + [Test] + public void Deleting_Media_Types_With_Hierarchy_Of_Media_Items_Doesnt_Raise_Trashed_Event_For_Deleted_Items() + { + MediaService.Trashed += MediaServiceOnTrashed; + + try + { + IMediaType contentType1 = MockedContentTypes.CreateSimpleMediaType("test1", "Test1"); + ServiceContext.MediaTypeService.Save(contentType1); + IMediaType contentType2 = MockedContentTypes.CreateSimpleMediaType("test2", "Test2"); + ServiceContext.MediaTypeService.Save(contentType2); + IMediaType contentType3 = MockedContentTypes.CreateSimpleMediaType("test3", "Test3"); + ServiceContext.MediaTypeService.Save(contentType3); + + var contentTypes = new[] { contentType1, contentType2, contentType3 }; + var parentId = -1; + + var ids = new List(); + + for (int i = 0; i < 2; i++) + { + for (var index = 0; index < contentTypes.Length; index++) + { + var contentType = contentTypes[index]; + var contentItem = MockedMedia.CreateSimpleMedia(contentType, "MyName_" + index + "_" + i, parentId); + ServiceContext.MediaService.Save(contentItem); + parentId = contentItem.Id; + + ids.Add(contentItem.Id); + } + } + + foreach (var contentType in contentTypes.Reverse()) + { + ServiceContext.MediaTypeService.Delete(contentType); + } + } + finally + { + MediaService.Trashed -= MediaServiceOnTrashed; + } + } + + private void MediaServiceOnTrashed(IMediaService sender, MoveEventArgs e) + { + foreach (var item in e.MoveInfoCollection) + { + //if this item doesn't exist then Fail! + var exists = ServiceContext.MediaService.GetById(item.Entity.Id); + if (exists == null) + Assert.Fail("The item doesn't exist"); + } + } + + [Test] + public void Can_Copy_MediaType_By_Performing_Clone() + { + // Arrange + var mediaType = MockedContentTypes.CreateImageMediaType("Image2") as IMediaType; + ServiceContext.MediaTypeService.Save(mediaType); + + // Act + var sut = mediaType.DeepCloneWithResetIdentities("Image2_2"); + Assert.IsNotNull(sut); + ServiceContext.MediaTypeService.Save(sut); + + // Assert + Assert.That(sut.HasIdentity, Is.True); + Assert.AreEqual(mediaType.ParentId, sut.ParentId); + Assert.AreEqual(mediaType.Level, sut.Level); + Assert.AreEqual(mediaType.PropertyTypes.Count(), sut.PropertyTypes.Count()); + Assert.AreNotEqual(mediaType.Id, sut.Id); + Assert.AreNotEqual(mediaType.Key, sut.Key); + Assert.AreNotEqual(mediaType.Path, sut.Path); + Assert.AreNotEqual(mediaType.SortOrder, sut.SortOrder); + Assert.AreNotEqual(mediaType.PropertyTypes.First(x => x.Alias.Equals("umbracoFile")).Id, sut.PropertyTypes.First(x => x.Alias.Equals("umbracoFile")).Id); + Assert.AreNotEqual(mediaType.PropertyGroups.First(x => x.Name.Equals("Media")).Id, sut.PropertyGroups.First(x => x.Name.Equals("Media")).Id); + } + + [Test] + public void Can_Copy_MediaType_To_New_Parent_By_Performing_Clone() + { + // Arrange + var parentMediaType1 = MockedContentTypes.CreateSimpleMediaType("parent1", "Parent1"); + ServiceContext.MediaTypeService.Save(parentMediaType1); + var parentMediaType2 = MockedContentTypes.CreateSimpleMediaType("parent2", "Parent2", null, true); + ServiceContext.MediaTypeService.Save(parentMediaType2); + var mediaType = MockedContentTypes.CreateImageMediaType("Image2") as IMediaType; + ServiceContext.MediaTypeService.Save(mediaType); + + // Act + var clone = mediaType.DeepCloneWithResetIdentities("newcategory"); + Assert.IsNotNull(clone); + clone.RemoveContentType("parent1"); + clone.AddContentType(parentMediaType2); + clone.ParentId = parentMediaType2.Id; + ServiceContext.MediaTypeService.Save(clone); + + // Assert + Assert.That(clone.HasIdentity, Is.True); + + var clonedMediaType = ServiceContext.MediaTypeService.Get(clone.Id); + var originalMediaType = ServiceContext.MediaTypeService.Get(mediaType.Id); + + Assert.That(clonedMediaType.CompositionAliases().Any(x => x.Equals("parent2")), Is.True); + Assert.That(clonedMediaType.CompositionAliases().Any(x => x.Equals("parent1")), Is.False); + + Assert.AreEqual(clonedMediaType.Path, "-1," + parentMediaType2.Id + "," + clonedMediaType.Id); + Assert.AreEqual(clonedMediaType.PropertyTypes.Count(), originalMediaType.PropertyTypes.Count()); + + Assert.AreNotEqual(clonedMediaType.ParentId, originalMediaType.ParentId); + Assert.AreEqual(clonedMediaType.ParentId, parentMediaType2.Id); + + Assert.AreNotEqual(clonedMediaType.Id, originalMediaType.Id); + Assert.AreNotEqual(clonedMediaType.Key, originalMediaType.Key); + Assert.AreNotEqual(clonedMediaType.Path, originalMediaType.Path); + + Assert.AreNotEqual(clonedMediaType.PropertyTypes.First(x => x.Alias.StartsWith("umbracoFile")).Id, originalMediaType.PropertyTypes.First(x => x.Alias.StartsWith("umbracoFile")).Id); + Assert.AreNotEqual(clonedMediaType.PropertyGroups.First(x => x.Name.StartsWith("Media")).Id, originalMediaType.PropertyGroups.First(x => x.Name.StartsWith("Media")).Id); + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 7147cc8453..1286248b86 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -132,6 +132,7 @@ + From eda46a16d0c87f2568da6b29fb91a95117d6a36f Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 3 Jan 2019 09:27:52 +0100 Subject: [PATCH 02/33] Changed IContentType on IContent to a more simple class with lesser properties --- src/Umbraco.Core/ContentExtensions.cs | 38 +++++----- .../ContentVariationExtensions.cs | 10 +++ src/Umbraco.Core/Models/Content.cs | 20 ++--- src/Umbraco.Core/Models/IContent.cs | 2 +- src/Umbraco.Core/Models/ISimpleContentType.cs | 76 +++++++++++++++++++ .../Services/Implement/ContentService.cs | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Manifest/ManifestContentAppTests.cs | 2 +- src/Umbraco.Tests/Models/ContentTests.cs | 29 ++----- .../Mapping/ContentWebModelMappingTests.cs | 16 +++- src/Umbraco.Tests/Models/VariationTests.cs | 10 ++- .../Repositories/DocumentRepositoryTest.cs | 10 ++- .../PublishedContent/NuCacheTests.cs | 3 +- .../Scoping/ScopedNuCacheTests.cs | 3 +- .../Services/ContentServiceTests.cs | 50 ++++++------ .../ContentTypeServiceVariantsTests.cs | 23 +----- .../UmbracoExamine/IndexInitializer.cs | 4 +- .../UmbracoExamine/SearchTests.cs | 4 +- .../Web/Controllers/ContentControllerTests.cs | 10 +++ .../Umbraco/dialogs/ChangeDocType.aspx.cs | 13 ++-- src/Umbraco.Web/Editors/ContentController.cs | 3 +- .../Editors/ContentTypeController.cs | 9 ++- src/Umbraco.Web/Editors/MediaController.cs | 20 ++--- .../Models/Mapping/ContentMapperProfile.cs | 33 ++++++-- .../Mapping/ContentTypeBasicResolver.cs | 17 +++-- .../Models/Mapping/MediaMapperProfile.cs | 8 +- .../NuCache/PublishedSnapshotService.cs | 7 +- src/Umbraco.Web/umbraco.presentation/page.cs | 5 +- 28 files changed, 278 insertions(+), 150 deletions(-) create mode 100644 src/Umbraco.Core/Models/ISimpleContentType.cs diff --git a/src/Umbraco.Core/ContentExtensions.cs b/src/Umbraco.Core/ContentExtensions.cs index b15e371e87..632036a21e 100644 --- a/src/Umbraco.Core/ContentExtensions.cs +++ b/src/Umbraco.Core/ContentExtensions.cs @@ -127,7 +127,7 @@ namespace Umbraco.Core } return false; } - + /// /// Returns properties that do not belong to a group /// @@ -156,15 +156,15 @@ namespace Umbraco.Core .Contains(property.PropertyTypeId)); } - public static IContentTypeComposition GetContentType(this IContentBase contentBase) - { - if (contentBase == null) throw new ArgumentNullException(nameof(contentBase)); - - if (contentBase is IContent content) return content.ContentType; - if (contentBase is IMedia media) return media.ContentType; - if (contentBase is IMember member) return member.ContentType; - throw new NotSupportedException("Unsupported IContentBase implementation: " + contentBase.GetType().FullName + "."); - } +// public static IContentTypeComposition GetContentType(this IContentBase contentBase) +// { +// if (contentBase == null) throw new ArgumentNullException(nameof(contentBase)); +// +// if (contentBase is IContent content) return content.ContentType; +// if (contentBase is IMedia media) return media.ContentType; +// if (contentBase is IMember member) return member.ContentType; +// throw new NotSupportedException("Unsupported IContentBase implementation: " + contentBase.GetType().FullName + "."); +// } #region SetValue for setting file contents @@ -174,7 +174,7 @@ namespace Umbraco.Core /// This really is for FileUpload fields only, and should be obsoleted. For anything else, /// you need to store the file by yourself using Store and then figure out /// how to deal with auto-fill properties (if any) and thumbnails (if any) by yourself. - public static void SetValue(this IContentBase content, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) + public static void SetValue(this IContentBase content, IContentTypeService contentTypeService, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) { if (filename == null || filestream == null) return; @@ -183,24 +183,25 @@ namespace Umbraco.Core if (string.IsNullOrWhiteSpace(filename)) return; filename = filename.ToLower(); // fixme - er... why? - SetUploadFile(content, propertyTypeAlias, filename, filestream, culture, segment); + SetUploadFile(content,contentTypeService, propertyTypeAlias, filename, filestream, culture, segment); } - private static void SetUploadFile(this IContentBase content, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) + private static void SetUploadFile(this IContentBase content, IContentTypeService contentTypeService, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) { - var property = GetProperty(content, propertyTypeAlias); + var property = GetProperty(content, contentTypeService, propertyTypeAlias); var oldpath = property.GetValue(culture, segment) is string svalue ? MediaFileSystem.GetRelativePath(svalue) : null; var filepath = MediaFileSystem.StoreFile(content, property.PropertyType, filename, filestream, oldpath); property.SetValue(MediaFileSystem.GetUrl(filepath), culture, segment); } // gets or creates a property for a content item. - private static Property GetProperty(IContentBase content, string propertyTypeAlias) + private static Property GetProperty(IContentBase content, IContentTypeService contentTypeService, string propertyTypeAlias) { var property = content.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias)); if (property != null) return property; - var propertyType = content.GetContentType().CompositionPropertyTypes + var contentType = contentTypeService.Get(content.ContentTypeId); + var propertyType = contentType.CompositionPropertyTypes .FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias)); if (propertyType == null) throw new Exception("No property type exists with alias " + propertyTypeAlias + "."); @@ -226,9 +227,10 @@ namespace Umbraco.Core /// the "folder number" that was assigned to the previous file referenced by the property, /// if any. /// - public static string StoreFile(this IContentBase content, string propertyTypeAlias, string filename, Stream filestream, string filepath) + public static string StoreFile(this IContentBase content, IContentTypeService contentTypeService, string propertyTypeAlias, string filename, Stream filestream, string filepath) { - var propertyType = content.GetContentType() + var contentType = contentTypeService.Get(content.ContentTypeId); + var propertyType = contentType .CompositionPropertyTypes.FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias)); if (propertyType == null) throw new ArgumentException("Invalid property type alias " + propertyTypeAlias + "."); return MediaFileSystem.StoreFile(content, propertyType, filename, filestream, filepath); diff --git a/src/Umbraco.Core/ContentVariationExtensions.cs b/src/Umbraco.Core/ContentVariationExtensions.cs index 516192b905..d25997b5f0 100644 --- a/src/Umbraco.Core/ContentVariationExtensions.cs +++ b/src/Umbraco.Core/ContentVariationExtensions.cs @@ -9,6 +9,16 @@ namespace Umbraco.Core /// public static class ContentVariationExtensions { + /// + /// Determines whether the content type is invariant. + /// + public static bool VariesByNothing(this ISimpleContentType contentType) => contentType.Variations.VariesByNothing(); + + /// + /// Determines whether the content type varies by culture. + /// + public static bool VariesByCulture(this ISimpleContentType contentType) => contentType.Variations.VariesByCulture(); + /// /// Determines whether the content type is invariant. /// diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 3f6e387dec..3ae5b4f534 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Models [DataContract(IsReference = true)] public class Content : ContentBase, IContent { - private IContentType _contentType; + private ISimpleContentType _contentType; private ITemplate _template; private ContentScheduleCollection _schedule; private bool _published; @@ -48,7 +48,8 @@ namespace Umbraco.Core.Models public Content(string name, IContent parent, IContentType contentType, PropertyCollection properties, string culture = null) : base(name, parent, contentType, properties, culture) { - _contentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); + if (contentType == null) throw new ArgumentNullException(nameof(contentType)); + _contentType = new SimpleContentType(contentType); _publishedState = PublishedState.Unpublished; PublishedVersionId = 0; } @@ -75,7 +76,8 @@ namespace Umbraco.Core.Models public Content(string name, int parentId, IContentType contentType, PropertyCollection properties, string culture = null) : base(name, parentId, contentType, properties, culture) { - _contentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); + if (contentType == null) throw new ArgumentNullException(nameof(contentType)); + _contentType = new SimpleContentType(contentType); _publishedState = PublishedState.Unpublished; PublishedVersionId = 0; } @@ -181,7 +183,7 @@ namespace Umbraco.Core.Models /// Gets the ContentType used by this content object /// [IgnoreDataMember] - public IContentType ContentType => _contentType; + public ISimpleContentType ContentType => _contentType; /// [IgnoreDataMember] @@ -423,7 +425,7 @@ namespace Umbraco.Core.Models public void ChangeContentType(IContentType contentType) { ContentTypeId = contentType.Id; - _contentType = contentType; + _contentType = new SimpleContentType(contentType); ContentTypeBase = contentType; Properties.EnsurePropertyTypes(PropertyTypes); @@ -442,7 +444,7 @@ namespace Umbraco.Core.Models if(clearProperties) { ContentTypeId = contentType.Id; - _contentType = contentType; + _contentType = new SimpleContentType(contentType); ContentTypeBase = contentType; Properties.EnsureCleanPropertyTypes(PropertyTypes); @@ -460,8 +462,6 @@ namespace Umbraco.Core.Models if (Template != null) Template.ResetDirtyProperties(rememberDirty); - if (ContentType != null) - ContentType.ResetDirtyProperties(rememberDirty); // take care of the published state _publishedState = _published ? PublishedState.Published : PublishedState.Unpublished; @@ -502,7 +502,9 @@ namespace Umbraco.Core.Models var clonedContent = (Content)clone; //need to manually clone this since it's not settable - clonedContent._contentType = (IContentType) ContentType.DeepClone(); + clonedContent._contentType = ContentType; + + //if culture infos exist then deal with event bindings if (clonedContent._publishInfos != null) diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index a414a03d2f..aadd920995 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -69,7 +69,7 @@ namespace Umbraco.Core.Models /// /// Gets the content type of this content. /// - IContentType ContentType { get; } + ISimpleContentType ContentType { get; } /// /// Gets a value indicating whether a culture is published. diff --git a/src/Umbraco.Core/Models/ISimpleContentType.cs b/src/Umbraco.Core/Models/ISimpleContentType.cs new file mode 100644 index 0000000000..320913819a --- /dev/null +++ b/src/Umbraco.Core/Models/ISimpleContentType.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Models +{ + /// + /// Defines a ContentType, which Content is based on + /// + public interface ISimpleContentType + { + string Alias { get; } + int Id { get; } + ITemplate DefaultTemplate { get; } + ContentVariation Variations { get; } + string Icon { get; } + bool IsContainer { get; } + string Name { get; } + bool AllowedAsRoot { get; } + bool SupportsPropertyVariation(string culture, string s, bool b); + } + + public class SimpleContentType : ISimpleContentType + { + public SimpleContentType(IContentType contentType) + { + Id = contentType.Id; + Alias = contentType.Alias; + DefaultTemplate = contentType.DefaultTemplate; + Variations = contentType.Variations; + Icon = contentType.Icon; + IsContainer = contentType.IsContainer; + Icon = contentType.Icon; + Name = contentType.Name; + AllowedAsRoot = contentType.AllowedAsRoot; + } + + public string Alias { get; } + public int Id { get; } + public ITemplate DefaultTemplate { get; } + public ContentVariation Variations { get; } + public string Icon { get; } + public bool IsContainer { get; } + public string Name { get; } + public bool AllowedAsRoot { get; } + + /// + public bool SupportsPropertyVariation(string culture, string segment, bool wildcards = false) + { + // non-exact validation: can accept a 'null' culture if the property type varies + // by culture, and likewise for segment + // wildcard validation: can accept a '*' culture or segment + return Variations.ValidateVariation(culture, segment, false, wildcards, false); + } + + + protected bool Equals(SimpleContentType other) + { + return string.Equals(Alias, other.Alias) && Id == other.Id; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((SimpleContentType) obj); + } + + public override int GetHashCode() + { + unchecked + { + return ((Alias != null ? Alias.GetHashCode() : 0) * 397) ^ Id; + } + } + } +} diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 22ff9d5917..741a483bbe 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -2739,7 +2739,7 @@ namespace Umbraco.Core.Services.Implement { if (blueprint == null) throw new ArgumentNullException(nameof(blueprint)); - var contentType = blueprint.ContentType; + var contentType = _contentTypeRepository.Get(blueprint.ContentType.Id); var content = new Content(name, -1, contentType); content.Path = string.Concat(content.ParentId.ToString(), ",", content.Id); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 8ef76ade37..d83a08483a 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -404,6 +404,7 @@ + diff --git a/src/Umbraco.Tests/Manifest/ManifestContentAppTests.cs b/src/Umbraco.Tests/Manifest/ManifestContentAppTests.cs index 016eb4113a..1e02f562e3 100644 --- a/src/Umbraco.Tests/Manifest/ManifestContentAppTests.cs +++ b/src/Umbraco.Tests/Manifest/ManifestContentAppTests.cs @@ -18,7 +18,7 @@ namespace Umbraco.Tests.Manifest var contentType = Mock.Of(); Mock.Get(contentType).Setup(x => x.Alias).Returns("type1"); var content = Mock.Of(); - Mock.Get(content).Setup(x => x.ContentType).Returns(contentType); + Mock.Get(content).Setup(x => x.ContentType).Returns(new SimpleContentType(contentType)); var group1 = Mock.Of(); Mock.Get(group1).Setup(x => x.Alias).Returns("group1"); diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs index d31fcbf744..bec1b15e90 100644 --- a/src/Umbraco.Tests/Models/ContentTests.cs +++ b/src/Umbraco.Tests/Models/ContentTests.cs @@ -51,7 +51,7 @@ namespace Umbraco.Tests.Models var content = new Content("content", -1, contentType) { Id = 1, VersionId = 1 }; const string langFr = "fr-FR"; - + contentType.Variations = ContentVariation.Culture; Assert.IsFalse(content.IsPropertyDirty("CultureInfos")); //hasn't been changed @@ -69,7 +69,7 @@ namespace Umbraco.Tests.Models Thread.Sleep(500); //The "Date" wont be dirty if the test runs too fast since it will be the same date content.SetCultureName("name-fr", langFr); - Assert.IsTrue(frCultureName.IsPropertyDirty("Date")); + Assert.IsTrue(frCultureName.IsPropertyDirty("Date")); Assert.IsTrue(content.IsPropertyDirty("CultureInfos")); //it's true now since we've updated a name } @@ -83,6 +83,8 @@ namespace Umbraco.Tests.Models contentType.Variations = ContentVariation.Culture; + content.ChangeContentType(contentType); + Assert.IsFalse(content.IsPropertyDirty("PublishCultureInfos")); //hasn't been changed Thread.Sleep(500); //The "Date" wont be dirty if the test runs too fast since it will be the same date @@ -100,7 +102,7 @@ namespace Umbraco.Tests.Models Thread.Sleep(500); //The "Date" wont be dirty if the test runs too fast since it will be the same date content.SetCultureName("name-fr", langFr); content.PublishCulture(langFr); //we've set the name, now we're publishing it - Assert.IsTrue(frCultureName.IsPropertyDirty("Date")); + Assert.IsTrue(frCultureName.IsPropertyDirty("Date")); Assert.IsTrue(content.IsPropertyDirty("PublishCultureInfos")); //it's true now since we've updated a name } @@ -305,7 +307,7 @@ namespace Umbraco.Tests.Models content.UpdateDate = DateTime.Now; content.WriterId = 23; - + // Act var clone = (Content)content.DeepClone(); @@ -315,20 +317,7 @@ namespace Umbraco.Tests.Models Assert.AreEqual(clone, content); Assert.AreEqual(clone.Id, content.Id); Assert.AreEqual(clone.VersionId, content.VersionId); - Assert.AreNotSame(clone.ContentType, content.ContentType); Assert.AreEqual(clone.ContentType, content.ContentType); - Assert.AreEqual(clone.ContentType.PropertyGroups.Count, content.ContentType.PropertyGroups.Count); - for (var index = 0; index < content.ContentType.PropertyGroups.Count; index++) - { - Assert.AreNotSame(clone.ContentType.PropertyGroups[index], content.ContentType.PropertyGroups[index]); - Assert.AreEqual(clone.ContentType.PropertyGroups[index], content.ContentType.PropertyGroups[index]); - } - Assert.AreEqual(clone.ContentType.PropertyTypes.Count(), content.ContentType.PropertyTypes.Count()); - for (var index = 0; index < content.ContentType.PropertyTypes.Count(); index++) - { - Assert.AreNotSame(clone.ContentType.PropertyTypes.ElementAt(index), content.ContentType.PropertyTypes.ElementAt(index)); - Assert.AreEqual(clone.ContentType.PropertyTypes.ElementAt(index), content.ContentType.PropertyTypes.ElementAt(index)); - } Assert.AreEqual(clone.ContentTypeId, content.ContentTypeId); Assert.AreEqual(clone.CreateDate, content.CreateDate); Assert.AreEqual(clone.CreatorId, content.CreatorId); @@ -402,7 +391,7 @@ namespace Umbraco.Tests.Models content.SetCultureName("Hello", "en-US"); content.SetCultureName("World", "es-ES"); content.PublishCulture("en-US"); - + var i = 200; foreach (var property in content.Properties) { @@ -420,13 +409,12 @@ namespace Umbraco.Tests.Models { Id = 88 }; - + content.Trashed = true; content.UpdateDate = DateTime.Now; content.WriterId = 23; content.Template.UpdateDate = DateTime.Now; //update a child object - content.ContentType.UpdateDate = DateTime.Now; //update a child object // Act content.ResetDirtyProperties(); @@ -466,7 +454,6 @@ namespace Umbraco.Tests.Models } //verify child objects were reset too Assert.IsTrue(content.Template.WasPropertyDirty("UpdateDate")); - Assert.IsTrue(content.ContentType.WasPropertyDirty("UpdateDate")); } [Test] diff --git a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs index 03a59584a3..d2a5faa17f 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs @@ -27,6 +27,7 @@ namespace Umbraco.Tests.Models.Mapping base.Compose(); Container.RegisterSingleton(f => Mock.Of()); + Container.RegisterSingleton(f => Mock.Of()); } [DataEditor("Test.Test", "Test", "~/Test.html")] @@ -117,7 +118,12 @@ namespace Umbraco.Tests.Models.Mapping public void To_Display_Model() { var contentType = MockedContentTypes.CreateSimpleContentType(); + var contentTypeServiceMock = Mock.Get(Current.Services.ContentTypeService); + contentTypeServiceMock.Setup(x => x.Get(contentType.Id)).Returns(() => contentType); + var content = MockedContent.CreateSimpleContent(contentType); + + FixUsers(content); // need ids for tabs @@ -146,6 +152,10 @@ namespace Umbraco.Tests.Models.Mapping { var contentType = MockedContentTypes.CreateSimpleContentType(); contentType.PropertyGroups.Clear(); + var contentTypeServiceMock = Mock.Get(Current.Services.ContentTypeService); + contentTypeServiceMock.Setup(x => x.Get(contentType.Id)).Returns(() => contentType); + + var content = new Content("Home", -1, contentType) { Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 }; var result = Mapper.Map(content); @@ -158,7 +168,7 @@ namespace Umbraco.Tests.Models.Mapping AssertBasicProperty(invariantContent, p); AssertDisplayProperty(invariantContent, p); } - + Assert.AreEqual(content.PropertyGroups.Count(), invariantContent.Tabs.Count()); } @@ -177,6 +187,10 @@ namespace Umbraco.Tests.Models.Mapping p.Id = idSeed; idSeed++; } + var contentTypeServiceMock = Mock.Get(Current.Services.ContentTypeService); + contentTypeServiceMock.Setup(x => x.Get(contentType.Id)).Returns(() => contentType); + + var content = MockedContent.CreateSimpleContent(contentType); FixUsers(content); diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs index e6f4e53d26..a5b6b71b9a 100644 --- a/src/Umbraco.Tests/Models/VariationTests.cs +++ b/src/Umbraco.Tests/Models/VariationTests.cs @@ -59,7 +59,7 @@ namespace Umbraco.Tests.Models if (x == typeof(ILocalizedTextService)) return serviceContext.LocalizationService; throw new Exception("oops"); }); - + } [Test] @@ -285,6 +285,7 @@ namespace Umbraco.Tests.Models // change - now we vary by culture contentType.Variations |= ContentVariation.Culture; propertyType.Variations |= ContentVariation.Culture; + content.ChangeContentType(contentType); // can set value // and get values @@ -401,9 +402,12 @@ namespace Umbraco.Tests.Models var content = new Content("content", -1, contentType) { Id = 1, VersionId = 1 }; // change - now we vary by culture + contentType.Variations |= ContentVariation.Culture; propertyType.Variations |= ContentVariation.Culture; + content.ChangeContentType(contentType); + Assert.Throws(() => content.SetValue("prop", "a")); // invariant = no content.SetValue("prop", "a-fr", langFr); content.SetValue("prop", "a-uk", langUk); @@ -430,7 +434,7 @@ namespace Umbraco.Tests.Models Assert.IsTrue(content.IsCultureAvailable(langUk)); Assert.IsFalse(content.IsCulturePublished(langUk)); Assert.IsNull(content.GetPublishName(langUk)); - Assert.IsNull(content.GetPublishDate(langUk)); // not published + Assert.IsNull(content.GetPublishDate(langUk)); // not published Assert.IsFalse(content.IsCultureAvailable(langEs)); Assert.IsFalse(content.IsCultureEdited(langEs)); // not avail, so... not edited @@ -438,7 +442,7 @@ namespace Umbraco.Tests.Models // not published! Assert.IsNull(content.GetPublishName(langEs)); - Assert.IsNull(content.GetPublishDate(langEs)); + Assert.IsNull(content.GetPublishDate(langEs)); // cannot test IsCultureEdited here - as that requires the content service and repository // see: ContentServiceTests.Can_SaveRead_Variations diff --git a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs index 9f84d9faf5..8827766d4a 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs @@ -302,13 +302,15 @@ namespace Umbraco.Tests.Persistence.Repositories var emptyContentType = MockedContentTypes.CreateBasicContentType(); var hasPropertiesContentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage"); + contentTypeRepository.Save(emptyContentType); + contentTypeRepository.Save(hasPropertiesContentType); + ServiceContext.FileService.SaveTemplate(hasPropertiesContentType.DefaultTemplate); // else, FK violation on contentType! var content1 = MockedContent.CreateSimpleContent(hasPropertiesContentType); var content2 = MockedContent.CreateBasicContent(emptyContentType); var content3 = MockedContent.CreateSimpleContent(hasPropertiesContentType); - contentTypeRepository.Save(emptyContentType); - contentTypeRepository.Save(hasPropertiesContentType); + repository.Save(content1); repository.Save(content2); repository.Save(content3); @@ -424,9 +426,9 @@ namespace Umbraco.Tests.Persistence.Repositories var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage2", "Textpage"); contentType.AllowedTemplates = Enumerable.Empty(); // because CreateSimpleContentType assigns one already contentType.SetDefaultTemplate(template); - var textpage = MockedContent.CreateSimpleContent(contentType); - contentTypeRepository.Save(contentType); + + var textpage = MockedContent.CreateSimpleContent(contentType); repository.Save(textpage); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index 492f1f7dc0..8e3f99c543 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -148,7 +148,8 @@ namespace Umbraco.Tests.PublishedContent new TestDefaultCultureAccessor(), dataSource, globalSettings, - new SiteDomainHelper()); + new SiteDomainHelper(), + contentTypeService); // get a snapshot, get a published content var snapshot = snapshotService.CreatePublishedSnapshot(previewToken: null); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 211bdc3cdb..045a6ad147 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -82,6 +82,7 @@ namespace Umbraco.Tests.Scoping var documentRepository = Mock.Of(); var mediaRepository = Mock.Of(); var memberRepository = Mock.Of(); + var contentTypeService = Current.Services.ContentTypeService; return new PublishedSnapshotService( options, @@ -97,7 +98,7 @@ namespace Umbraco.Tests.Scoping documentRepository, mediaRepository, memberRepository, DefaultCultureAccessor, new DatabaseDataSource(), - Container.GetInstance(), new SiteDomainHelper()); + Container.GetInstance(), new SiteDomainHelper(), contentTypeService); } protected UmbracoContext GetUmbracoContextNu(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null) diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index d3ebf703a5..0cd7207e6c 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -139,29 +139,33 @@ namespace Umbraco.Tests.Services [Test] public void Create_Content_From_Blueprint() { - var contentService = ServiceContext.ContentService; - var contentTypeService = ServiceContext.ContentTypeService; + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + var contentService = ServiceContext.ContentService; + var contentTypeService = ServiceContext.ContentTypeService; - var contentType = MockedContentTypes.CreateTextPageContentType(); - ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); - contentTypeService.Save(contentType); + var contentType = MockedContentTypes.CreateTextPageContentType(); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); + contentTypeService.Save(contentType); - var blueprint = MockedContent.CreateTextpageContent(contentType, "hello", -1); - blueprint.SetValue("title", "blueprint 1"); - blueprint.SetValue("bodyText", "blueprint 2"); - blueprint.SetValue("keywords", "blueprint 3"); - blueprint.SetValue("description", "blueprint 4"); + var blueprint = MockedContent.CreateTextpageContent(contentType, "hello", -1); + blueprint.SetValue("title", "blueprint 1"); + blueprint.SetValue("bodyText", "blueprint 2"); + blueprint.SetValue("keywords", "blueprint 3"); + blueprint.SetValue("description", "blueprint 4"); - contentService.SaveBlueprint(blueprint); + contentService.SaveBlueprint(blueprint); - var fromBlueprint = contentService.CreateContentFromBlueprint(blueprint, "hello world"); - contentService.Save(fromBlueprint); + var fromBlueprint = contentService.CreateContentFromBlueprint(blueprint, "hello world"); + contentService.Save(fromBlueprint); + + Assert.IsTrue(fromBlueprint.HasIdentity); + Assert.AreEqual("blueprint 1", fromBlueprint.Properties["title"].GetValue()); + Assert.AreEqual("blueprint 2", fromBlueprint.Properties["bodyText"].GetValue()); + Assert.AreEqual("blueprint 3", fromBlueprint.Properties["keywords"].GetValue()); + Assert.AreEqual("blueprint 4", fromBlueprint.Properties["description"].GetValue()); + } - Assert.IsTrue(fromBlueprint.HasIdentity); - Assert.AreEqual("blueprint 1", fromBlueprint.Properties["title"].GetValue()); - Assert.AreEqual("blueprint 2", fromBlueprint.Properties["bodyText"].GetValue()); - Assert.AreEqual("blueprint 3", fromBlueprint.Properties["keywords"].GetValue()); - Assert.AreEqual("blueprint 4", fromBlueprint.Properties["description"].GetValue()); } [Test] @@ -222,7 +226,7 @@ namespace Umbraco.Tests.Services c.ContentSchedule.Add(now.AddSeconds(5), null); //release in 5 seconds var r = ServiceContext.ContentService.Save(c); Assert.IsTrue(r.Success, r.Result.ToString()); - } + } else { c.ContentSchedule.Add(null, now.AddSeconds(5)); //expire in 5 seconds @@ -258,7 +262,7 @@ namespace Umbraco.Tests.Services variant.Add(c); } - + var runSched = ServiceContext.ContentService.PerformScheduledPublish( now.AddMinutes(1)).ToList(); //process anything scheduled before a minute from now @@ -742,7 +746,7 @@ namespace Umbraco.Tests.Services public void Can_Unpublish_Content_Variation() { // Arrange - + var langUk = new Language("en-GB") { IsDefault = true }; var langFr = new Language("fr-FR"); @@ -1066,7 +1070,7 @@ namespace Umbraco.Tests.Services foreach (var x in descendants) Console.WriteLine(" ".Substring(0, x.Level) + x.Id); } - + Console.WriteLine(); // publish parent & its branch @@ -1420,7 +1424,7 @@ namespace Umbraco.Tests.Services var descendants = new List(); while(page * pageSize < total) descendants.AddRange(contentService.GetPagedDescendants(content.Id, page++, pageSize, out total)); - + Assert.AreNotEqual(-20, content.ParentId); Assert.IsFalse(content.Trashed); Assert.AreEqual(3, descendants.Count); diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index c28d4f7955..8f453c6d53 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -53,6 +53,7 @@ namespace Umbraco.Tests.Services var documentRepository = Container.GetInstance(); var mediaRepository = Mock.Of(); var memberRepository = Mock.Of(); + var contentTypeService = Current.Services.ContentTypeService; return new PublishedSnapshotService( options, @@ -68,7 +69,7 @@ namespace Umbraco.Tests.Services documentRepository, mediaRepository, memberRepository, DefaultCultureAccessor, new DatabaseDataSource(), - Container.GetInstance(), new SiteDomainHelper()); + Container.GetInstance(), new SiteDomainHelper(), contentTypeService); } public class LocalServerMessenger : ServerMessengerBase @@ -173,8 +174,6 @@ namespace Umbraco.Tests.Services Assert.AreEqual("v1en", document.GetValue("value1")); Assert.AreEqual("v2", document.GetValue("value2")); - Assert.IsFalse(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture()); - Console.WriteLine(GetJson(document.Id)); AssertJsonStartsWith(document.Id, "{'properties':{'value1':[{'culture':'','seg':'','val':'v1en'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':"); @@ -192,8 +191,6 @@ namespace Umbraco.Tests.Services Assert.AreEqual("v1en", document.GetValue("value1")); Assert.AreEqual("v2", document.GetValue("value2")); - Assert.IsFalse(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture()); - Console.WriteLine(GetJson(document.Id)); AssertJsonStartsWith(document.Id, "{'properties':{'value1':[{'culture':'','seg':'','val':'v1en'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':"); @@ -210,8 +207,6 @@ namespace Umbraco.Tests.Services Assert.AreEqual("v1fr", document.GetValue("value1", "fr")); Assert.AreEqual("v2", document.GetValue("value2")); - Assert.IsTrue(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture()); - Console.WriteLine(GetJson(document.Id)); AssertJsonStartsWith(document.Id, "{'properties':{'value1':[{'culture':'en','seg':'','val':'v1en'},{'culture':'fr','seg':'','val':'v1fr'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':"); @@ -286,8 +281,6 @@ namespace Umbraco.Tests.Services Assert.AreEqual("v1", document.GetValue("value1")); Assert.AreEqual("v2", document.GetValue("value2")); - Assert.IsFalse(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture()); - Console.WriteLine(GetJson(document.Id)); AssertJsonStartsWith(document.Id, "{'properties':{'value1':[{'culture':'','seg':'','val':'v1'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':"); @@ -303,8 +296,6 @@ namespace Umbraco.Tests.Services Assert.IsNull(document.GetValue("value1", "fr")); Assert.AreEqual("v2", document.GetValue("value2")); - Assert.IsTrue(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture()); - Console.WriteLine(GetJson(document.Id)); AssertJsonStartsWith(document.Id, "{'properties':{'value1':[{'culture':'en','seg':'','val':'v1'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':"); @@ -322,8 +313,6 @@ namespace Umbraco.Tests.Services Assert.AreEqual("v1", document.GetValue("value1")); Assert.AreEqual("v2", document.GetValue("value2")); - Assert.IsFalse(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture()); - Console.WriteLine(GetJson(document.Id)); AssertJsonStartsWith(document.Id, "{'properties':{'value1':[{'culture':'','seg':'','val':'v1'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':"); @@ -399,8 +388,6 @@ namespace Umbraco.Tests.Services Assert.AreEqual("v1en", document.GetValue("value1")); Assert.AreEqual("v2", document.GetValue("value2")); - Assert.IsFalse(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture()); - Console.WriteLine(GetJson(document.Id)); AssertJsonStartsWith(document.Id, "{'properties':{'value1':[{'culture':'','seg':'','val':'v1en'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':"); @@ -417,8 +404,6 @@ namespace Umbraco.Tests.Services Assert.AreEqual("v1fr", document.GetValue("value1", "fr")); Assert.AreEqual("v2", document.GetValue("value2")); - Assert.IsTrue(document.ContentType.PropertyTypes.First(x => x.Alias == "value1").VariesByCulture()); - Console.WriteLine(GetJson(document.Id)); AssertJsonStartsWith(document.Id, "{'properties':{'value1':[{'culture':'en','seg':'','val':'v1en'},{'culture':'fr','seg':'','val':'v1fr'}],'value2':[{'culture':'','seg':'','val':'v2'}]},'cultureData':"); @@ -437,8 +422,6 @@ namespace Umbraco.Tests.Services Assert.IsNull(document.GetValue("value2", "fr")); Assert.IsNull(document.GetValue("value2")); - Assert.IsTrue(document.ContentType.PropertyTypes.First(x => x.Alias == "value2").VariesByCulture()); - Console.WriteLine(GetJson(document.Id)); AssertJsonStartsWith(document.Id, "{'properties':{'value1':[{'culture':'en','seg':'','val':'v1en'},{'culture':'fr','seg':'','val':'v1fr'}],'value2':[{'culture':'en','seg':'','val':'v2'}]},'cultureData':"); @@ -770,4 +753,4 @@ namespace Umbraco.Tests.Services "{'properties':{'value11':[{'culture':'','seg':'','val':'v11'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value31':[{'culture':'','seg':'','val':'v31'}],'value32':[{'culture':'','seg':'','val':'v32'}]},'cultureData':"); } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 4435a5e829..8565321bae 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -70,7 +70,7 @@ namespace Umbraco.Tests.UmbracoExamine m.GetCultureName(It.IsAny()) == (string)x.Attribute("nodeName") && m.Path == (string)x.Attribute("path") && m.Properties == new PropertyCollection() && - m.ContentType == Mock.Of(mt => + m.ContentType == Mock.Of(mt => mt.Icon == "test" && mt.Alias == x.Name.LocalName && mt.Id == (int)x.Attribute("nodeType")))) @@ -161,7 +161,7 @@ namespace Umbraco.Tests.UmbracoExamine if (validator == null) validator = new ContentValueSetValidator(true); - + var i = new UmbracoContentIndex( "testIndexer", new UmbracoFieldDefinitionCollection(), diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index 747bab3c6d..85100410be 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -42,7 +42,7 @@ namespace Umbraco.Tests.UmbracoExamine m.Path == (string)x.Attribute("path") && m.Properties == new PropertyCollection() && m.Published == true && - m.ContentType == Mock.Of(mt => + m.ContentType == Mock.Of(mt => mt.Icon == "test" && mt.Alias == x.Name.LocalName && mt.Id == (int)x.Attribute("nodeType")))) @@ -62,7 +62,7 @@ namespace Umbraco.Tests.UmbracoExamine { indexer.CreateIndex(); rebuilder.Populate(indexer); - + var searcher = indexer.GetSearcher(); var numberSortedCriteria = searcher.CreateQuery() diff --git a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs index 9bd00f3d96..2a2aa536aa 100644 --- a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs @@ -82,6 +82,7 @@ namespace Umbraco.Tests.Web.Controllers textService.Setup(x => x.Localize(It.IsAny(), It.IsAny(), It.IsAny>())).Returns("text"); Container.RegisterSingleton(f => Mock.Of()); + Container.RegisterSingleton(f => Mock.Of()); Container.RegisterSingleton(f => userServiceMock.Object); Container.RegisterSingleton(f => entityService.Object); Container.RegisterSingleton(f => dataTypeService.Object); @@ -308,6 +309,9 @@ namespace Umbraco.Tests.Web.Controllers contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success + var contentTypeServiceMock = Mock.Get(Current.Services.ContentTypeService); + contentTypeServiceMock.Setup(x => x.Get(content.ContentTypeId)).Returns(() => MockedContentTypes.CreateSimpleContentType()); + var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); var usersController = new ContentController(propertyEditorCollection); Container.InjectProperties(usersController); @@ -340,6 +344,9 @@ namespace Umbraco.Tests.Web.Controllers contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success + var contentTypeServiceMock = Mock.Get(Current.Services.ContentTypeService); + contentTypeServiceMock.Setup(x => x.Get(content.ContentTypeId)).Returns(() => MockedContentTypes.CreateSimpleContentType()); + var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); var usersController = new ContentController(propertyEditorCollection); Container.InjectProperties(usersController); @@ -376,6 +383,9 @@ namespace Umbraco.Tests.Web.Controllers contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success + var contentTypeServiceMock = Mock.Get(Current.Services.ContentTypeService); + contentTypeServiceMock.Setup(x => x.Get(content.ContentTypeId)).Returns(() => MockedContentTypes.CreateSimpleContentType()); + var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); var usersController = new ContentController(propertyEditorCollection); Container.InjectProperties(usersController); diff --git a/src/Umbraco.Web.UI/Umbraco/dialogs/ChangeDocType.aspx.cs b/src/Umbraco.Web.UI/Umbraco/dialogs/ChangeDocType.aspx.cs index 8111410edd..cb145ee282 100644 --- a/src/Umbraco.Web.UI/Umbraco/dialogs/ChangeDocType.aspx.cs +++ b/src/Umbraco.Web.UI/Umbraco/dialogs/ChangeDocType.aspx.cs @@ -104,8 +104,9 @@ namespace Umbraco.Web.UI.Umbraco.Dialogs { // Below root, so only include those allowed as sub-nodes for the parent var parentNode = Services.ContentService.GetById(_content.ParentId); + var parentContentType = Services.ContentTypeService.Get(parentNode.ContentTypeId); return documentTypes - .Where(x => parentNode.ContentType.AllowedContentTypes + .Where(x => parentContentType.AllowedContentTypes .Select(y => y.Id.Value) .Contains(x.Id)); } @@ -149,7 +150,8 @@ namespace Umbraco.Web.UI.Umbraco.Dialogs private void PopulatePropertyMappingWithSources() { - PropertyMappingRepeater.DataSource = GetPropertiesOfContentType(_content.ContentType); + var contentType = Services.ContentTypeService.Get(_content.ContentTypeId); + PropertyMappingRepeater.DataSource = contentType.CompositionPropertyTypes; PropertyMappingRepeater.DataBind(); } @@ -159,7 +161,7 @@ namespace Umbraco.Web.UI.Umbraco.Dialogs var contentType = GetSelectedDocumentType(); // Get properties of new document type (including any from parent types) - var properties = GetPropertiesOfContentType(contentType); + var properties = contentType.CompositionPropertyTypes; // Loop through list of source properties and populate destination options with all those of same property type foreach (RepeaterItem ri in PropertyMappingRepeater.Items) @@ -193,10 +195,7 @@ namespace Umbraco.Web.UI.Umbraco.Dialogs return Services.ContentTypeService.Get(int.Parse(NewDocumentTypeList.SelectedItem.Value)); } - private IEnumerable GetPropertiesOfContentType(IContentType contentType) - { - return contentType.CompositionPropertyTypes; - } + private void DisplayNotAvailable() { diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 55862d14b8..d0699c4ad4 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -1834,8 +1834,9 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.NotFound); } + var parentContentType = Services.ContentTypeService.Get(parent.ContentTypeId); //check if the item is allowed under this one - if (parent.ContentType.AllowedContentTypes.Select(x => x.Id).ToArray() + if (parentContentType.AllowedContentTypes.Select(x => x.Id).ToArray() .Any(x => x.Value == toMove.ContentType.Id) == false) { throw new HttpResponseException( diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index 397f6b3e9d..703e2d1e54 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -193,7 +193,7 @@ namespace Umbraco.Web.Editors ? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id : Request.CreateNotificationValidationErrorResponse(result.Exception.Message); } - + public CreatedContentTypeCollectionResult PostCreateCollection(int parentId, string collectionName, bool collectionCreateTemplate, string collectionItemName, bool collectionItemCreateTemplate, string collectionIcon, string collectionItemIcon) { // create item doctype @@ -201,7 +201,7 @@ namespace Umbraco.Web.Editors itemDocType.Name = collectionItemName; itemDocType.Alias = collectionItemName.ToSafeAlias(true); itemDocType.Icon = collectionItemIcon; - + // create item doctype template if (collectionItemCreateTemplate) { @@ -222,7 +222,7 @@ namespace Umbraco.Web.Editors { new ContentTypeSort(itemDocType.Id, 0) }; - + // create collection doctype template if (collectionCreateTemplate) { @@ -382,7 +382,8 @@ namespace Umbraco.Web.Editors return Enumerable.Empty(); } - var ids = contentItem.ContentType.AllowedContentTypes.Select(x => x.Id.Value).ToArray(); + var contentType = Services.ContentTypeService.Get(contentItem.ContentTypeId); + var ids = contentType.AllowedContentTypes.Select(x => x.Id.Value).ToArray(); if (ids.Any() == false) return Enumerable.Empty(); diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index e0ee7c85a9..1f73b0c629 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -50,9 +50,10 @@ namespace Umbraco.Web.Editors [MediaControllerControllerConfiguration] public class MediaController : ContentControllerBase { - public MediaController(PropertyEditorCollection propertyEditors) + public MediaController(PropertyEditorCollection propertyEditors, IContentTypeService contentTypeService) { _propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors)); + _contentTypeService = contentTypeService; } /// @@ -236,6 +237,7 @@ namespace Umbraco.Web.Editors private int[] _userStartNodes; private readonly PropertyEditorCollection _propertyEditors; + private readonly IContentTypeService _contentTypeService; protected int[] UserStartNodes { @@ -295,7 +297,7 @@ namespace Umbraco.Web.Editors else { //better to not use this without paging where possible, currently only the sort dialog does - children = Services.MediaService.GetPagedChildren(id, 0, int.MaxValue, out var total).ToList(); + children = Services.MediaService.GetPagedChildren(id,0, int.MaxValue, out var total).ToList(); totalChildren = children.Count; } @@ -426,7 +428,7 @@ namespace Umbraco.Web.Editors var toMove = ValidateMoveOrCopy(move); var destinationParentID = move.ParentId; var sourceParentID = toMove.ParentId; - + var moveResult = Services.MediaService.Move(toMove, move.ParentId); if (sourceParentID == destinationParentID) @@ -544,7 +546,7 @@ namespace Umbraco.Web.Editors return display; } - + /// /// Empties the recycle bin /// @@ -597,11 +599,11 @@ namespace Umbraco.Web.Editors throw; } } - + public MediaItemDisplay PostAddFolder(PostedFolder folder) { var intParentId = GetParentIdAsInt(folder.ParentId, validatePermissions:true); - + var mediaService = Services.MediaService; var f = mediaService.CreateMedia(folder.Name, intParentId, Constants.Conventions.MediaTypes.Folder); @@ -641,10 +643,10 @@ namespace Umbraco.Web.Editors //get the string json from the request string currentFolderId = result.FormData["currentFolder"]; int parentId = GetParentIdAsInt(currentFolderId, validatePermissions: true); - + var tempFiles = new PostedFiles(); var mediaService = Services.MediaService; - + //in case we pass a path with a folder in it, we will create it and upload media to it. if (result.FormData.ContainsKey("path")) { @@ -727,7 +729,7 @@ namespace Umbraco.Web.Editors if (fs == null) throw new InvalidOperationException("Could not acquire file stream"); using (fs) { - f.SetValue(Constants.Conventions.Media.File, fileName, fs); + f.SetValue(_contentTypeService, Constants.Conventions.Media.File,fileName, fs); } var saveResult = mediaService.Save(f, Security.CurrentUser.Id); diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs index 6de3bdc02c..1f078a9990 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using AutoMapper; using Umbraco.Core; @@ -29,7 +30,8 @@ namespace Umbraco.Web.Models.Mapping var creatorResolver = new CreatorResolver(userService); var actionButtonsResolver = new ActionButtonsResolver(userService, contentService); var childOfListViewResolver = new ContentChildOfListViewResolver(contentService, contentTypeService); - var contentTypeBasicResolver = new ContentTypeBasicResolver(); + var contentTypeBasicResolver = new ContentTypeBasicResolver(contentTypeService); + var allowedTemplatesResolver = new AllowedTemplatesResolver(contentTypeService); var defaultTemplateResolver = new DefaultTemplateResolver(); var variantResolver = new ContentVariantResolver(localizationService); var schedPublishReleaseDateResolver = new ScheduledPublishDateResolver(ContentScheduleAction.Release); @@ -56,10 +58,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Notifications, opt => opt.Ignore()) .ForMember(dest => dest.Errors, opt => opt.Ignore()) .ForMember(dest => dest.DocumentType, opt => opt.ResolveUsing(contentTypeBasicResolver)) - .ForMember(dest => dest.AllowedTemplates, opt => - opt.MapFrom(content => content.ContentType.AllowedTemplates - .Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false) - .ToDictionary(t => t.Alias, t => t.Name))) + .ForMember(dest => dest.AllowedTemplates, opt => opt.ResolveUsing(allowedTemplatesResolver)) .ForMember(dest => dest.AllowedActions, opt => opt.ResolveUsing(src => actionButtonsResolver.Resolve(src))) .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); @@ -140,5 +139,27 @@ namespace Umbraco.Web.Models.Mapping return source.CultureInfos.TryGetValue(culture, out var name) && !name.Name.IsNullOrWhiteSpace() ? name.Name : $"(({source.Name}))"; } } - } + + + private class AllowedTemplatesResolver : IValueResolver> + { + private readonly IContentTypeService _contentTypeService; + + public AllowedTemplatesResolver(IContentTypeService contentTypeService) + { + _contentTypeService = contentTypeService; + } + + public IDictionary Resolve(IContent source, ContentItemDisplay destination, IDictionary destMember, ResolutionContext context) + { + var contentType = _contentTypeService.Get(source.ContentTypeId); + + return contentType.AllowedTemplates + .Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false) + .ToDictionary(t => t.Alias, t => t.Name); + } + } + } + + } diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeBasicResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeBasicResolver.cs index 01ff458bd5..c219df8596 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeBasicResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeBasicResolver.cs @@ -4,6 +4,7 @@ using System.Web; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping @@ -15,6 +16,13 @@ namespace Umbraco.Web.Models.Mapping internal class ContentTypeBasicResolver : IValueResolver where TSource : IContentBase { + private readonly IContentTypeService _contentTypeService; + + public ContentTypeBasicResolver(IContentTypeService contentTypeService) + { + _contentTypeService = contentTypeService; + } + public ContentTypeBasic Resolve(TSource source, TDestination destination, ContentTypeBasic destMember, ResolutionContext context) { //TODO: We can resolve the UmbracoContext from the IValueResolver options! @@ -22,13 +30,8 @@ namespace Umbraco.Web.Models.Mapping if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null && UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) { - ContentTypeBasic contentTypeBasic; - if (source is IContent content) - contentTypeBasic = Mapper.Map(content.ContentType); - else if (source is IMedia media) - contentTypeBasic = Mapper.Map(media.ContentType); - else - throw new NotSupportedException($"Expected TSource to be IContent or IMedia, got {typeof(TSource).Name}."); + var contentType = _contentTypeService.Get(source.ContentTypeId); + var contentTypeBasic = Mapper.Map(contentType); return contentTypeBasic; } diff --git a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs index dc16ed5fe4..feaa2917fe 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs @@ -15,8 +15,7 @@ namespace Umbraco.Web.Models.Mapping /// internal class MediaMapperProfile : Profile { - public MediaMapperProfile( - TabsAndPropertiesResolver tabsAndPropertiesResolver, + public MediaMapperProfile(TabsAndPropertiesResolver tabsAndPropertiesResolver, ContentTreeNodeUrlResolver contentTreeNodeUrlResolver, MediaAppResolver mediaAppResolver, IUserService userService, @@ -24,12 +23,13 @@ namespace Umbraco.Web.Models.Mapping IDataTypeService dataTypeService, IMediaService mediaService, IMediaTypeService mediaTypeService, - ILogger logger) + ILogger logger, + IContentTypeService contentTypeService) { // create, capture, cache var mediaOwnerResolver = new OwnerResolver(userService); var childOfListViewResolver = new MediaChildOfListViewResolver(mediaService, mediaTypeService); - var mediaTypeBasicResolver = new ContentTypeBasicResolver(); + var mediaTypeBasicResolver = new ContentTypeBasicResolver(contentTypeService); //FROM IMedia TO MediaItemDisplay CreateMap() diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 4d6115f02d..4df0cf2ae7 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -43,6 +43,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IMemberRepository _memberRepository; private readonly IGlobalSettings _globalSettings; private readonly ISiteDomainHelper _siteDomainHelper; + private readonly IContentTypeService _contentTypeService; private readonly IDefaultCultureAccessor _defaultCultureAccessor; // volatile because we read it with no lock @@ -85,7 +86,7 @@ namespace Umbraco.Web.PublishedCache.NuCache ILogger logger, IScopeProvider scopeProvider, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IDefaultCultureAccessor defaultCultureAccessor, - IDataSource dataSource, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper) + IDataSource dataSource, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, IContentTypeService contentTypeService) : base(publishedSnapshotAccessor, variationContextAccessor) { //if (Interlocked.Increment(ref _singletonCheck) > 1) @@ -102,6 +103,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _defaultCultureAccessor = defaultCultureAccessor; _globalSettings = globalSettings; _siteDomainHelper = siteDomainHelper; + _contentTypeService = contentTypeService; // we always want to handle repository events, configured or not // assuming no repository event will trigger before the whole db is ready @@ -1196,7 +1198,8 @@ namespace Umbraco.Web.PublishedCache.NuCache var cultureData = new Dictionary(); // sanitize - names should be ok but ... never knows - if (content.GetContentType().VariesByCulture()) + var contentType = _contentTypeService.Get(content.ContentTypeId); + if (contentType.VariesByCulture()) { var infos = content is IContent document ? (published diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index 219e2101be..9e6c85c9ea 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -141,7 +141,7 @@ namespace umbraco _elements.Add("path", path); _elements.Add("splitpath", _splitpath); } - + /// /// Puts the properties of the node into the elements table /// @@ -328,7 +328,8 @@ namespace umbraco _creatorName = _inner.GetCreatorProfile().Name; _writerName = _inner.GetWriterProfile().Name; - _contentType = Current.PublishedContentTypeFactory.CreateContentType(_inner.ContentType); + + _contentType = Current.PublishedContentTypeFactory.CreateContentType(Current.Services.ContentTypeService.Get(_inner.ContentTypeId)); _properties = _contentType.PropertyTypes .Select(x => From 6490f57f70e07c9aba5b4fd3b7bdf40b1bbf73a8 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 3 Jan 2019 10:53:33 +0100 Subject: [PATCH 03/33] Removed commented out code. --- src/Umbraco.Core/ContentExtensions.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Umbraco.Core/ContentExtensions.cs b/src/Umbraco.Core/ContentExtensions.cs index 632036a21e..15ba6b1a22 100644 --- a/src/Umbraco.Core/ContentExtensions.cs +++ b/src/Umbraco.Core/ContentExtensions.cs @@ -156,16 +156,6 @@ namespace Umbraco.Core .Contains(property.PropertyTypeId)); } -// public static IContentTypeComposition GetContentType(this IContentBase contentBase) -// { -// if (contentBase == null) throw new ArgumentNullException(nameof(contentBase)); -// -// if (contentBase is IContent content) return content.ContentType; -// if (contentBase is IMedia media) return media.ContentType; -// if (contentBase is IMember member) return member.ContentType; -// throw new NotSupportedException("Unsupported IContentBase implementation: " + contentBase.GetType().FullName + "."); -// } - #region SetValue for setting file contents /// From 586a7a167a17d3add83e935b37203234bdabd8fe Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 11 Jan 2019 08:55:25 +0100 Subject: [PATCH 04/33] Merged v8 --- src/Umbraco.Core/ContentExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/ContentExtensions.cs b/src/Umbraco.Core/ContentExtensions.cs index 1431396d01..0e119e0aee 100644 --- a/src/Umbraco.Core/ContentExtensions.cs +++ b/src/Umbraco.Core/ContentExtensions.cs @@ -180,7 +180,7 @@ namespace Umbraco.Core private static void SetUploadFile(this IContentBase content, IContentTypeService contentTypeService, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) { - var property = GetProperty(content, contentTypeService propertyTypeAlias); + var property = GetProperty(content, contentTypeService, propertyTypeAlias); // Fixes https://github.com/umbraco/Umbraco-CMS/issues/3937 - Assigning a new file to an // existing IMedia with extension SetValue causes exception 'Illegal characters in path' From 5f3596c7d9427d178408f011031bf36ea819668c Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 16 Jan 2019 13:11:46 +0100 Subject: [PATCH 05/33] Fixes after merge of temp8 --- src/Umbraco.Core/Models/ISimpleContentType.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Umbraco.Core/Models/ISimpleContentType.cs b/src/Umbraco.Core/Models/ISimpleContentType.cs index 320913819a..679a69520c 100644 --- a/src/Umbraco.Core/Models/ISimpleContentType.cs +++ b/src/Umbraco.Core/Models/ISimpleContentType.cs @@ -15,6 +15,7 @@ namespace Umbraco.Core.Models bool IsContainer { get; } string Name { get; } bool AllowedAsRoot { get; } + bool IsElement { get; } bool SupportsPropertyVariation(string culture, string s, bool b); } @@ -31,6 +32,7 @@ namespace Umbraco.Core.Models Icon = contentType.Icon; Name = contentType.Name; AllowedAsRoot = contentType.AllowedAsRoot; + IsElement = contentType.IsElement; } public string Alias { get; } @@ -41,6 +43,7 @@ namespace Umbraco.Core.Models public bool IsContainer { get; } public string Name { get; } public bool AllowedAsRoot { get; } + public bool IsElement { get; } /// public bool SupportsPropertyVariation(string culture, string segment, bool wildcards = false) From ec8c455635e410203f7149bfe56f6df8467bbcf8 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 24 Jan 2019 11:28:27 +0100 Subject: [PATCH 06/33] Fixes for search of members + Removed fixme in member that apparently are not necessary after tags refactoring --- .../MemberValueSetValidator.cs | 4 ++-- src/Umbraco.Web/Editors/EntityController.cs | 21 +++++++------------ src/Umbraco.Web/Editors/MemberController.cs | 5 ----- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Examine/MemberValueSetValidator.cs b/src/Umbraco.Examine/MemberValueSetValidator.cs index 71de6001ce..0c585854e9 100644 --- a/src/Umbraco.Examine/MemberValueSetValidator.cs +++ b/src/Umbraco.Examine/MemberValueSetValidator.cs @@ -23,10 +23,10 @@ namespace Umbraco.Examine /// /// By default these are the member fields we index /// - public static readonly string[] DefaultMemberIndexFields = { "id", "nodeName", "updateDate", "loginName", "email" }; + public static readonly string[] DefaultMemberIndexFields = { "id", "nodeName", "updateDate", "loginName", "email", UmbracoExamineIndex.NodeKeyFieldName }; private static readonly IEnumerable ValidCategories = new[] { IndexTypes.Member }; protected override IEnumerable ValidIndexCategories => ValidCategories; - + } } diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index b25f3f5af1..392ad7a8df 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -43,12 +43,16 @@ namespace Umbraco.Web.Editors public class EntityController : UmbracoAuthorizedJsonController { private readonly ITreeService _treeService; + private readonly UmbracoTreeSearcher _treeSearcher; + private readonly SearchableTreeCollection _searchableTreeCollection; public EntityController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, - ITreeService treeService) + ITreeService treeService, SearchableTreeCollection searchableTreeCollection, UmbracoTreeSearcher treeSearcher) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) { _treeService = treeService; + _searchableTreeCollection = searchableTreeCollection; + _treeSearcher = treeSearcher; } /// @@ -69,15 +73,6 @@ namespace Umbraco.Web.Editors } } - private readonly UmbracoTreeSearcher _treeSearcher; - private readonly SearchableTreeCollection _searchableTreeCollection; - - public EntityController(SearchableTreeCollection searchableTreeCollection, UmbracoTreeSearcher treeSearcher) - { - _searchableTreeCollection = searchableTreeCollection; - _treeSearcher = treeSearcher; - } - /// /// Returns an Umbraco alias given a string /// @@ -242,7 +237,7 @@ namespace Umbraco.Web.Editors }; } - + /// /// Gets an entity by a xpath query /// @@ -932,7 +927,7 @@ namespace Umbraco.Web.Editors throw new NotSupportedException("Filtering on stylesheets is not currently supported"); return Services.FileService.GetStylesheets().Select(Mapper.Map); - + case UmbracoEntityTypes.Language: if (!postFilter.IsNullOrWhiteSpace() || (postFilterParams != null && postFilterParams.Count > 0)) @@ -993,7 +988,7 @@ namespace Umbraco.Web.Editors GetChildItemsForList(childItem, list); } - } + } #endregion } diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index 92ca648c3f..399cb13360 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -592,11 +592,6 @@ namespace Umbraco.Web.Editors if (builtInAliases.Contains(p.Alias) == false && valueMapped != null) { p.SetValue(valueMapped.GetValue()); - - // fixme/task - ok, I give up, at that point tags are dead here, until we figure it out - //p.TagChanges.Behavior = valueMapped.TagChanges.Behavior; - //p.TagChanges.Enable = valueMapped.TagChanges.Enable; - //p.TagChanges.Tags = valueMapped.TagChanges.Tags; } } } From 414f838476653170ede3266230a480078c4b29fe Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 29 Jan 2019 15:30:25 +0100 Subject: [PATCH 07/33] #3645 - Lists available languages in the preview bar (if more than 1) Reads the culture from hash instead of query and updates the hash when changing culture. --- .../components/content/edit.controller.js | 2 +- .../src/preview/preview.controller.js | 54 +++++++++++-------- .../Umbraco/Views/Preview/Index.cshtml | 11 ++++ .../Editors/BackOfficePreviewLinkModel.cs | 8 +++ .../Editors/BackOfficePreviewModel.cs | 14 +++-- src/Umbraco.Web/Editors/PreviewController.cs | 20 +++++-- .../Mvc/UmbracoViewPageOfTModel.cs | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 8 files changed, 80 insertions(+), 32 deletions(-) create mode 100644 src/Umbraco.Web/Editors/BackOfficePreviewLinkModel.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index fc7fff7e3c..9850f251f3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -785,7 +785,7 @@ // Build the correct path so both /#/ and #/ work. var query = 'id=' + content.id; if ($scope.culture) { - query += "&culture=" + $scope.culture; + query += "#?culture=" + $scope.culture; } var redirect = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + '/preview/?' + query; diff --git a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js index fe0bb28aeb..5eb985a582 100644 --- a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js @@ -68,16 +68,7 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi return; } - $scope.pageId = $location.search().id || getParameterByName("id"); - var culture = $location.search().culture || getParameterByName("culture"); - - if ($scope.pageId) { - var query = 'id=' + $scope.pageId; - if (culture) { - query += "&culture=" + culture; - } - $scope.pageUrl = "frame?" + query; - } + setPageUrl(); $scope.isOpen = false; $scope.frameLoaded = false; @@ -93,6 +84,19 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi ]; $scope.previewDevice = $scope.devices[0]; + + function setPageUrl(){ + $scope.pageId = $location.search().id || getParameterByName("id"); + var culture = $location.search().culture || getParameterByName("culture"); + + if ($scope.pageId) { + var query = 'id=' + $scope.pageId; + if (culture) { + query += "&culture=" + culture; + } + $scope.pageUrl = "frame?" + query; + } + } /*****************************************************************************/ /* Preview devices */ /*****************************************************************************/ @@ -107,28 +111,39 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi /*****************************************************************************/ $scope.exitPreview = function () { - window.top.location.href = "../preview/end?redir=%2f" + $scope.pageId; + + var culture = $location.search().culture || getParameterByName("culture"); + var relativeUrl = "/" + $scope.pageId +'?culture='+ culture; + window.top.location.href = "../preview/end?redir=" + encodeURIComponent(relativeUrl); }; $scope.onFrameLoaded = function (iframe) { $scope.frameLoaded = true; configureSignalR(iframe); - } + }; /*****************************************************************************/ - /* Panel managment */ + /* Panel management */ /*****************************************************************************/ $scope.openPreviewDevice = function () { $scope.showDevicesPreview = true; - } + }; + + /*****************************************************************************/ + /* Change culture */ + /*****************************************************************************/ + $scope.changeCulture = function (culture) { + // $scope.frameLoaded = false; + $location.search("culture", culture); + setPageUrl(); + }; }) .component('previewIFrame', { - - template: "
", + template: "
", controller: function ($element, $scope, angularHelper) { var vm = this; @@ -136,22 +151,15 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi vm.$postLink = function () { var resultFrame = $element.find("#resultFrame"); resultFrame.on("load", iframeReady); - vm.srcDelayed = vm.src; }; function iframeReady() { var iframe = $element.find("#resultFrame").get(0); - hideUmbracoPreviewBadge(iframe); angularHelper.safeApply($scope, function () { vm.onLoaded({ iframe: iframe }); }); } - function hideUmbracoPreviewBadge (iframe) { - if (iframe && iframe.contentDocument && iframe.contentDocument.getElementById("umbracoPreviewBadge")) { - iframe.contentDocument.getElementById("umbracoPreviewBadge").style.display = "none"; - } - }; }, controllerAs: "vm", diff --git a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml index fcd375da45..db9fc2d3bf 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml @@ -46,6 +46,17 @@
  • + + @if (Model.PreviewLinks != null && Model.PreviewLinks.Count() > 2) + { + foreach (var previewLink in Model.PreviewLinks) + { +
  • + @previewLink.CultureName +
  • + } + } +
  • diff --git a/src/Umbraco.Web/Editors/BackOfficePreviewLinkModel.cs b/src/Umbraco.Web/Editors/BackOfficePreviewLinkModel.cs new file mode 100644 index 0000000000..c47db0811a --- /dev/null +++ b/src/Umbraco.Web/Editors/BackOfficePreviewLinkModel.cs @@ -0,0 +1,8 @@ +namespace Umbraco.Web.Editors +{ + public class BackOfficePreviewLinkModel + { + public string CultureName { get; set; } + public string CultureId { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs b/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs index 1298575e50..89b055d9d7 100644 --- a/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs +++ b/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs @@ -1,15 +1,21 @@ -using Umbraco.Core.Configuration; +using System.Collections.Generic; +using Umbraco.Core.Configuration; using Umbraco.Web.Features; namespace Umbraco.Web.Editors { public class BackOfficePreviewModel : BackOfficeModel { - public BackOfficePreviewModel(UmbracoFeatures features, IGlobalSettings globalSettings) : base(features, globalSettings) + private readonly UmbracoFeatures _features; + public IEnumerable PreviewLinks { get; } + + public BackOfficePreviewModel(UmbracoFeatures features, IGlobalSettings globalSettings, IEnumerable previewLinks) : base(features, globalSettings) { + _features = features; + PreviewLinks = previewLinks; } - public bool DisableDevicePreview => Features.Disabled.DisableDevicePreview; - public string PreviewExtendedHeaderView => Features.Enabled.PreviewExtendedView; + public bool DisableDevicePreview => _features.Disabled.DisableDevicePreview; + public string PreviewExtendedHeaderView => _features.Enabled.PreviewExtendedView; } } diff --git a/src/Umbraco.Web/Editors/PreviewController.cs b/src/Umbraco.Web/Editors/PreviewController.cs index bd14a6c6f4..a71f963678 100644 --- a/src/Umbraco.Web/Editors/PreviewController.cs +++ b/src/Umbraco.Web/Editors/PreviewController.cs @@ -1,12 +1,13 @@ using System; +using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.UI; using Umbraco.Core; using Umbraco.Core.Configuration; +using Umbraco.Core.Services; using Umbraco.Web.Composing; using Umbraco.Web.Features; -using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; using Umbraco.Web.UI.JavaScript; @@ -21,20 +22,33 @@ namespace Umbraco.Web.Editors private readonly IGlobalSettings _globalSettings; private readonly IPublishedSnapshotService _publishedSnapshotService; private readonly UmbracoContext _umbracoContext; + private readonly ILocalizationService _localizationService; - public PreviewController(UmbracoFeatures features, IGlobalSettings globalSettings, IPublishedSnapshotService publishedSnapshotService, UmbracoContext umbracoContext) + public PreviewController( + UmbracoFeatures features, + IGlobalSettings globalSettings, + IPublishedSnapshotService publishedSnapshotService, + UmbracoContext umbracoContext, + ILocalizationService localizationService) { _features = features; _globalSettings = globalSettings; _publishedSnapshotService = publishedSnapshotService; _umbracoContext = umbracoContext; + _localizationService = localizationService; } [UmbracoAuthorize(redirectToUmbracoLogin: true)] [DisableBrowserCache] public ActionResult Index() { - var model = new BackOfficePreviewModel(_features, _globalSettings); + var availableLanguages = _localizationService.GetAllLanguages(); + var previewLinks = availableLanguages.Select(x => new BackOfficePreviewLinkModel() { + CultureName = x.CultureName, + CultureId = x.IsoCode} + ); + + var model = new BackOfficePreviewModel(_features, _globalSettings, previewLinks); if (model.PreviewExtendedHeaderView.IsNullOrWhiteSpace() == false) { diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs index a16f9661aa..748efbb27a 100644 --- a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs +++ b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs @@ -217,7 +217,7 @@ namespace Umbraco.Web.Mvc markupToInject = string.Format(Current.Configs.Settings().Content.PreviewBadge, IOHelper.ResolveUrl(SystemDirectories.Umbraco), - Server.UrlEncode(UmbracoContext.Current.HttpContext.Request.Path)); + Server.UrlEncode(UmbracoContext.Current.HttpContext.Request.Url?.PathAndQuery)); } else { diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index ccf8c49485..c9c33ceb8d 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -138,6 +138,7 @@ + From cf0095dc1c88b452e648c820be9fbda67f4871fd Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 30 Jan 2019 07:49:45 +0100 Subject: [PATCH 08/33] #3645 - removed viewmodel and uses ILanguage directly in the view --- src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml | 6 +++--- src/Umbraco.Web/Editors/BackOfficePreviewLinkModel.cs | 8 -------- src/Umbraco.Web/Editors/BackOfficePreviewModel.cs | 7 ++++--- src/Umbraco.Web/Editors/PreviewController.cs | 6 +----- src/Umbraco.Web/Umbraco.Web.csproj | 1 - 5 files changed, 8 insertions(+), 20 deletions(-) delete mode 100644 src/Umbraco.Web/Editors/BackOfficePreviewLinkModel.cs diff --git a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml index db9fc2d3bf..b656942020 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml @@ -47,12 +47,12 @@ - @if (Model.PreviewLinks != null && Model.PreviewLinks.Count() > 2) + @if (Model.Languages != null && Model.Languages.Count() > 2) { - foreach (var previewLink in Model.PreviewLinks) + foreach (var previewLink in Model.Languages) {
  • - @previewLink.CultureName + @previewLink.CultureName
  • } } diff --git a/src/Umbraco.Web/Editors/BackOfficePreviewLinkModel.cs b/src/Umbraco.Web/Editors/BackOfficePreviewLinkModel.cs deleted file mode 100644 index c47db0811a..0000000000 --- a/src/Umbraco.Web/Editors/BackOfficePreviewLinkModel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Umbraco.Web.Editors -{ - public class BackOfficePreviewLinkModel - { - public string CultureName { get; set; } - public string CultureId { get; set; } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs b/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs index 89b055d9d7..b66e432699 100644 --- a/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs +++ b/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Umbraco.Core.Configuration; +using Umbraco.Core.Models; using Umbraco.Web.Features; namespace Umbraco.Web.Editors @@ -7,12 +8,12 @@ namespace Umbraco.Web.Editors public class BackOfficePreviewModel : BackOfficeModel { private readonly UmbracoFeatures _features; - public IEnumerable PreviewLinks { get; } + public IEnumerable Languages { get; } - public BackOfficePreviewModel(UmbracoFeatures features, IGlobalSettings globalSettings, IEnumerable previewLinks) : base(features, globalSettings) + public BackOfficePreviewModel(UmbracoFeatures features, IGlobalSettings globalSettings, IEnumerable languages) : base(features, globalSettings) { _features = features; - PreviewLinks = previewLinks; + Languages = languages; } public bool DisableDevicePreview => _features.Disabled.DisableDevicePreview; diff --git a/src/Umbraco.Web/Editors/PreviewController.cs b/src/Umbraco.Web/Editors/PreviewController.cs index 2b0bee052f..5507104860 100644 --- a/src/Umbraco.Web/Editors/PreviewController.cs +++ b/src/Umbraco.Web/Editors/PreviewController.cs @@ -44,12 +44,8 @@ namespace Umbraco.Web.Editors public ActionResult Index() { var availableLanguages = _localizationService.GetAllLanguages(); - var previewLinks = availableLanguages.Select(x => new BackOfficePreviewLinkModel() { - CultureName = x.CultureName, - CultureId = x.IsoCode} - ); - var model = new BackOfficePreviewModel(_features, _globalSettings, previewLinks); + var model = new BackOfficePreviewModel(_features, _globalSettings, availableLanguages); if (model.PreviewExtendedHeaderView.IsNullOrWhiteSpace() == false) { diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 376f1fe313..91ffbe68b5 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -138,7 +138,6 @@ - From 2287c0e402e228836febdb123b7fce111421552b Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 30 Jan 2019 08:24:47 +0100 Subject: [PATCH 09/33] #3645 - remove badge again in preview mode + added spinner --- src/Umbraco.Web.UI.Client/package-lock.json | 96 +++++++++---------- .../src/preview/preview.controller.js | 16 +++- 2 files changed, 61 insertions(+), 51 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index fe657ae470..ed28a93caf 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -937,7 +937,7 @@ }, "ansi-colors": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, "requires": { @@ -955,7 +955,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, @@ -1170,7 +1170,7 @@ "array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "integrity": "sha1-42jqFfibxwaff/uJrsOmx9SsItQ=", "dev": true }, "array-sort": { @@ -1216,7 +1216,7 @@ "arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "integrity": "sha1-O7xCdd1YTMGxCAm4nU6LY6aednU=", "dev": true }, "asap": { @@ -1269,7 +1269,7 @@ "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=", "dev": true }, "asynckit": { @@ -2379,7 +2379,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -2459,7 +2459,7 @@ "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", "dev": true }, "continuable-cache": { @@ -2502,7 +2502,7 @@ "core-js": { "version": "2.5.7", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", + "integrity": "sha1-+XJgj/DOrWi4QaFqky0LGDeRgU4=", "dev": true }, "core-util-is": { @@ -3425,7 +3425,7 @@ "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "integrity": "sha1-XNAfwQFiG0LEzX9dGmYkNxbT850=", "dev": true, "requires": { "esutils": "^2.0.2" @@ -3953,7 +3953,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -3989,7 +3989,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -4153,7 +4153,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true, "optional": true } @@ -4248,7 +4248,7 @@ "eslint-scope": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", - "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "integrity": "sha1-UL8wcekzi83EMzF5Sgy1M/ATYXI=", "dev": true, "requires": { "esrecurse": "^4.1.0", @@ -4258,13 +4258,13 @@ "eslint-utils": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", + "integrity": "sha1-moUbqJ7nxGA0b5fPiTnHKYgn5RI=", "dev": true }, "eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "integrity": "sha1-PzGA+y4pEBdxastMnW1bXDSmqB0=", "dev": true }, "espree": { @@ -4287,7 +4287,7 @@ "esquery": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "integrity": "sha1-QGxRZYsfWZGl+bYrHcJbAOPlxwg=", "dev": true, "requires": { "estraverse": "^4.0.0" @@ -4296,7 +4296,7 @@ "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "integrity": "sha1-AHo7n9vCs7uH5IeeoZyS/b05Qs8=", "dev": true, "requires": { "estraverse": "^4.1.0" @@ -4356,7 +4356,7 @@ "eventemitter3": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "integrity": "sha1-CQtNbNvWRe0Qv3UNS1QHlC17oWM=", "dev": true }, "exec-buffer": { @@ -4571,7 +4571,7 @@ "fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha1-6x53OrsFbc2N8r/favWbizqTZWU=", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, "requires": { "is-number": "^2.1.0", @@ -5891,7 +5891,7 @@ "global-modules": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=", "dev": true, "requires": { "global-prefix": "^1.0.1", @@ -6461,7 +6461,7 @@ "gulp-eslint": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-5.0.0.tgz", - "integrity": "sha512-9GUqCqh85C7rP9120cpxXuZz2ayq3BZc85pCTuPJS03VQYxne0aWPIXWx6LSvsGPa3uRqtSO537vaugOh+5cXg==", + "integrity": "sha1-KiaECV93Syz3kxAmIHjFbMehK1I=", "dev": true, "requires": { "eslint": "^5.0.1", @@ -7415,7 +7415,7 @@ "has-binary2": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "integrity": "sha1-d3asYn8+p3JQz8My2rfd9eT10R0=", "dev": true, "requires": { "isarray": "2.0.1" @@ -7566,7 +7566,7 @@ "http-proxy": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "integrity": "sha1-etOElGWPhGBeL220Q230EPTlvpo=", "dev": true, "requires": { "eventemitter3": "^3.0.0", @@ -7860,7 +7860,7 @@ "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "integrity": "sha1-OV4a6EsR8mrReV5zwXN45IowFXY=", "dev": true, "requires": { "is-relative": "^1.0.0", @@ -8155,7 +8155,7 @@ "is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "integrity": "sha1-obtpNc6MXboei5dUubLcwCDiJg0=", "dev": true, "requires": { "is-unc-path": "^1.0.0" @@ -8164,7 +8164,7 @@ "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "integrity": "sha1-+xj4fOH+uSUWnJpAfBkxijIG7Yg=", "dev": true }, "is-retry-allowed": { @@ -8212,7 +8212,7 @@ "is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "integrity": "sha1-1zHoiY7QkKEsNSrS6u1Qla0yLJ0=", "dev": true, "requires": { "unc-path-regex": "^0.1.2" @@ -8369,7 +8369,7 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", "dev": true }, "json-stable-stringify-without-jsonify": { @@ -8496,7 +8496,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -9146,7 +9146,7 @@ "make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "integrity": "sha1-KbM/MSqo9UfEpeSQ9Wr87JkTOtY=", "dev": true, "requires": { "kind-of": "^6.0.2" @@ -9327,7 +9327,7 @@ "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=", "dev": true }, "minimatch": { @@ -12855,7 +12855,7 @@ "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "integrity": "sha1-KYuJ34uTsCIdv0Ia0rGx6iP8Z3c=", "dev": true }, "posix-character-classes": { @@ -13345,7 +13345,7 @@ "qjobs": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "integrity": "sha1-xF6cYYAL0IfviNfiVkI73Unl0HE=", "dev": true }, "qs": { @@ -13541,7 +13541,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -14039,7 +14039,7 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=", "dev": true }, "sax": { @@ -14226,7 +14226,7 @@ "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=", "dev": true }, "shebang-command": { @@ -14516,7 +14516,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -14796,7 +14796,7 @@ "stream-consume": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz", - "integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg==", + "integrity": "sha1-0721mMK9CugrjKx6xQsRB6eZbEg=", "dev": true }, "stream-shift": { @@ -14808,7 +14808,7 @@ "streamroller": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", - "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", + "integrity": "sha1-odG3z4PTmvsNYwSaWsv5NJO99ks=", "dev": true, "requires": { "date-format": "^1.2.0", @@ -14835,7 +14835,7 @@ "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -14850,7 +14850,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -14867,7 +14867,7 @@ "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", @@ -15409,7 +15409,7 @@ "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=", "dev": true, "requires": { "os-tmpdir": "~1.0.2" @@ -15573,7 +15573,7 @@ "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=", "dev": true, "requires": { "media-typer": "0.3.0", @@ -15615,7 +15615,7 @@ "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "integrity": "sha1-n+FTahCmZKZSZqHjzPhf02MCvJw=", "dev": true }, "unc-path-regex": { @@ -15777,13 +15777,13 @@ "upath": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "integrity": "sha1-NSVll+RqWB20eT0M5H+prr/J+r0=", "dev": true }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=", "dev": true, "requires": { "punycode": "^2.1.0" @@ -16218,7 +16218,7 @@ "ws": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "integrity": "sha1-8c+E/i1ekB686U767OeF8YeiKPI=", "dev": true, "requires": { "async-limiter": "~1.0.0", diff --git a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js index 5eb985a582..7d6584d2f1 100644 --- a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js @@ -134,9 +134,11 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi /* Change culture */ /*****************************************************************************/ $scope.changeCulture = function (culture) { - // $scope.frameLoaded = false; - $location.search("culture", culture); - setPageUrl(); + if($location.search().culture !== culture){ + $scope.frameLoaded = false; + $location.search("culture", culture); + setPageUrl(); + } }; }) @@ -155,11 +157,19 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi function iframeReady() { var iframe = $element.find("#resultFrame").get(0); + hideUmbracoPreviewBadge(iframe); angularHelper.safeApply($scope, function () { vm.onLoaded({ iframe: iframe }); + $scope.frameLoaded = true; }); } + function hideUmbracoPreviewBadge (iframe) { + if (iframe && iframe.contentDocument && iframe.contentDocument.getElementById("umbracoPreviewBadge")) { + iframe.contentDocument.getElementById("umbracoPreviewBadge").style.display = "none"; + } + }; + }, controllerAs: "vm", From a6f9598a23e41687d2d6b0f6b0e518cf830ab825 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 31 Jan 2019 09:08:51 +0100 Subject: [PATCH 10/33] Introduce IPublishedRouter --- .../TestControllerActivatorBase.cs | 2 +- .../Testing/TestingTests/MockTests.cs | 2 +- src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 2 +- .../Web/Mvc/SurfaceControllerTests.cs | 2 +- .../Models/Mapping/ContentUrlResolver.cs | 4 +- .../EnsurePublishedContentRequestAttribute.cs | 8 ++- src/Umbraco.Web/Mvc/RenderRouteHandler.cs | 2 +- .../Mvc/UmbracoVirtualNodeRouteHandler.cs | 2 +- src/Umbraco.Web/Routing/IPublishedRouter.cs | 54 +++++++++++++++++++ src/Umbraco.Web/Routing/PublishedRequest.cs | 8 +-- src/Umbraco.Web/Routing/PublishedRouter.cs | 43 +++++---------- .../Routing/UrlProviderExtensions.cs | 6 +-- src/Umbraco.Web/Runtime/WebRuntimeComposer.cs | 2 +- src/Umbraco.Web/Security/MembershipHelper.cs | 27 +++++++--- src/Umbraco.Web/Templates/TemplateRenderer.cs | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + src/Umbraco.Web/UmbracoInjectedModule.cs | 4 +- 17 files changed, 111 insertions(+), 60 deletions(-) create mode 100644 src/Umbraco.Web/Routing/IPublishedRouter.cs diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index 602b5907d8..6414ea469d 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -150,7 +150,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(UrlInfo.Url("/hello/world/1234")); - var membershipHelper = new MembershipHelper(new TestUmbracoContextAccessor(umbCtx), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), null, Mock.Of(), Mock.Of()); + var membershipHelper = new MembershipHelper(new TestUmbracoContextAccessor(umbCtx), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of()); var umbHelper = new UmbracoHelper(umbCtx, Mock.Of(), diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 575f14e818..622186a547 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -65,7 +65,7 @@ namespace Umbraco.Tests.Testing.TestingTests Mock.Of(), Mock.Of(), Mock.Of(), - new MembershipHelper(new TestUmbracoContextAccessor(umbracoContext), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), null, Mock.Of(), Mock.Of()), + new MembershipHelper(new TestUmbracoContextAccessor(umbracoContext), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of()), ServiceContext.CreatePartial()); Assert.Pass(); } diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 9616a26891..7c8d1100f8 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -213,7 +213,7 @@ namespace Umbraco.Tests.Testing // web Composition.RegisterUnique(_ => Umbraco.Web.Composing.Current.UmbracoContextAccessor); - Composition.RegisterUnique(); + Composition.RegisterUnique(); Composition.WithCollectionBuilder(); Composition.RegisterUnique(); Composition.RegisterUnique(); diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index 69f6c5d13e..ec37793c10 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -132,7 +132,7 @@ namespace Umbraco.Tests.Web.Mvc Mock.Of(), Mock.Of(), Mock.Of(), - new MembershipHelper(new TestUmbracoContextAccessor(umbracoContext), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), null, Mock.Of(), Mock.Of()), + new MembershipHelper(new TestUmbracoContextAccessor(umbracoContext), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of()), ServiceContext.CreatePartial()); var ctrl = new TestSurfaceController(umbracoContext, helper); diff --git a/src/Umbraco.Web/Models/Mapping/ContentUrlResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentUrlResolver.cs index f09638330b..42c4997d86 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentUrlResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentUrlResolver.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web.Models.Mapping internal class ContentUrlResolver : IValueResolver { private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly PublishedRouter _publishedRouter; + private readonly IPublishedRouter _publishedRouter; private readonly ILocalizationService _localizationService; private readonly ILocalizedTextService _textService; private readonly IContentService _contentService; @@ -19,7 +19,7 @@ namespace Umbraco.Web.Models.Mapping public ContentUrlResolver( IUmbracoContextAccessor umbracoContextAccessor, - PublishedRouter publishedRouter, + IPublishedRouter publishedRouter, ILocalizationService localizationService, ILocalizedTextService textService, IContentService contentService, diff --git a/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs index 62a7c48d2b..471cec6db7 100644 --- a/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs +++ b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs @@ -33,8 +33,7 @@ namespace Umbraco.Web.Mvc /// public EnsurePublishedContentRequestAttribute(UmbracoContext umbracoContext, int contentId) { - if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext)); - _umbracoContext = umbracoContext; + _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); _contentId = contentId; } @@ -63,8 +62,7 @@ namespace Umbraco.Web.Mvc /// public EnsurePublishedContentRequestAttribute(UmbracoContext umbracoContext, string dataTokenName) { - if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext)); - _umbracoContext = umbracoContext; + _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); _dataTokenName = dataTokenName; } @@ -74,7 +72,7 @@ namespace Umbraco.Web.Mvc protected UmbracoContext UmbracoContext => _umbracoContext ?? (_umbracoContext = UmbracoContext.Current); // TODO: try lazy property injection? - private PublishedRouter PublishedRouter => Core.Composing.Current.Factory.GetInstance(); + private IPublishedRouter PublishedRouter => Core.Composing.Current.Factory.GetInstance(); /// /// Exposes an UmbracoHelper diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs index 215eb39573..2c550effc4 100644 --- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs @@ -377,7 +377,7 @@ namespace Umbraco.Web.Mvc if ((request.HasTemplate == false && Features.Disabled.DisableTemplates == false) && routeDef.HasHijackedRoute == false) { - request.UpdateOnMissingTemplate(); // request will go 404 + request.UpdateToNotFound(); // request will go 404 // HandleHttpResponseStatus returns a value indicating that the request should // not be processed any further, eg because it has been redirect. then, exit. diff --git a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs index 48e16b48d7..1af434e94e 100644 --- a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs @@ -12,7 +12,7 @@ namespace Umbraco.Web.Mvc public abstract class UmbracoVirtualNodeRouteHandler : IRouteHandler { // TODO: try lazy property injection? - private PublishedRouter PublishedRouter => Core.Composing.Current.Factory.GetInstance(); + private IPublishedRouter PublishedRouter => Current.Factory.GetInstance(); /// /// Returns the UmbracoContext for this route handler diff --git a/src/Umbraco.Web/Routing/IPublishedRouter.cs b/src/Umbraco.Web/Routing/IPublishedRouter.cs new file mode 100644 index 0000000000..df06e42e55 --- /dev/null +++ b/src/Umbraco.Web/Routing/IPublishedRouter.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Web.Routing +{ + /// + /// Routes requests. + /// + public interface IPublishedRouter + { + // TODO: consider this and RenderRouteHandler - move some code around? + + /// + /// Creates a published request. + /// + /// The current Umbraco context. + /// The (optional) request Uri. + /// A published request. + PublishedRequest CreateRequest(UmbracoContext umbracoContext, Uri uri = null); + + /// + /// Prepares a request for rendering. + /// + /// The request. + /// A value indicating whether the request was successfully prepared and can be rendered. + bool PrepareRequest(PublishedRequest request); + + /// + /// Tries to route a request. + /// + /// The request. + /// A value indicating whether the request can be routed to a document. + bool TryRouteRequest(PublishedRequest request); + + /// + /// Gets a template. + /// + /// The template alias + /// The template. + ITemplate GetTemplate(string alias); + + /// + /// Updates the request to "not found". + /// + /// The request. + /// + /// This method is invoked when the pipeline decides it cannot render + /// the request, for whatever reason, and wants to force it to be re-routed + /// and rendered as if no document were found (404). + /// + void UpdateRequestToNotFound(PublishedRequest request); + } +} diff --git a/src/Umbraco.Web/Routing/PublishedRequest.cs b/src/Umbraco.Web/Routing/PublishedRequest.cs index c5475f8b73..52f5820744 100644 --- a/src/Umbraco.Web/Routing/PublishedRequest.cs +++ b/src/Umbraco.Web/Routing/PublishedRequest.cs @@ -17,7 +17,7 @@ namespace Umbraco.Web.Routing /// public class PublishedRequest { - private readonly PublishedRouter _publishedRouter; + private readonly IPublishedRouter _publishedRouter; private bool _readonly; // after prepared private bool _readonlyUri; // after preparing @@ -35,7 +35,7 @@ namespace Umbraco.Web.Routing /// The published router. /// The Umbraco context. /// The request Uri. - internal PublishedRequest(PublishedRouter publishedRouter, UmbracoContext umbracoContext, Uri uri = null) + internal PublishedRequest(IPublishedRouter publishedRouter, UmbracoContext umbracoContext, Uri uri = null) { UmbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); _publishedRouter = publishedRouter ?? throw new ArgumentNullException(nameof(publishedRouter)); @@ -291,11 +291,11 @@ namespace Umbraco.Web.Routing /// public bool HasTemplate => TemplateModel != null; - internal void UpdateOnMissingTemplate() + internal void UpdateToNotFound() { var __readonly = _readonly; _readonly = false; - _publishedRouter.UpdateRequestOnMissingTemplate(this); + _publishedRouter.UpdateRequestToNotFound(this); _readonly = __readonly; } diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Web/Routing/PublishedRouter.cs index 14c36198d8..7dd1ec9f1a 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Web/Routing/PublishedRouter.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.IO; using System.Web.Security; using Umbraco.Core; +using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; @@ -18,8 +19,10 @@ using Umbraco.Web.Security; namespace Umbraco.Web.Routing { - // TODO: making sense to have an interface? - public class PublishedRouter + /// + /// Provides the default implementation. + /// + public class PublishedRouter : IPublishedRouter { private readonly IWebRoutingSection _webRoutingSection; private readonly ContentFinderCollection _contentFinders; @@ -47,15 +50,9 @@ namespace Umbraco.Web.Routing _profilingLogger = proflog ?? throw new ArgumentNullException(nameof(proflog)); _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); _logger = proflog; - - GetRolesForLogin = s => Roles.Provider.GetRolesForUser(s); } - // TODO: in 7.7 this is cached in the PublishedContentRequest, which ... makes little sense - // killing it entirely, if we need cache, just implement it properly !! - // this is all so weird - public Func> GetRolesForLogin { get; } - + /// public PublishedRequest CreateRequest(UmbracoContext umbracoContext, Uri uri = null) { return new PublishedRequest(this, umbracoContext, uri ?? umbracoContext.CleanedUmbracoUrl); @@ -63,10 +60,8 @@ namespace Umbraco.Web.Routing #region Request - /// - /// Tries to route the request. - /// - internal bool TryRouteRequest(PublishedRequest request) + /// + public bool TryRouteRequest(PublishedRequest request) { // disabled - is it going to change the routing? //_pcr.OnPreparing(); @@ -96,12 +91,7 @@ namespace Umbraco.Web.Routing _variationContextAccessor.VariationContext = new VariationContext(culture); } - /// - /// Prepares the request. - /// - /// - /// Returns false if the request was not successfully prepared - /// + /// public bool PrepareRequest(PublishedRequest request) { // note - at that point the original legacy module did something do handle IIS custom 404 errors @@ -213,11 +203,8 @@ namespace Umbraco.Web.Routing return true; } - /// - /// Updates the request when there is no template to render the content. - /// - /// This is called from Mvc when there's a document to render but no template. - public void UpdateRequestOnMissingTemplate(PublishedRequest request) + /// + public void UpdateRequestToNotFound(PublishedRequest request) { // clear content var content = request.PublishedContent; @@ -386,11 +373,7 @@ namespace Umbraco.Web.Routing #region Document and template - /// - /// Gets a template. - /// - /// The template alias - /// The template. + /// public ITemplate GetTemplate(string alias) { return _services.FileService.GetTemplate(alias); @@ -607,7 +590,7 @@ namespace Umbraco.Web.Routing if (loginPageId != request.PublishedContent.Id) request.PublishedContent = request.UmbracoContext.PublishedSnapshot.Content.GetById(loginPageId); } - else if (_services.PublicAccessService.HasAccess(request.PublishedContent.Id, _services.ContentService, membershipHelper.CurrentUserName, GetRolesForLogin(membershipHelper.CurrentUserName)) == false) + else if (_services.PublicAccessService.HasAccess(request.PublishedContent.Id, _services.ContentService, membershipHelper.CurrentUserName, membershipHelper.GetCurrentUserRoles()) == false) { _logger.Debug("EnsurePublishedContentAccess: Current member has not access, redirect to error page"); var errorPageId = publicAccessAttempt.Result.NoAccessNodeId; diff --git a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs index 8db657ed30..2a840828b6 100644 --- a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.Routing /// Contains all the Urls that we can figure out (based upon domains, etc). /// public static IEnumerable GetContentUrls(this IContent content, - PublishedRouter publishedRouter, + IPublishedRouter publishedRouter, UmbracoContext umbracoContext, ILocalizationService localizationService, ILocalizedTextService textService, @@ -92,7 +92,7 @@ namespace Umbraco.Web.Routing /// private static IEnumerable GetContentUrlsByCulture(IContent content, IEnumerable cultures, - PublishedRouter publishedRouter, + IPublishedRouter publishedRouter, UmbracoContext umbracoContext, IContentService contentService, ILocalizedTextService textService, @@ -161,7 +161,7 @@ namespace Umbraco.Web.Routing return UrlInfo.Message(textService.Localize("content/parentCultureNotPublished", new[] {parent.Name}), culture); } - private static bool DetectCollision(IContent content, string url, string culture, UmbracoContext umbracoContext, PublishedRouter publishedRouter, ILocalizedTextService textService, out UrlInfo urlInfo) + private static bool DetectCollision(IContent content, string url, string culture, UmbracoContext umbracoContext, IPublishedRouter publishedRouter, ILocalizedTextService textService, out UrlInfo urlInfo) { // test for collisions on the 'main' url var uri = new Uri(url.TrimEnd('/'), UriKind.RelativeOrAbsolute); diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs b/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs index de3ca47b38..4070ee9ece 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs @@ -178,7 +178,7 @@ namespace Umbraco.Web.Runtime composition.RegisterAuto(typeof(UmbracoViewPage<>)); // register published router - composition.RegisterUnique(); + composition.RegisterUnique(); composition.Register(_ => Current.Configs.Settings().WebRouting); // register preview SignalR hub diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index e053617806..daec4bb1f7 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -31,7 +31,6 @@ namespace Umbraco.Web.Security private readonly IMemberTypeService _memberTypeService; private readonly IUserService _userService; private readonly IPublicAccessService _publicAccessService; - private readonly PublishedRouter _publishedRouter; private readonly AppCaches _appCaches; private readonly ILogger _logger; @@ -46,7 +45,6 @@ namespace Umbraco.Web.Security IMemberTypeService memberTypeService, IUserService userService, IPublicAccessService publicAccessService, - PublishedRouter publishedRouter, AppCaches appCaches, ILogger logger ) @@ -57,7 +55,6 @@ namespace Umbraco.Web.Security _memberTypeService = memberTypeService; _userService = userService; _publicAccessService = publicAccessService; - _publishedRouter = publishedRouter; _appCaches = appCaches; _logger = logger; @@ -116,7 +113,7 @@ namespace Umbraco.Web.Security { return UmbracoContext.PublishedRequest == null ? _publicAccessService.HasAccess(path, CurrentUserName, roleProvider.GetRolesForUser) - : _publicAccessService.HasAccess(path, CurrentUserName, _publishedRouter.GetRolesForLogin); + : _publicAccessService.HasAccess(path, CurrentUserName, GetUserRoles); } /// @@ -514,6 +511,24 @@ namespace Umbraco.Web.Security } #endregion + /// + /// Gets the current user's roles. + /// + /// Roles are cached per user name, at request level. + public IEnumerable GetCurrentUserRoles() + => GetUserRoles(CurrentUserName); + + /// + /// Gets a user's roles. + /// + /// Roles are cached per user name, at request level. + public IEnumerable GetUserRoles(string userName) + { + // optimize by caching per-request (v7 cached per PublishedRequest, in PublishedRouter) + var key = "Umbraco.Web.Security.MembershipHelper__Roles__" + userName; + return _appCaches.RequestCache.GetCacheItem(key, () => Roles.Provider.GetRolesForUser(userName)); + } + /// /// Returns the login status model of the currently logged in member, if no member is logged in it returns null; /// @@ -618,7 +633,7 @@ namespace Umbraco.Web.Security var provider = _membershipProvider; string username; - + if (provider.IsUmbracoMembershipProvider()) { var member = GetCurrentPersistedMember(); @@ -626,7 +641,7 @@ namespace Umbraco.Web.Security if (member == null) return false; username = member.Username; - + // If types defined, check member is of one of those types var allowTypesList = allowTypes as IList ?? allowTypes.ToList(); if (allowTypesList.Any(allowType => allowType != string.Empty)) diff --git a/src/Umbraco.Web/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs index 9279eea124..7d678b93ca 100644 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -36,7 +36,7 @@ namespace Umbraco.Web.Templates } private IFileService FileService => Current.Services.FileService; // TODO: inject - private PublishedRouter PublishedRouter => Core.Composing.Current.Factory.GetInstance(); // TODO: inject + private IPublishedRouter PublishedRouter => Core.Composing.Current.Factory.GetInstance(); // TODO: inject /// diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index f078e9d79f..709d938ba4 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -183,6 +183,7 @@ + diff --git a/src/Umbraco.Web/UmbracoInjectedModule.cs b/src/Umbraco.Web/UmbracoInjectedModule.cs index 95819e79d7..ba1a384484 100644 --- a/src/Umbraco.Web/UmbracoInjectedModule.cs +++ b/src/Umbraco.Web/UmbracoInjectedModule.cs @@ -45,7 +45,7 @@ namespace Umbraco.Web private readonly UrlProviderCollection _urlProviders; private readonly IRuntimeState _runtime; private readonly ILogger _logger; - private readonly PublishedRouter _publishedRouter; + private readonly IPublishedRouter _publishedRouter; private readonly IVariationContextAccessor _variationContextAccessor; public UmbracoInjectedModule( @@ -56,7 +56,7 @@ namespace Umbraco.Web UrlProviderCollection urlProviders, IRuntimeState runtime, ILogger logger, - PublishedRouter publishedRouter, + IPublishedRouter publishedRouter, IVariationContextAccessor variationContextAccessor) { _combinedRouteCollection = new Lazy(CreateRouteCollection); From 92d53eb527cad1faf1ce6e91ad0f49efe33d142d Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 31 Jan 2019 11:10:27 +0100 Subject: [PATCH 11/33] #3648 - Besides mculture also retain cculture in querystring on navigation --- .../src/common/services/navigation.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js index 76fcc2d1d8..d9c505aa23 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js @@ -28,7 +28,7 @@ function navigationService($routeParams, $location, $q, $timeout, $injector, eve //A list of query strings defined that when changed will not cause a reload of the route var nonRoutingQueryStrings = ["mculture", "cculture", "lq"]; - var retainedQueryStrings = ["mculture"]; + var retainedQueryStrings = ["mculture", "cculture"]; function setMode(mode) { From 34be548aec4593433d21c31d0499c22419074023 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 31 Jan 2019 10:56:09 +0000 Subject: [PATCH 12/33] Start work on adding AppSettings keys into a constants class & use new prefix of Umbraco.Core. --- build/NuSpecs/tools/install.ps1 | 2 +- src/Umbraco.Core/Composing/RegisterFactory.cs | 4 ++-- .../Configuration/GlobalSettings.cs | 6 +++--- src/Umbraco.Core/Constants-AppSettings.cs | 20 +++++++++++++++++++ .../Migrations/Upgrade/UmbracoPlan.cs | 4 ++-- .../Migrations/Upgrade/UmbracoUpgrader.cs | 4 ++-- src/Umbraco.Core/Umbraco.Core.csproj | 1 + src/Umbraco.Tests/App.config | 2 +- src/Umbraco.Tests/Runtimes/StandaloneTests.cs | 2 +- .../Security/BackOfficeCookieManagerTests.cs | 2 +- src/Umbraco.Web.UI/web.Template.config | 2 +- 11 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 src/Umbraco.Core/Constants-AppSettings.cs diff --git a/build/NuSpecs/tools/install.ps1 b/build/NuSpecs/tools/install.ps1 index 9cc7bd5b26..5ca42b54a1 100644 --- a/build/NuSpecs/tools/install.ps1 +++ b/build/NuSpecs/tools/install.ps1 @@ -54,7 +54,7 @@ if ($project) { [xml]$config = Get-Content $destinationWebConfig $config.configuration.appSettings.ChildNodes | ForEach-Object { - if($_.key -eq "umbracoConfigurationStatus") + if($_.key -eq "Umbraco.Core.ConfigurationStatus") { # The web.config has an umbraco-specific appSetting in it # don't overwrite it and let config transforms do their thing diff --git a/src/Umbraco.Core/Composing/RegisterFactory.cs b/src/Umbraco.Core/Composing/RegisterFactory.cs index 8ee6e5a94c..975de7647d 100644 --- a/src/Umbraco.Core/Composing/RegisterFactory.cs +++ b/src/Umbraco.Core/Composing/RegisterFactory.cs @@ -18,14 +18,14 @@ namespace Umbraco.Core.Composing /// Creates a new instance of the configured container. /// /// - /// To override the default LightInjectContainer, add an appSetting named umbracoContainerType with + /// To override the default LightInjectContainer, add an appSetting named umbracoRegisterType with /// a fully qualified type name to a class with a static method "Create" returning an IRegister. /// public static IRegister Create() { Type type; - var configuredTypeName = ConfigurationManager.AppSettings["umbracoRegisterType"]; + var configuredTypeName = ConfigurationManager.AppSettings[Constants.AppSettings.RegisterType]; if (configuredTypeName.IsNullOrWhiteSpace()) { // try to get the web LightInject container type, diff --git a/src/Umbraco.Core/Configuration/GlobalSettings.cs b/src/Umbraco.Core/Configuration/GlobalSettings.cs index b89a4d1daf..b9bce30cd8 100644 --- a/src/Umbraco.Core/Configuration/GlobalSettings.cs +++ b/src/Umbraco.Core/Configuration/GlobalSettings.cs @@ -161,13 +161,13 @@ namespace Umbraco.Core.Configuration { get { - return ConfigurationManager.AppSettings.ContainsKey("umbracoConfigurationStatus") - ? ConfigurationManager.AppSettings["umbracoConfigurationStatus"] + return ConfigurationManager.AppSettings.ContainsKey(Constants.AppSettings.ConfigurationStatus) + ? ConfigurationManager.AppSettings[Constants.AppSettings.ConfigurationStatus] : string.Empty; } set { - SaveSetting("umbracoConfigurationStatus", value); + SaveSetting(Constants.AppSettings.ConfigurationStatus, value); } } diff --git a/src/Umbraco.Core/Constants-AppSettings.cs b/src/Umbraco.Core/Constants-AppSettings.cs new file mode 100644 index 0000000000..800f476c2a --- /dev/null +++ b/src/Umbraco.Core/Constants-AppSettings.cs @@ -0,0 +1,20 @@ +using System; +using System.ComponentModel; + +namespace Umbraco.Core +{ + public static partial class Constants + { + /// + /// Defines the identifiers for Umbraco system nodes. + /// + public static class AppSettings + { + public const string RegisterType = "Umbraco.Core.RegisterType"; + + public const string ConfigurationStatus = "Umbraco.Core.ConfigurationStatus"; //umbracoConfigurationStatus + + + } + } +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index 9bf58c8d2f..0d0e733591 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -36,8 +36,8 @@ namespace Umbraco.Core.Migrations.Upgrade get { // no state in database yet - assume we have something in web.config that makes some sense - if (!SemVersion.TryParse(ConfigurationManager.AppSettings["umbracoConfigurationStatus"], out var currentVersion)) - throw new InvalidOperationException("Could not get current version from web.config umbracoConfigurationStatus appSetting."); + if (!SemVersion.TryParse(ConfigurationManager.AppSettings[Constants.AppSettings.ConfigurationStatus], out var currentVersion)) + throw new InvalidOperationException($"Could not get current version from web.config {Constants.AppSettings.ConfigurationStatus} appSetting."); // we currently support upgrading from 7.10.0 and later if (currentVersion < new SemVersion(7, 10)) diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoUpgrader.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoUpgrader.cs index fa29e80a6b..ac87b8c94d 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoUpgrader.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoUpgrader.cs @@ -35,8 +35,8 @@ namespace Umbraco.Core.Migrations.Upgrade public override void AfterMigrations(IScope scope, ILogger logger) { // assume we have something in web.config that makes some sense = the origin version - if (!SemVersion.TryParse(ConfigurationManager.AppSettings["umbracoConfigurationStatus"], out var originVersion)) - throw new InvalidOperationException("Could not get current version from web.config umbracoConfigurationStatus appSetting."); + if (!SemVersion.TryParse(ConfigurationManager.AppSettings[Constants.AppSettings.ConfigurationStatus], out var originVersion)) + throw new InvalidOperationException($"Could not get current version from web.config {Constants.AppSettings.ConfigurationStatus} appSetting."); // target version is the code version var targetVersion = UmbracoVersion.SemanticVersion; diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 4e6e832294..64a62d1cf0 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -285,6 +285,7 @@ + diff --git a/src/Umbraco.Tests/App.config b/src/Umbraco.Tests/App.config index 4c5bb57aa2..6607a288b5 100644 --- a/src/Umbraco.Tests/App.config +++ b/src/Umbraco.Tests/App.config @@ -4,7 +4,7 @@ - + diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index 3790a49cfc..49bc8149a9 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -52,7 +52,7 @@ namespace Umbraco.Tests.Runtimes // settings // reset the current version to 0.0.0, clear connection strings - ConfigurationManager.AppSettings["umbracoConfigurationStatus"] = ""; + ConfigurationManager.AppSettings[Constants.AppSettings.ConfigurationStatus] = ""; // FIXME: we need a better management of settings here (and, true config files?) // create the very basic and essential things we need diff --git a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs index f0409d8928..e32df610b9 100644 --- a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs +++ b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs @@ -26,7 +26,7 @@ namespace Umbraco.Tests.Security public void ShouldAuthenticateRequest_When_Not_Configured() { //should force app ctx to show not-configured - ConfigurationManager.AppSettings.Set("umbracoConfigurationStatus", ""); + ConfigurationManager.AppSettings.Set(Constants.AppSettings.ConfigurationStatus, ""); var globalSettings = TestObjects.GetGlobalSettings(); var umbracoContext = new UmbracoContext( diff --git a/src/Umbraco.Web.UI/web.Template.config b/src/Umbraco.Web.UI/web.Template.config index 2eb3551797..06c055b98e 100644 --- a/src/Umbraco.Web.UI/web.Template.config +++ b/src/Umbraco.Web.UI/web.Template.config @@ -30,7 +30,7 @@ - + From d45d0a816ef23346a9b52bc912b9162be5e8de74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 31 Jan 2019 12:58:17 +0100 Subject: [PATCH 13/33] =?UTF-8?q?V8:=20UI=20=E2=80=94=20changed=20colors?= =?UTF-8?q?=20for=20selection=20state=20for=20rows=20in=20list-view?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/less/buttons.less | 3 ++- .../less/components/umb-layout-selector.less | 4 ++-- .../src/less/components/umb-table.less | 18 ++++++++++++++++-- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/buttons.less b/src/Umbraco.Web.UI.Client/src/less/buttons.less index a246ef585e..c6a8447342 100644 --- a/src/Umbraco.Web.UI.Client/src/less/buttons.less +++ b/src/Umbraco.Web.UI.Client/src/less/buttons.less @@ -201,7 +201,8 @@ input[type="button"] { } // Made for Umbraco, 2019 .btn-selection { - .buttonBackground(@pinkLight, ligthen(@pinkLight, 20%), @blueExtraDark, @blueDark); + @btnSelectionBackgroundHover: darken(@pinkLight, 10%); + .buttonBackground(@pinkLight, @btnSelectionBackgroundHover, @blueExtraDark, @blueDark); } // Made for Umbraco, 2019, used for buttons that has to stand back. .btn-white { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less index adaf45b4b0..cf407b667f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less @@ -5,7 +5,7 @@ .umb-layout-selector__active-layout { box-sizing: border-box; - border: 1px solid transparent; + border: 1px solid @inputBorder; cursor: pointer; height: 30px; width: 30px; @@ -16,7 +16,7 @@ } .umb-layout-selector__active-layout:hover { - border-color: @gray-8; + border-color: @inputBorderFocus; } .umb-layout-selector__dropdown { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less index c1687636d3..291eef43e0 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less @@ -90,13 +90,13 @@ input.umb-table__input { .umb-table-body .umb-table-row { color: @gray-5; - border-top: 1px solid @gray-8; + border-top: 1px solid @gray-9; cursor: pointer; font-size: 14px; position: relative; min-height: 52px; &:hover { - background-color: @gray-10; + background-color: @ui-option-hover; } } @@ -151,12 +151,26 @@ input.umb-table__input { // Show checkmark when checked, hide file icon .umb-table-row--selected { + /* .umb-table-body__fileicon { display: none; } .umb-table-body__checkicon { display: inline-block; } + */ + &::before { + content: ""; + position: absolute; + z-index:1; + top: 1px; + left: 1px; + right: 1px; + bottom: 1px; + border: 2px solid @ui-selected-border; + box-shadow: 0 0 2px 0 fade(@ui-selected-border, 80%); + pointer-events: none; + } } // Table Row Styles From 69f7a1b7b9b2467346af2e7c895aea5d7e7bd37a Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 31 Jan 2019 12:05:56 +0000 Subject: [PATCH 14/33] Finish up remaining AppSetting keys as Constants --- src/Umbraco.Core/Composing/TypeFinder.cs | 2 +- src/Umbraco.Core/Configuration/CoreDebug.cs | 4 +- .../Configuration/GlobalSettings.cs | 34 ++++++++--------- .../Configuration/UmbracoVersion.cs | 2 +- src/Umbraco.Core/Constants-AppSettings.cs | 37 +++++++++++++++++++ src/Umbraco.Core/IO/SystemFiles.cs | 5 ++- src/Umbraco.Core/Models/Language.cs | 2 +- .../Persistence/UmbracoDatabaseFactory.cs | 2 +- .../Runtime/CoreRuntimeComposer.cs | 2 +- src/Umbraco.Tests/App.config | 15 ++++---- .../PublishedMediaCache.cs | 2 +- src/Umbraco.Web.UI/web.Template.config | 14 +++---- .../Runtime/WebRuntimeComponent.cs | 2 +- ...eDirectoryBackOfficeUserPasswordChecker.cs | 1 + 14 files changed, 81 insertions(+), 43 deletions(-) diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index 308d0ecfd7..5ad1e43580 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -32,7 +32,7 @@ namespace Umbraco.Core.Composing if (_assembliesAcceptingLoadExceptions != null) return _assembliesAcceptingLoadExceptions; - var s = ConfigurationManager.AppSettings["Umbraco.AssembliesAcceptingLoadExceptions"]; + var s = ConfigurationManager.AppSettings[Constants.AppSettings.AssembliesAcceptingLoadExceptions]; return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s) ? Array.Empty() : s.Split(',').Select(x => x.Trim()).ToArray(); diff --git a/src/Umbraco.Core/Configuration/CoreDebug.cs b/src/Umbraco.Core/Configuration/CoreDebug.cs index 3e39eb6db5..b24e8a3329 100644 --- a/src/Umbraco.Core/Configuration/CoreDebug.cs +++ b/src/Umbraco.Core/Configuration/CoreDebug.cs @@ -7,8 +7,8 @@ namespace Umbraco.Core.Configuration public CoreDebug() { var appSettings = System.Configuration.ConfigurationManager.AppSettings; - LogUncompletedScopes = string.Equals("true", appSettings["Umbraco.CoreDebug.LogUncompletedScopes"], StringComparison.OrdinalIgnoreCase); - DumpOnTimeoutThreadAbort = string.Equals("true", appSettings["Umbraco.CoreDebug.DumpOnTimeoutThreadAbort"], StringComparison.OrdinalIgnoreCase); + LogUncompletedScopes = string.Equals("true", appSettings[Constants.AppSettings.Debug.LogUncompletedScopes], StringComparison.OrdinalIgnoreCase); + DumpOnTimeoutThreadAbort = string.Equals("true", appSettings[Constants.AppSettings.Debug.DumpOnTimeoutThreadAbort], StringComparison.OrdinalIgnoreCase); } // when true, Scope logs the stack trace for any scope that gets disposed without being completed. diff --git a/src/Umbraco.Core/Configuration/GlobalSettings.cs b/src/Umbraco.Core/Configuration/GlobalSettings.cs index b9bce30cd8..e30bf85fd0 100644 --- a/src/Umbraco.Core/Configuration/GlobalSettings.cs +++ b/src/Umbraco.Core/Configuration/GlobalSettings.cs @@ -85,8 +85,8 @@ namespace Umbraco.Core.Configuration { if (_reservedUrls != null) return _reservedUrls; - var urls = ConfigurationManager.AppSettings.ContainsKey("umbracoReservedUrls") - ? ConfigurationManager.AppSettings["umbracoReservedUrls"] + var urls = ConfigurationManager.AppSettings.ContainsKey(Constants.AppSettings.ReservedUrls) + ? ConfigurationManager.AppSettings[Constants.AppSettings.ReservedUrls] : string.Empty; //ensure the built on (non-changeable) reserved paths are there at all times @@ -107,14 +107,14 @@ namespace Umbraco.Core.Configuration if (_reservedPaths != null) return _reservedPaths; var reservedPaths = StaticReservedPaths; - var umbPath = ConfigurationManager.AppSettings.ContainsKey("umbracoPath") && !ConfigurationManager.AppSettings["umbracoPath"].IsNullOrWhiteSpace() - ? ConfigurationManager.AppSettings["umbracoPath"] + var umbPath = ConfigurationManager.AppSettings.ContainsKey(Constants.AppSettings.Path) && !ConfigurationManager.AppSettings[Constants.AppSettings.Path].IsNullOrWhiteSpace() + ? ConfigurationManager.AppSettings[Constants.AppSettings.Path] : "~/umbraco"; //always add the umbraco path to the list reservedPaths += umbPath.EnsureEndsWith(','); - var allPaths = ConfigurationManager.AppSettings.ContainsKey("umbracoReservedPaths") - ? ConfigurationManager.AppSettings["umbracoReservedPaths"] + var allPaths = ConfigurationManager.AppSettings.ContainsKey(Constants.AppSettings.ReservedPaths) + ? ConfigurationManager.AppSettings[Constants.AppSettings.ReservedPaths] : string.Empty; _reservedPaths = reservedPaths + allPaths; @@ -133,8 +133,8 @@ namespace Umbraco.Core.Configuration { get { - return ConfigurationManager.AppSettings.ContainsKey("umbracoContentXML") - ? ConfigurationManager.AppSettings["umbracoContentXML"] + return ConfigurationManager.AppSettings.ContainsKey(Constants.AppSettings.ContentXML) + ? ConfigurationManager.AppSettings[Constants.AppSettings.ContentXML] : "~/App_Data/umbraco.config"; } } @@ -147,8 +147,8 @@ namespace Umbraco.Core.Configuration { get { - return ConfigurationManager.AppSettings.ContainsKey("umbracoPath") - ? IOHelper.ResolveUrl(ConfigurationManager.AppSettings["umbracoPath"]) + return ConfigurationManager.AppSettings.ContainsKey(Constants.AppSettings.Path) + ? IOHelper.ResolveUrl(ConfigurationManager.AppSettings[Constants.AppSettings.Path]) : string.Empty; } } @@ -249,7 +249,7 @@ namespace Umbraco.Core.Configuration { try { - return int.Parse(ConfigurationManager.AppSettings["umbracoTimeOutInMinutes"]); + return int.Parse(ConfigurationManager.AppSettings[Constants.AppSettings.TimeOutInMinutes]); } catch { @@ -268,7 +268,7 @@ namespace Umbraco.Core.Configuration { try { - return int.Parse(ConfigurationManager.AppSettings["umbracoVersionCheckPeriod"]); + return int.Parse(ConfigurationManager.AppSettings[Constants.AppSettings.VersionCheckPeriod]); } catch { @@ -287,7 +287,7 @@ namespace Umbraco.Core.Configuration { get { - var setting = ConfigurationManager.AppSettings["umbracoLocalTempStorage"]; + var setting = ConfigurationManager.AppSettings[Constants.AppSettings.LocalTempStorage]; if (!string.IsNullOrWhiteSpace(setting)) return Enum.Parse(setting); @@ -304,8 +304,8 @@ namespace Umbraco.Core.Configuration { get { - return ConfigurationManager.AppSettings.ContainsKey("umbracoDefaultUILanguage") - ? ConfigurationManager.AppSettings["umbracoDefaultUILanguage"] + return ConfigurationManager.AppSettings.ContainsKey(Constants.AppSettings.DefaultUILanguage) + ? ConfigurationManager.AppSettings[Constants.AppSettings.DefaultUILanguage] : string.Empty; } } @@ -322,7 +322,7 @@ namespace Umbraco.Core.Configuration { try { - return bool.Parse(ConfigurationManager.AppSettings["umbracoHideTopLevelNodeFromPath"]); + return bool.Parse(ConfigurationManager.AppSettings[Constants.AppSettings.HideTopLevelNodeFromPath]); } catch { @@ -340,7 +340,7 @@ namespace Umbraco.Core.Configuration { try { - return bool.Parse(ConfigurationManager.AppSettings["umbracoUseHttps"]); + return bool.Parse(ConfigurationManager.AppSettings[Constants.AppSettings.UseHttps]); } catch { diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 7ab97500f0..2f615d26b3 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -82,7 +82,7 @@ namespace Umbraco.Core.Configuration try { // TODO: https://github.com/umbraco/Umbraco-CMS/issues/4238 - stop having version in web.config appSettings - var value = ConfigurationManager.AppSettings["umbracoConfigurationStatus"]; + var value = ConfigurationManager.AppSettings[Constants.AppSettings.ConfigurationStatus]; return value.IsNullOrWhiteSpace() ? null : SemVersion.TryParse(value, out var semver) ? semver : null; } catch diff --git a/src/Umbraco.Core/Constants-AppSettings.cs b/src/Umbraco.Core/Constants-AppSettings.cs index 800f476c2a..67f92f4046 100644 --- a/src/Umbraco.Core/Constants-AppSettings.cs +++ b/src/Umbraco.Core/Constants-AppSettings.cs @@ -10,11 +10,48 @@ namespace Umbraco.Core /// public static class AppSettings { + // TODO: Kill me - still used in Umbraco.Core.IO.SystemFiles:27 + [Obsolete("We need to kill this appsetting as we do not use XML content cache umbraco.config anymore due to NuCache")] + public const string ContentXML = "Umbraco.Core.ContentXML"; //umbracoContentXML + + public const string RegisterType = "Umbraco.Core.RegisterType"; + + public const string PublishedMediaCacheSeconds = "Umbraco.Core.PublishedMediaCacheSeconds"; //"Umbraco.PublishedMediaCache.Seconds" + + public const string AssembliesAcceptingLoadExceptions = "Umbraco.Core.AssembliesAcceptingLoadExceptions"; //Umbraco.AssembliesAcceptingLoadExceptions public const string ConfigurationStatus = "Umbraco.Core.ConfigurationStatus"; //umbracoConfigurationStatus + + public const string Path = "Umbraco.Core.Path"; //umbracoPath + + public const string ReservedUrls = "Umbraco.Core.ReservedUrls"; //umbracoReservedUrls + + public const string ReservedPaths = "Umbraco.Core.ReservedPaths"; //umbracoReservedPaths + + public const string TimeOutInMinutes = "Umbraco.Core.TimeOutInMinutes"; //umbracoTimeOutInMinutes + + public const string VersionCheckPeriod = "Umbraco.Core.VersionCheckPeriod"; //umbracoVersionCheckPeriod + + public const string LocalTempStorage = "Umbraco.Core.LocalTempStorage"; //umbracoLocalTempStorage + + public const string DefaultUILanguage = "Umbraco.Core.DefaultUILanguage"; //umbracoDefaultUILanguage + + public const string HideTopLevelNodeFromPath = "Umbraco.Core.HideTopLevelNodeFromPath"; //umbracoHideTopLevelNodeFromPath + + public const string UseHttps = "Umbraco.Core.UseHttps"; //umbracoUseHttps + + public const string DisableElectionForSingleServer = "Umbraco.Core.DisableElectionForSingleServer"; //umbracoDisableElectionForSingleServer + + public const string DatabaseFactoryServerVersion = "Umbraco.Core.DatabaseFactoryServerVersion"; //Umbraco.DatabaseFactory.ServerVersion + public static class Debug + { + public const string LogUncompletedScopes = "Umbraco.Core.LogUncompletedScopes"; //"Umbraco.CoreDebug.LogUncompletedScopes" + + public const string DumpOnTimeoutThreadAbort = "Umbraco.Core.DumpOnTimeoutThreadAbort"; //Umbraco.CoreDebug.DumpOnTimeoutThreadAbort + } } } } diff --git a/src/Umbraco.Core/IO/SystemFiles.cs b/src/Umbraco.Core/IO/SystemFiles.cs index 0e5ae8388b..f3376901a9 100644 --- a/src/Umbraco.Core/IO/SystemFiles.cs +++ b/src/Umbraco.Core/IO/SystemFiles.cs @@ -8,7 +8,8 @@ namespace Umbraco.Core.IO public class SystemFiles { public static string TinyMceConfig => SystemDirectories.Config + "/tinyMceConfig.config"; - + + // TODO: Kill this off we don't have umbraco.config XML cache we now have NuCache public static string GetContentCacheXml(IGlobalSettings globalSettings) { switch (globalSettings.LocalTempStorageLocation) @@ -24,7 +25,7 @@ namespace Umbraco.Core.IO appDomainHash); return Path.Combine(cachePath, "umbraco.config"); case LocalTempStorage.Default: - return IOHelper.ReturnPath("umbracoContentXML", "~/App_Data/umbraco.config"); + return IOHelper.ReturnPath(Constants.AppSettings.ContentXML, "~/App_Data/umbraco.config"); default: throw new ArgumentOutOfRangeException(); } diff --git a/src/Umbraco.Core/Models/Language.cs b/src/Umbraco.Core/Models/Language.cs index 03f8f87cd3..b02eb4805c 100644 --- a/src/Umbraco.Core/Models/Language.cs +++ b/src/Umbraco.Core/Models/Language.cs @@ -67,7 +67,7 @@ namespace Umbraco.Core.Models // culture // // I assume that, on a site, all language names should be in the SAME language, in DB, - // and that would be the umbracoDefaultUILanguage (app setting) - BUT if by accident + // and that would be the Umbraco.Core.DefaultUILanguage (app setting) - BUT if by accident // ANY culture has been retrieved with another current thread culture - it's now corrupt // // so, the logic below ensures that the name always end up being the correct name diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs index eab0ae5509..3d0d49a5f0 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs @@ -139,7 +139,7 @@ namespace Umbraco.Core.Persistence { // replace NPoco database type by a more efficient one - var setting = ConfigurationManager.AppSettings["Umbraco.DatabaseFactory.ServerVersion"]; + var setting = ConfigurationManager.AppSettings[Constants.AppSettings.DatabaseFactoryServerVersion]; var fromSettings = false; if (setting.IsNullOrWhiteSpace() || !setting.StartsWith("SqlServer.") diff --git a/src/Umbraco.Core/Runtime/CoreRuntimeComposer.cs b/src/Umbraco.Core/Runtime/CoreRuntimeComposer.cs index ee289ddcfa..2d1a4c1650 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntimeComposer.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntimeComposer.cs @@ -76,7 +76,7 @@ namespace Umbraco.Core.Runtime // TODO: this is a hack, use proper configuration! // also: we still register the full IServerMessenger because // even on 1 single server we can have 2 concurrent app domains - var singleServer = "true".InvariantEquals(ConfigurationManager.AppSettings["umbracoDisableElectionForSingleServer"]); + var singleServer = "true".InvariantEquals(ConfigurationManager.AppSettings[Constants.AppSettings.DisableElectionForSingleServer]); return singleServer ? (IServerRegistrar) new SingleServerRegistrar(f.GetInstance()) : new DatabaseServerRegistrar( diff --git a/src/Umbraco.Tests/App.config b/src/Umbraco.Tests/App.config index 6607a288b5..5e366eef33 100644 --- a/src/Umbraco.Tests/App.config +++ b/src/Umbraco.Tests/App.config @@ -5,14 +5,13 @@ - - - - - - - - + + + + + + + diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs index 8cfc06c501..c62614ef22 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs @@ -645,7 +645,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private static void InitializeCacheConfig() { - var value = ConfigurationManager.AppSettings["Umbraco.PublishedMediaCache.Seconds"]; + var value = ConfigurationManager.AppSettings[Constants.AppSettings.PublishedMediaCacheSeconds]; int seconds; if (int.TryParse(value, out seconds) == false) seconds = PublishedMediaCacheTimespanSeconds; diff --git a/src/Umbraco.Web.UI/web.Template.config b/src/Umbraco.Web.UI/web.Template.config index 06c055b98e..d93089fe21 100644 --- a/src/Umbraco.Web.UI/web.Template.config +++ b/src/Umbraco.Web.UI/web.Template.config @@ -31,13 +31,13 @@ - - - - - - - + + + + + + + diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index 4fb8dadf17..123cb952d7 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -248,7 +248,7 @@ namespace Umbraco.Web.Runtime XmlFileMapper.FileMapDefaultFolder = SystemDirectories.TempData.EnsureEndsWith('/') + "ClientDependency"; BaseCompositeFileProcessingProvider.UrlTypeDefault = CompositeUrlType.Base64QueryStrings; - // Now we need to detect if we are running umbracoLocalTempStorage as EnvironmentTemp and in that case we want to change the CDF file + // Now we need to detect if we are running 'Umbraco.Core.LocalTempStorage' as EnvironmentTemp and in that case we want to change the CDF file // location to be there if (globalSettings.LocalTempStorageLocation == LocalTempStorage.EnvironmentTemp) { diff --git a/src/Umbraco.Web/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs b/src/Umbraco.Web/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs index 5b571f304e..036a1718b0 100644 --- a/src/Umbraco.Web/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs +++ b/src/Umbraco.Web/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs @@ -13,6 +13,7 @@ namespace Umbraco.Web.Security { get { + // TODO: Verify this AppSetting key is used in .NET Framework & canot be changed to Umbraco.Core. prefix return ConfigurationManager.AppSettings["ActiveDirectoryDomain"]; } } From bc7748aed618019785b62474238b7d26dfed81b9 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 31 Jan 2019 12:37:54 +0000 Subject: [PATCH 15/33] Adds in XML docs summary - still need assitance with a few keys so put in some TODOs --- src/Umbraco.Core/Composing/RegisterFactory.cs | 2 +- src/Umbraco.Core/Constants-AppSettings.cs | 135 +++++++++++++----- .../Persistence/UmbracoDatabaseFactory.cs | 2 +- 3 files changed, 103 insertions(+), 36 deletions(-) diff --git a/src/Umbraco.Core/Composing/RegisterFactory.cs b/src/Umbraco.Core/Composing/RegisterFactory.cs index 975de7647d..ea25d6a135 100644 --- a/src/Umbraco.Core/Composing/RegisterFactory.cs +++ b/src/Umbraco.Core/Composing/RegisterFactory.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.Composing /// Creates a new instance of the configured container. ///
    /// - /// To override the default LightInjectContainer, add an appSetting named umbracoRegisterType with + /// To override the default LightInjectContainer, add an appSetting named 'Umbraco.Core.RegisterType' with /// a fully qualified type name to a class with a static method "Create" returning an IRegister. /// public static IRegister Create() diff --git a/src/Umbraco.Core/Constants-AppSettings.cs b/src/Umbraco.Core/Constants-AppSettings.cs index 67f92f4046..ac0cbde8de 100644 --- a/src/Umbraco.Core/Constants-AppSettings.cs +++ b/src/Umbraco.Core/Constants-AppSettings.cs @@ -1,12 +1,11 @@ using System; -using System.ComponentModel; namespace Umbraco.Core { public static partial class Constants { /// - /// Defines the identifiers for Umbraco system nodes. + /// Specific web.config AppSetting keys for Umbraco.Core application /// public static class AppSettings { @@ -14,43 +13,111 @@ namespace Umbraco.Core [Obsolete("We need to kill this appsetting as we do not use XML content cache umbraco.config anymore due to NuCache")] public const string ContentXML = "Umbraco.Core.ContentXML"; //umbracoContentXML - + /// + /// TODO: FILL ME IN + /// public const string RegisterType = "Umbraco.Core.RegisterType"; + + /// + /// This is used for a unit test in PublishedMediaCache + /// + public const string PublishedMediaCacheSeconds = "Umbraco.Core.PublishedMediaCacheSeconds"; + + /// + /// TODO: FILL ME IN + /// + public const string AssembliesAcceptingLoadExceptions = "Umbraco.Core.AssembliesAcceptingLoadExceptions"; + + /// + /// This will return the version number of the currently installed umbraco instance + /// + /// + /// Umbraco will automatically set & modify this value when installing & upgrading + /// + public const string ConfigurationStatus = "Umbraco.Core.ConfigurationStatus"; + + /// + /// Gets the path to umbraco's root directory (/umbraco by default). + /// + public const string Path = "Umbraco.Core.Path"; + + /// + /// The reserved urls from web.config. + /// + public const string ReservedUrls = "Umbraco.Core.ReservedUrls"; + + /// + /// The reserved paths from web.config + /// + public const string ReservedPaths = "Umbraco.Core.ReservedPaths"; + + /// + /// Set the timeout for the Umbraco backoffice in minutes + /// + public const string TimeOutInMinutes = "Umbraco.Core.TimeOutInMinutes"; + + /// + /// The number of days to check for a new version of Umbraco + /// + /// + /// Default is set to 7. Setting this to 0 will never check + /// This is used to help track statistics + /// + public const string VersionCheckPeriod = "Umbraco.Core.VersionCheckPeriod"; + + /// + /// This is the location type to store temporary files such as cache files or other localized files for a given machine + /// + /// + /// Currently used for the xml cache file and the plugin cache files + /// + public const string LocalTempStorage = "Umbraco.Core.LocalTempStorage"; + + /// + /// The default UI language of the backoffice such as 'en-US' + /// + public const string DefaultUILanguage = "Umbraco.Core.DefaultUILanguage"; + + /// + /// A true/false value indicating whether umbraco should hide top level nodes from generated urls. + /// + public const string HideTopLevelNodeFromPath = "Umbraco.Core.HideTopLevelNodeFromPath"; + + /// + /// A true or false indicating whether umbraco should force a secure (https) connection to the backoffice. + /// + public const string UseHttps = "Umbraco.Core.UseHttps"; + + /// + /// TODO: FILL ME IN + /// + public const string DisableElectionForSingleServer = "Umbraco.Core.DisableElectionForSingleServer"; + - public const string PublishedMediaCacheSeconds = "Umbraco.Core.PublishedMediaCacheSeconds"; //"Umbraco.PublishedMediaCache.Seconds" - - public const string AssembliesAcceptingLoadExceptions = "Umbraco.Core.AssembliesAcceptingLoadExceptions"; //Umbraco.AssembliesAcceptingLoadExceptions - - public const string ConfigurationStatus = "Umbraco.Core.ConfigurationStatus"; //umbracoConfigurationStatus - - public const string Path = "Umbraco.Core.Path"; //umbracoPath - - public const string ReservedUrls = "Umbraco.Core.ReservedUrls"; //umbracoReservedUrls - - public const string ReservedPaths = "Umbraco.Core.ReservedPaths"; //umbracoReservedPaths - - public const string TimeOutInMinutes = "Umbraco.Core.TimeOutInMinutes"; //umbracoTimeOutInMinutes - - public const string VersionCheckPeriod = "Umbraco.Core.VersionCheckPeriod"; //umbracoVersionCheckPeriod - - public const string LocalTempStorage = "Umbraco.Core.LocalTempStorage"; //umbracoLocalTempStorage - - public const string DefaultUILanguage = "Umbraco.Core.DefaultUILanguage"; //umbracoDefaultUILanguage - - public const string HideTopLevelNodeFromPath = "Umbraco.Core.HideTopLevelNodeFromPath"; //umbracoHideTopLevelNodeFromPath - - public const string UseHttps = "Umbraco.Core.UseHttps"; //umbracoUseHttps - - public const string DisableElectionForSingleServer = "Umbraco.Core.DisableElectionForSingleServer"; //umbracoDisableElectionForSingleServer - - public const string DatabaseFactoryServerVersion = "Umbraco.Core.DatabaseFactoryServerVersion"; //Umbraco.DatabaseFactory.ServerVersion - - + /// + /// Debug specific web.config AppSetting keys for Umbraco + /// + /// + /// Do not use these keys in a production environment + /// public static class Debug { - public const string LogUncompletedScopes = "Umbraco.Core.LogUncompletedScopes"; //"Umbraco.CoreDebug.LogUncompletedScopes" + /// + /// When set to true, Scope logs the stack trace for any scope that gets disposed without being completed. + /// this helps troubleshooting rogue scopes that we forget to complete + /// + public const string LogUncompletedScopes = "Umbraco.Core.Debug.LogUncompletedScopes"; - public const string DumpOnTimeoutThreadAbort = "Umbraco.Core.DumpOnTimeoutThreadAbort"; //Umbraco.CoreDebug.DumpOnTimeoutThreadAbort + /// + /// When set to true, the Logger creates a mini dump of w3wp in ~/App_Data/MiniDump whenever it logs + /// an error due to a ThreadAbortException that is due to a timeout. + /// + public const string DumpOnTimeoutThreadAbort = "Umbraco.Core.Debug.DumpOnTimeoutThreadAbort"; + + /// + /// TODO: FILL ME IN + /// + public const string DatabaseFactoryServerVersion = "Umbraco.Core.Debug.DatabaseFactoryServerVersion"; } } } diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs index 3d0d49a5f0..681eb1232e 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs @@ -139,7 +139,7 @@ namespace Umbraco.Core.Persistence { // replace NPoco database type by a more efficient one - var setting = ConfigurationManager.AppSettings[Constants.AppSettings.DatabaseFactoryServerVersion]; + var setting = ConfigurationManager.AppSettings[Constants.AppSettings.Debug.DatabaseFactoryServerVersion]; var fromSettings = false; if (setting.IsNullOrWhiteSpace() || !setting.StartsWith("SqlServer.") From 6bf3b19d8624f2ea991eacf632ee0edd31f1daa7 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 31 Jan 2019 15:09:57 +0100 Subject: [PATCH 16/33] #4068 - Fix for ordering of grid --- .../propertyeditors/grid/grid.controller.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js index be92128280..f7e69a2683 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js @@ -60,8 +60,8 @@ angular.module("umbraco") start: function (e, ui) { // Fade out row when sorting - ui.item.context.style.display = "block"; - ui.item.context.style.opacity = "0.5"; + ui.item[0].style.display = "block"; + ui.item[0].style.opacity = "0.5"; draggedRteSettings = {}; ui.item.find(".mceNoEditor").each(function () { @@ -75,7 +75,7 @@ angular.module("umbraco") stop: function (e, ui) { // Fade in row when sorting stops - ui.item.context.style.opacity = "1"; + ui.item[0].style.opacity = "1"; // reset all RTEs affected by the dragging ui.item.parents(".umb-column").find(".mceNoEditor").each(function () { @@ -185,12 +185,12 @@ angular.module("umbraco") startingArea = area; // fade out control when sorting - ui.item.context.style.display = "block"; - ui.item.context.style.opacity = "0.5"; + ui.item[0].style.display = "block"; + ui.item[0].style.opacity = "0.5"; // reset dragged RTE settings in case a RTE isn't dragged draggedRteSettings = undefined; - ui.item.context.style.display = "block"; + ui.item[0].style.display = "block"; ui.item.find(".mceNoEditor").each(function () { notIncludedRte = []; var editors = _.findWhere(tinyMCE.editors, { id: $(this).attr("id") }); @@ -210,7 +210,7 @@ angular.module("umbraco") stop: function (e, ui) { // Fade in control when sorting stops - ui.item.context.style.opacity = "1"; + ui.item[0].style.opacity = "1"; ui.item.offsetParent().find(".mceNoEditor").each(function () { if ($.inArray($(this).attr("id"), notIncludedRte) < 0) { @@ -400,7 +400,7 @@ angular.module("umbraco") } $scope.editGridItemSettings = function (gridItem, itemType) { - + placeHolder = "{0}"; var styles, config; @@ -658,7 +658,7 @@ angular.module("umbraco") // ********************************************* $scope.initContent = function () { var clear = true; - + //settings indicator shortcut if (($scope.model.config.items.config && $scope.model.config.items.config.length > 0) || ($scope.model.config.items.styles && $scope.model.config.items.styles.length > 0)) { $scope.hasSettings = true; @@ -755,7 +755,7 @@ angular.module("umbraco") // Init layout / row // ********************************************* $scope.initRow = function (row) { - + //merge the layout data with the original config data //if there are no config info on this, splice it out var original = _.find($scope.model.config.items.layouts, function (o) { return o.name === row.name; }); From 4a480ab3918be8f0bce3c57027bd2ce750a361ba Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 31 Jan 2019 15:10:19 +0100 Subject: [PATCH 17/33] #4068 - Added interceptor to remove `$` variables from post data --- .../src/common/interceptors/_module.js | 1 + ...tpostdollarvariablesrequest.interceptor.js | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/common/interceptors/donotpostdollarvariablesrequest.interceptor.js diff --git a/src/Umbraco.Web.UI.Client/src/common/interceptors/_module.js b/src/Umbraco.Web.UI.Client/src/common/interceptors/_module.js index 765f44a636..05263cebf2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/interceptors/_module.js +++ b/src/Umbraco.Web.UI.Client/src/common/interceptors/_module.js @@ -6,4 +6,5 @@ angular.module('umbraco.interceptors', []) $httpProvider.interceptors.push('securityInterceptor'); $httpProvider.interceptors.push('debugRequestInterceptor'); + $httpProvider.interceptors.push('doNotPostDollarVariablesOnPostRequestInterceptor'); }]); diff --git a/src/Umbraco.Web.UI.Client/src/common/interceptors/donotpostdollarvariablesrequest.interceptor.js b/src/Umbraco.Web.UI.Client/src/common/interceptors/donotpostdollarvariablesrequest.interceptor.js new file mode 100644 index 0000000000..0a5f6d837d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/interceptors/donotpostdollarvariablesrequest.interceptor.js @@ -0,0 +1,38 @@ +(function() { + 'use strict'; + + function removeProperty(obj, propertyPrefix) { + for (var property in obj) { + if (obj.hasOwnProperty(property)) { + + if (property.startsWith(propertyPrefix) && obj[property]) { + obj[property] = undefined; + } + + if (typeof obj[property] == "object") { + removeProperty(obj[property], propertyPrefix); + } + } + } + + } + + function transform(data){ + removeProperty(data, "$"); + } + + function doNotPostDollarVariablesRequestInterceptor($q, urlHelper) { + return { + //dealing with requests: + 'request': function(config) { + if(config.method === "POST"){ + config.transformRequest.push(transform); + } + + return config; + } + }; + } + + angular.module('umbraco.interceptors').factory('doNotPostDollarVariablesOnPostRequestInterceptor', doNotPostDollarVariablesRequestInterceptor); +})(); From 452badab41580ca362e7fac017d2c2039116cd12 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 1 Feb 2019 11:27:40 +1100 Subject: [PATCH 18/33] Fixes tag editor for proper dirty tracking and model updating, the models weren't being updated correctly --- .../tags/umbtagseditor.directive.js | 37 ++++++++++++++----- .../propertyeditors/tags/tags.controller.js | 5 +-- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js index 1850445bd4..4d10625d23 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js @@ -52,7 +52,8 @@ vm.isLoading = false; - configureViewModel(); + //ensure that the models are formatted correctly + configureViewModel(true); // Set the visible prompt to -1 to ensure it will not be visible vm.promptIsVisible = "-1"; @@ -123,9 +124,13 @@ }); } + /** + * Watch for value changes + * @param {any} changes + */ function onChanges(changes) { - // watch for value changes externally + //when the model 'value' changes, sync the viewModel object if (changes.value) { if (!changes.value.isFirstChange() && changes.value.currentValue !== changes.value.previousValue) { @@ -145,13 +150,15 @@ $element.find('.tags-' + vm.htmlId).typeahead('destroy'); } - function configureViewModel() { + function configureViewModel(isInitLoad) { if (vm.value) { if (angular.isString(vm.value) && vm.value.length > 0) { if (vm.config.storageType === "Json") { //json storage vm.viewModel = JSON.parse(vm.value); - updateModelValue(vm.viewModel); + if (!isInitLoad) { + updateModelValue(vm.viewModel); + } } else { //csv storage @@ -165,7 +172,10 @@ return self.indexOf(v) === i; }); - updateModelValue(vm.viewModel); + if (!isInitLoad) { + updateModelValue(vm.viewModel); + } + } } else if (angular.isArray(vm.value)) { @@ -175,13 +185,18 @@ } function updateModelValue(val) { - if (val) { - vm.onValueChanged({ value: val }); + + //need to format the underlying model value for persistence based on the storage type + if (vm.config.storageType === "Json") { + val = val ? val : []; } else { - vm.onValueChanged({ value: [] }); + //then it is csv and we need to format it like that + val = val ? val.join() : ""; } + vm.onValueChanged({ value: val }); + reValidate(); } @@ -273,8 +288,10 @@ } function reValidate() { - //this is required to re-validate - vm.tagEditorForm.tagCount.$setViewValue(vm.viewModel.length); + //this is required to re-validate for the mandatory validation + if (vm.tagEditorForm && vm.tagEditorForm.tagCount) { + vm.tagEditorForm.tagCount.$setViewValue(vm.viewModel.length); + } } } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js index a61930f877..688ac7693f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js @@ -1,12 +1,9 @@ angular.module("umbraco") .controller("Umbraco.PropertyEditors.TagsController", - function ($scope, angularHelper) { + function ($scope) { $scope.valueChanged = function(value) { $scope.model.value = value; - // the model value seems to be a reference to the same array, so we need - // to set the form as dirty explicitly when the content of the array changes - angularHelper.getCurrentForm($scope).$setDirty(); } } From 7614ba42df7b1ef33575e58bbfc068ad50149619 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 1 Feb 2019 13:32:17 +1100 Subject: [PATCH 19/33] Fixes the tag editor and tag suggestions --- .../tags/umbtagseditor.directive.js | 85 +++++++------------ .../Editors/BackOfficeServerVariables.cs | 2 +- .../PropertyEditors/TagsDataController.cs | 24 +++++- 3 files changed, 56 insertions(+), 55 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js index 4d10625d23..edf54ca034 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js @@ -24,10 +24,10 @@ function umbTagsEditorController($rootScope, assetsService, umbRequestHelper, angularHelper, $timeout, $element) { - var vm = this; + let vm = this; - var typeahead; - var tagsHound; + let typeahead; + let tagsHound; vm.$onInit = onInit; vm.$onChanges = onChanges; @@ -53,50 +53,51 @@ vm.isLoading = false; //ensure that the models are formatted correctly - configureViewModel(true); + configureViewModel(); // Set the visible prompt to -1 to ensure it will not be visible vm.promptIsVisible = "-1"; tagsHound = new Bloodhound({ - datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), + initialize: false, + identify: function (obj) { return obj.id; }, + datumTokenizer: Bloodhound.tokenizers.obj.whitespace('text'), queryTokenizer: Bloodhound.tokenizers.whitespace, //pre-fetch the tags for this category prefetch: { url: umbRequestHelper.getApiUrl("tagsDataBaseUrl", "GetTags", { tagGroup: vm.config.group, culture: vm.culture }), //TTL = 5 minutes - ttl: 300000, - transform: dataTransform + ttl: 300000 }, //dynamically get the tags for this category (they may have changed on the server) remote: { - url: umbRequestHelper.getApiUrl("tagsDataBaseUrl", "GetTags", { tagGroup: vm.config.group, culture: vm.culture }), - transform: dataTransform + url: umbRequestHelper.getApiUrl("tagsDataBaseUrl", "GetTags", { tagGroup: vm.config.group, culture: vm.culture, query: "%QUERY" }), + wildcard: "%QUERY" } }); - tagsHound.initialize(true); - - //configure the type ahead - $timeout(function () { + tagsHound.initialize().then(function() { + //configure the type ahead + var sources = { //see: https://github.com/twitter/typeahead.js/blob/master/doc/jquery_typeahead.md#options - // name = the data set name, we'll make this the tag group name - name: vm.config.group, - display: "value", + // name = the data set name, we'll make this the tag group name + culture + name: vm.config.group + (vm.culture ? vm.culture : ""), + display: "text", //source: tagsHound - source: function (query, cb) { + source: function (query, syncCallback, asyncCallback) { tagsHound.search(query, function(suggestions) { - cb(removeCurrentTagsFromSuggestions(suggestions)); + syncCallback(removeCurrentTagsFromSuggestions(suggestions)); + }, function(suggestions) { + asyncCallback(removeCurrentTagsFromSuggestions(suggestions)); }); } }; var opts = { - //This causes some strangeness as it duplicates the textbox, best leave off for now. - hint: false, + hint: true, highlight: true, cacheKey: new Date(), // Force a cache refresh each time the control is initialized minLength: 1 @@ -105,22 +106,25 @@ typeahead = $element.find('.tags-' + vm.htmlId).typeahead(opts, sources) .bind("typeahead:selected", function (obj, datum, name) { angularHelper.safeApply($rootScope, function () { - addTagInternal(datum["value"]); + addTagInternal(datum["text"]); vm.tagToAdd = ""; // clear the typed text typeahead.typeahead('val', ''); }); }).bind("typeahead:autocompleted", function (obj, datum, name) { angularHelper.safeApply($rootScope, function () { - addTagInternal(datum["value"]); + addTagInternal(datum["text"]); vm.tagToAdd = ""; + // clear the typed text + typeahead.typeahead('val', ''); }); }).bind("typeahead:opened", function (obj) { }); - }); + }); + }); } @@ -150,15 +154,13 @@ $element.find('.tags-' + vm.htmlId).typeahead('destroy'); } - function configureViewModel(isInitLoad) { + function configureViewModel() { if (vm.value) { if (angular.isString(vm.value) && vm.value.length > 0) { if (vm.config.storageType === "Json") { //json storage vm.viewModel = JSON.parse(vm.value); - if (!isInitLoad) { - updateModelValue(vm.viewModel); - } + updateModelValue(vm.viewModel); } else { //csv storage @@ -172,9 +174,7 @@ return self.indexOf(v) === i; }); - if (!isInitLoad) { - updateModelValue(vm.viewModel); - } + updateModelValue(vm.viewModel); } } @@ -186,14 +186,7 @@ function updateModelValue(val) { - //need to format the underlying model value for persistence based on the storage type - if (vm.config.storageType === "Json") { - val = val ? val : []; - } - else { - //then it is csv and we need to format it like that - val = val ? val.join() : ""; - } + val = val ? val : []; vm.onValueChanged({ value: val }); @@ -267,23 +260,11 @@ function hidePrompt() { vm.promptIsVisible = "-1"; } - - //helper method to format the data for bloodhound - function dataTransform(list) { - //transform the result to what bloodhound wants - var tagList = _.map(list, function (i) { - return { value: i.text }; - }); - // remove current tags from the list - return $.grep(tagList, function (tag) { - return ($.inArray(tag.value, vm.viewModel) === -1); - }); - } - + // helper method to remove current tags function removeCurrentTagsFromSuggestions(suggestions) { return $.grep(suggestions, function (suggestion) { - return ($.inArray(suggestion.value, vm.viewModel) === -1); + return ($.inArray(suggestion.text, vm.viewModel) === -1); }); } diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs index 83bb152e0a..954491eab9 100644 --- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs @@ -259,7 +259,7 @@ namespace Umbraco.Web.Editors }, { "tagsDataBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( - controller => controller.GetTags("", "")) + controller => controller.GetTags("", "", null)) }, { "examineMgmtBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( diff --git a/src/Umbraco.Web/PropertyEditors/TagsDataController.cs b/src/Umbraco.Web/PropertyEditors/TagsDataController.cs index 15c39bf994..7d699077c3 100644 --- a/src/Umbraco.Web/PropertyEditors/TagsDataController.cs +++ b/src/Umbraco.Web/PropertyEditors/TagsDataController.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using System.Linq; +using Umbraco.Core; using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; @@ -15,10 +17,28 @@ namespace Umbraco.Web.PropertyEditors [PluginController("UmbracoApi")] public class TagsDataController : UmbracoAuthorizedApiController { - public IEnumerable GetTags(string tagGroup, string culture) + /// + /// Returns all tags matching tagGroup, culture and an optional query + /// + /// + /// + /// + /// + public IEnumerable GetTags(string tagGroup, string culture, string query = null) { if (culture == string.Empty) culture = null; - return Umbraco.TagQuery.GetAllTags(tagGroup, culture); + + var result = Umbraco.TagQuery.GetAllTags(tagGroup, culture); + + + if (!query.IsNullOrWhiteSpace()) + { + //TODO: add the query to TagQuery + the tag service, this is ugly but all we can do for now. + //currently we are post filtering this :( but works for now + result = result.Where(x => x.Text.InvariantContains(query)); + } + + return result.OrderBy(x => x.Text); } } } From 045020e739f277d016516e34e200018bfcd495b0 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 1 Feb 2019 08:37:06 +0100 Subject: [PATCH 20/33] #4068 - Changed `save` event triggered when save dialog is opened. `save` is still broadcasted on the actual save. Changed the grid event listener to only execute on `save` events --- .../common/directives/components/content/edit.controller.js | 2 +- .../donotpostdollarvariablesrequest.interceptor.js | 2 +- .../src/views/propertyeditors/grid/grid.controller.js | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index 5c5b1f933e..4f71fb0686 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -617,7 +617,7 @@ // TODO: Add "..." to save button label if there are more than one variant to publish - currently it just adds the elipses if there's more than 1 variant if (isContentCultureVariant()) { //before we launch the dialog we want to execute all client side validations first - if (formHelper.submitForm({ scope: $scope, action: "save" })) { + if (formHelper.submitForm({ scope: $scope, action: "openSaveDialog" })) { var dialog = { parentScope: $scope, diff --git a/src/Umbraco.Web.UI.Client/src/common/interceptors/donotpostdollarvariablesrequest.interceptor.js b/src/Umbraco.Web.UI.Client/src/common/interceptors/donotpostdollarvariablesrequest.interceptor.js index 0a5f6d837d..1bae002df9 100644 --- a/src/Umbraco.Web.UI.Client/src/common/interceptors/donotpostdollarvariablesrequest.interceptor.js +++ b/src/Umbraco.Web.UI.Client/src/common/interceptors/donotpostdollarvariablesrequest.interceptor.js @@ -26,7 +26,7 @@ //dealing with requests: 'request': function(config) { if(config.method === "POST"){ - config.transformRequest.push(transform); + transform(config.data); } return config; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js index f7e69a2683..b40305f5f5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js @@ -909,9 +909,9 @@ angular.module("umbraco") // needs to be merged in at runtime to ensure that the real config values are used // if they are ever updated. - var unsubscribe = $scope.$on("formSubmitting", function () { - - if ($scope.model.value && $scope.model.value.sections) { + var unsubscribe = $scope.$on("formSubmitting", function (e, args) { + + if (args.action === "save" && $scope.model.value && $scope.model.value.sections) { _.each($scope.model.value.sections, function(section) { if (section.rows) { _.each(section.rows, function (row) { From fc6e17f3f9032b0fc9b9dbf3a2cb82421c048fcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 1 Feb 2019 10:49:09 +0100 Subject: [PATCH 21/33] =?UTF-8?q?V8:=20UI=20=E2=80=94=20umb-content-grid?= =?UTF-8?q?=20color=20change?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/less/components/umb-content-grid.less | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less index 5a2821eb9e..ccf86e7ea6 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-content-grid.less @@ -15,10 +15,6 @@ box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16); border-radius: 3px; - color: @ui-option-type; - &:hover { - color:@ui-option-type-hover; - } } .umb-content-grid__item.-selected { @@ -70,9 +66,13 @@ .umb-content-grid__item-name { font-weight: bold; margin-bottom: 15px; - //color: @black; line-height: 1.4em; display: inline-flex; + + color: @ui-option-type; + &:hover { + color:@ui-option-type-hover; + } } .umb-content-grid__item-name:hover span { From 979f7b404cc32639392fcf1aa0dbc90584d2a8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 1 Feb 2019 10:51:14 +0100 Subject: [PATCH 22/33] V8: refactoring of layers add and removal methods --- .../components/editor/umbeditors.directive.js | 234 +++++++++--------- .../less/components/editor/umb-editor.less | 16 +- 2 files changed, 130 insertions(+), 120 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js index 0db239c56a..c25dd0a76f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js @@ -13,145 +13,145 @@ function addEditor(editor) { + + if (!editor.style) + editor.style = {}; + editor.animating = true; - + showOverlayOnPrevEditor(); - // start collapsing editors to make room for new ones - $timeout(function() { - - var editorsElement = el[0]; - // only select the editors which are allowed to be - // shown so we don't animate a lot of editors which aren't necessary - var moveEditors = editorsElement.querySelectorAll('.umb-editor:nth-last-child(-n+'+ allowedNumberOfVisibleEditors +')'); - - // collapse open editors before opening the new one - var collapseEditorAnimation = anime({ - targets: moveEditors, - width: function(el, index, length) { - // we have to resize all small editors when they move to the - // left side so they don't leave a gap - if(el.classList.contains("umb-editor--small")) { - return "100%"; - } - }, - left: function(el, index, length){ - if(length >= allowedNumberOfVisibleEditors) { - return index * editorIndent; - } - return (index + 1) * editorIndent; - }, + var i = allowedNumberOfVisibleEditors; + var len = scope.editors.length; + while(i= allowedNumberOfVisibleEditors) { - indentValue = allowedNumberOfVisibleEditors * editorIndent; } - - // indent all large editors - if(editor.size !== "small") { - lastEditor.style.left = indentValue + "px"; + + if(scope.editors[i].size !== "small") { + animeConfig.width = "100%"; } + + if(len >= allowedNumberOfVisibleEditors) { + animeConfig.left = i * editorIndent; + } else { + animeConfig.left = (i + 1) * editorIndent; + } + + anime(animeConfig); + + i++; + } + + + // push the new editor to the dom + scope.editors.push(editor); + + + + var indentValue = scope.editors.length * editorIndent; - // animation config - var addEditorAnimation = anime({ - targets: lastEditor, - translateX: [100 + '%', 0], - opacity: [0, 1], - easing: 'easeInOutQuint', - duration: 300, - complete: function() { - $timeout(function(){ - editor.animating = false; - }); - } - }); + // don't allow indent larger than what + // fits the max number of visible editors + if(scope.editors.length >= allowedNumberOfVisibleEditors) { + indentValue = allowedNumberOfVisibleEditors * editorIndent; + } + // indent all large editors + if(editor.size !== "small") { + editor.style.left = indentValue + "px"; + } + + editor.style._tx = 100; + //editor.style.opacity = 0; + editor.style.transform = "translateX("+editor.style._tx+"%)"; + + // animation config + anime({ + targets: editor.style, + _tx: [100, 0], + //opacity: [0, 1], + easing: 'easeOutExpo', + duration: 480, + update: () => { + editor.style.transform = "translateX("+editor.style._tx+"%)"; + scope.$digest(); + }, + complete: function() { + //$timeout(function(){ + editor.animating = false; + scope.$digest(); + //}); + } }); + } function removeEditor(editor) { editor.animating = true; - - $timeout(function(){ - - var editorsElement = el[0]; - var lastEditor = editorsElement.querySelector('.umb-editor:last-of-type'); - - var removeEditorAnimation = anime({ - targets: lastEditor, - translateX: [0, 100 + '%'], - opacity: [1, 0], - easing: 'easeInOutQuint', - duration: 300, - complete: function(a) { - $timeout(function(){ - scope.editors.splice(-1,1); - removeOverlayFromPrevEditor(); - }); - } - }); - - expandEditors(); - + + editor.style._tx = 0; + editor.style.transform = "translateX("+editor.style._tx+"%)"; + + // animation config + anime({ + targets: editor.style, + _tx: [0, 100], + //opacity: [1, 0], + easing: 'easeInExpo', + duration: 360, + update: () => { + editor.style.transform = "translateX("+editor.style._tx+"%)"; + scope.$digest(); + }, + complete: function() { + //$timeout(function(){ + scope.editors.splice(-1,1); + removeOverlayFromPrevEditor(); + scope.$digest(); + //}) + } }); - + + + expandEditors(); + + } function expandEditors() { - // expand hidden editors - $timeout(function() { - - var editorsElement = el[0]; - // only select the editors which are allowed to be - // shown so we don't animate a lot of editors which aren't necessary - // as the last element hasn't been removed from the dom yet we have to select the last four and then skip the last child (as it is the one closing). - var moveEditors = editorsElement.querySelectorAll('.umb-editor:nth-last-child(-n+'+ allowedNumberOfVisibleEditors + 1 +'):not(:last-child)'); - var editorWidth = editorsElement.offsetWidth; - - var expandEditorAnimation = anime({ - targets: moveEditors, - left: function(el, index, length){ - // move the editor all the way to the right if the top one is a small - if(el.classList.contains("umb-editor--small")) { - // only change the size if it is the editor on top - if(index + 1 === length) { - return editorWidth - 500; - } - } else { - return (index + 1) * editorIndent; - } - }, - width: function(el, index, length) { - // set the correct size if the top editor is of type "small" - if(el.classList.contains("umb-editor--small") && index + 1 === length) { - return "500px"; - } - }, + + var i = allowedNumberOfVisibleEditors + 1; + var len = scope.editors.length-1; + while(i Date: Fri, 1 Feb 2019 09:58:10 +0000 Subject: [PATCH 23/33] Fix count bug - so it works with two languages --- src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml index b656942020..17a2f3c66a 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml @@ -47,7 +47,7 @@ - @if (Model.Languages != null && Model.Languages.Count() > 2) + @if (Model.Languages != null && Model.Languages.Count() > 1) { foreach (var previewLink in Model.Languages) { From 0bb69aeeeeac4c7d27463a3318202beadf52909c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 1 Feb 2019 11:10:20 +0100 Subject: [PATCH 24/33] V8: preview colors changed for brandcolors 2019 --- .../src/less/canvas-designer.less | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less index 146f9b8a67..7f67d5c3b2 100644 --- a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less +++ b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less @@ -275,7 +275,7 @@ a, a:hover{ font-family: "Lato", Helvetica, Arial, sans-serif; font-size: 13px; line-height: 16px; - background: #413659; + background: #1b264f; -webkit-transition: all 0.2s ease-in-out; -moz-transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out; @@ -306,7 +306,7 @@ a, a:hover{ ul.sections { display: block; - background: #413659; + background: #1b264f; height: 100%; position:absolute; top: 90px; @@ -322,7 +322,7 @@ ul.sections { ul.sections li { display: block; - border-left: 4px #413659 solid; + border-left: 4px #1b264f solid; -webkit-transition: all .3s linear; -moz-transition: all .3s linear; transition: all .3s linear; @@ -330,7 +330,8 @@ ul.sections li { .fix-left-menu ul.sections li a span, .fix-left-menu ul.sections li a i { - color: #8d869b; + color: #fff; + opacity: .7; -webkit-transition: all .3s linear; -moz-transition: all .3s linear; transition: all .3s linear; @@ -345,6 +346,11 @@ ul.sections li a { text-align: center; text-decoration: none; border-bottom: 1px solid #2E2246; + &:hover { + span, i { + opacity: 1; + } + } } ul.sections li a i { @@ -367,7 +373,7 @@ ul.sections li.current a i { } ul.sections li.current, ul.sections li:hover { - border-left: 4px #00AEA2 solid; + border-left: 4px #f5c1bc solid; } .fix-left-menu:hover ul.sections li a span, From d38dba16600fcc45e05b8a6aa5b4a8821342470e Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Fri, 1 Feb 2019 10:14:29 +0000 Subject: [PATCH 25/33] Update nuspec to include new hosted image/icon for Nuget packages from .com site --- build/NuSpecs/UmbracoCms.Core.nuspec | 2 +- build/NuSpecs/UmbracoCms.Web.nuspec | 4 ++-- build/NuSpecs/UmbracoCms.nuspec | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index dc1224842e..9758c05dd6 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -8,7 +8,7 @@ Umbraco HQ http://opensource.org/licenses/MIT http://umbraco.com/ - http://umbraco.com/media/357769/100px_transparent.png + https://umbraco.com/dist/nuget/logo-small.png false Contains the core assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms package to setup Umbraco in Visual Studio as an ASP.NET project. Contains the core assemblies needed to run Umbraco Cms diff --git a/build/NuSpecs/UmbracoCms.Web.nuspec b/build/NuSpecs/UmbracoCms.Web.nuspec index 30fa303b30..fabd1e25a8 100644 --- a/build/NuSpecs/UmbracoCms.Web.nuspec +++ b/build/NuSpecs/UmbracoCms.Web.nuspec @@ -8,13 +8,13 @@ Umbraco HQ http://opensource.org/licenses/MIT http://umbraco.com/ - http://umbraco.com/media/357769/100px_transparent.png + https://umbraco.com/dist/nuget/logo-small.png false Contains the web assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms package to setup Umbraco in Visual Studio as an ASP.NET project. Contains the core assemblies needed to run Umbraco Cms en-US umbraco - + - + + diff --git a/src/Umbraco.Web.UI/config/serilog.user.config b/src/Umbraco.Web.UI/config/serilog.user.config index 374657795e..24e5e4e4be 100644 --- a/src/Umbraco.Web.UI/config/serilog.user.config +++ b/src/Umbraco.Web.UI/config/serilog.user.config @@ -3,7 +3,8 @@ - + +