diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index ad307324ea..9e2332db69 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -5,6 +5,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Migrations.Upgrade.V_7_12_0; using Umbraco.Core.Migrations.Upgrade.V_7_14_0; using Umbraco.Core.Migrations.Upgrade.V_8_0_0; +using Umbraco.Core.Migrations.Upgrade.V_8_1_0; namespace Umbraco.Core.Migrations.Upgrade { @@ -137,6 +138,8 @@ namespace Umbraco.Core.Migrations.Upgrade To("{E0CBE54D-A84F-4A8F-9B13-900945FD7ED9}"); To("{78BAF571-90D0-4D28-8175-EF96316DA789}"); + To("{80C0A0CB-0DD5-4573-B000-C4B7C313C70D}"); + //FINAL diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs index a5669d1a9e..749b37a41a 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs @@ -11,14 +11,14 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Initializes a new instance of the class. /// - public PublishedCultureInfo(string culture, string name, DateTime date) + public PublishedCultureInfo(string culture, string name, string urlSegment, DateTime date) { if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentNullOrEmptyException(nameof(culture)); if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); Culture = culture; Name = name; - UrlSegment = name.ToUrlSegment(culture); + UrlSegment = urlSegment; Date = date; } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index c0149ccadb..8d99ac3a7b 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -209,6 +209,7 @@ + diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index 3142e1038e..ad2b0220bb 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -15,6 +15,7 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; +using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing.Objects; using Umbraco.Tests.Testing.Objects.Accessors; @@ -181,7 +182,8 @@ namespace Umbraco.Tests.PublishedContent globalSettings, new SiteDomainHelper(), Mock.Of(), - Mock.Of()); + Mock.Of(), + new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() })); // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 9b1300ee23..1dcc928141 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -17,6 +17,7 @@ using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; +using Umbraco.Core.Strings; using Umbraco.Core.Sync; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; @@ -99,7 +100,8 @@ namespace Umbraco.Tests.Scoping new DatabaseDataSource(), Factory.GetInstance(), new SiteDomainHelper(), Factory.GetInstance(), - Mock.Of()); + Mock.Of(), + new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() })); } 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/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index b5bf9e61f8..c70b96a175 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -15,6 +15,7 @@ using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Core.Sync; using Umbraco.Tests.Testing; using Umbraco.Web; @@ -72,7 +73,8 @@ namespace Umbraco.Tests.Services new DatabaseDataSource(), Factory.GetInstance(), new SiteDomainHelper(), Factory.GetInstance(), - Mock.Of()); + Mock.Of(), + new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() })); } public class LocalServerMessenger : ServerMessengerBase diff --git a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs index cc88d183b6..9b3bc62cbf 100644 --- a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs +++ b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs @@ -6,6 +6,7 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; using Umbraco.Web.Composing; using Umbraco.Web.Editors; using Umbraco.Web.Routing; @@ -198,10 +199,10 @@ namespace Umbraco.Web.Macros Id = _inner.Id; Key = _inner.Key; - // TODO: ARGH! need to fix this - this is not good because it uses ApplicationContext.Current CreatorName = _inner.GetCreatorProfile()?.Name; WriterName = _inner.GetWriterProfile()?.Name; + // TODO: inject var contentType = Current.Services.ContentTypeBaseServices.GetContentTypeOf(_inner); ContentType = Current.PublishedContentTypeFactory.CreateContentType(contentType); @@ -252,8 +253,9 @@ namespace Umbraco.Web.Macros if (_cultureInfos != null) return _cultureInfos; + var urlSegmentProviders = Current.UrlSegmentProviders; // TODO inject return _cultureInfos = _inner.PublishCultureInfos.Values - .ToDictionary(x => x.Culture, x => new PublishedCultureInfo(x.Culture, x.Name, x.Date)); + .ToDictionary(x => x.Culture, x => new PublishedCultureInfo(x.Culture, x.Name, _inner.GetUrlSegment(urlSegmentProviders, x.Culture), x.Date)); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentDataSerializer.cs index 80633efe2e..056eacd717 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentDataSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentDataSerializer.cs @@ -14,6 +14,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource { Published = PrimitiveSerializer.Boolean.ReadFrom(stream), Name = PrimitiveSerializer.String.ReadFrom(stream), + UrlSegment = PrimitiveSerializer.String.ReadFrom(stream), VersionId = PrimitiveSerializer.Int32.ReadFrom(stream), VersionDate = PrimitiveSerializer.DateTime.ReadFrom(stream), WriterId = PrimitiveSerializer.Int32.ReadFrom(stream), @@ -27,6 +28,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource { PrimitiveSerializer.Boolean.WriteTo(value.Published, stream); PrimitiveSerializer.String.WriteTo(value.Name, stream); + PrimitiveSerializer.String.WriteTo(value.UrlSegment, stream); PrimitiveSerializer.Int32.WriteTo(value.VersionId, stream); PrimitiveSerializer.DateTime.WriteTo(value.VersionDate, stream); PrimitiveSerializer.Int32.WriteTo(value.WriterId, stream); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs index 107e74b7c3..958f6302fa 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource for (var i = 0; i < pcount; i++) { var languageId = PrimitiveSerializer.String.ReadFrom(stream); - var cultureVariation = new CultureVariation { Name = ReadStringObject(stream), Date = ReadDateTime(stream) }; + var cultureVariation = new CultureVariation { Name = ReadStringObject(stream), UrlSegment = ReadStringObject(stream), Date = ReadDateTime(stream) }; dict[languageId] = cultureVariation; } return dict; @@ -40,6 +40,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource PrimitiveSerializer.String.WriteTo(culture, stream); // should never be null WriteObject(variation.Name, stream); // write an object in case it's null (though... should not happen) + WriteObject(variation.UrlSegment, stream); // write an object in case it's null (though... should not happen) PrimitiveSerializer.DateTime.WriteTo(variation.Date, stream); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs index 520cc99011..36586acd12 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs @@ -7,6 +7,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource internal class ContentData { public string Name { get; set; } + public string UrlSegment { get; set; } public int VersionId { get; set; } public DateTime VersionDate { get; set; } public int WriterId { get; set; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentNestedData.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentNestedData.cs index 0f120024cc..adff28a2ba 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentNestedData.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentNestedData.cs @@ -16,5 +16,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource [JsonProperty("cultureData")] [JsonConverter(typeof(CaseInsensitiveDictionaryConverter))] public Dictionary CultureData { get; set; } + + [JsonProperty("urlSegment")] + public string UrlSegment { get; set; } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/CultureVariation.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/CultureVariation.cs index c6e603f5a9..a98a96f424 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/CultureVariation.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/CultureVariation.cs @@ -11,6 +11,9 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("urlSegment")] + public string UrlSegment { get; set; } + [JsonProperty("date")] public DateTime Date { get; set; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs index ca3abcca48..dcb3f44487 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs @@ -223,6 +223,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource p = new ContentData { Name = dto.PubName, + UrlSegment = nested.UrlSegment, Published = true, TemplateId = dto.PubTemplateId, VersionId = dto.VersionId, diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 3526a6daeb..057823f29c 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -31,7 +31,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); VariationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); - _urlSegment = ContentData.Name.ToUrlSegment(); + _urlSegment = ContentData.UrlSegment; IsPreviewing = ContentData.Published == false; var properties = new List(); @@ -276,8 +276,9 @@ namespace Umbraco.Web.PublishedCache.NuCache if (ContentData.CultureInfos == null) throw new Exception("oops: _contentDate.CultureInfos is null."); + return _cultureInfos = ContentData.CultureInfos - .ToDictionary(x => x.Key, x => new PublishedCultureInfo(x.Key, x.Value.Name, x.Value.Date), StringComparer.OrdinalIgnoreCase); + .ToDictionary(x => x.Key, x => new PublishedCultureInfo(x.Key, x.Value.Name, x.Value.UrlSegment, x.Value.Date), StringComparer.OrdinalIgnoreCase); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 9ede3657de..e19531a25b 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -23,6 +23,7 @@ using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Services.Implement; +using Umbraco.Core.Strings; using Umbraco.Web.Cache; using Umbraco.Web.Install; using Umbraco.Web.PublishedCache.NuCache.DataSource; @@ -46,7 +47,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly ISiteDomainHelper _siteDomainHelper; private readonly IEntityXmlSerializer _entitySerializer; private readonly IDefaultCultureAccessor _defaultCultureAccessor; - private readonly IPublishedModelFactory _publishedModelFactory; + private readonly UrlSegmentProviderCollection _urlSegmentProviders; // volatile because we read it with no lock private volatile bool _isReady; @@ -89,7 +90,8 @@ namespace Umbraco.Web.PublishedCache.NuCache IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IDefaultCultureAccessor defaultCultureAccessor, IDataSource dataSource, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, - IEntityXmlSerializer entitySerializer, IPublishedModelFactory publishedModelFactory) + IEntityXmlSerializer entitySerializer, IPublishedModelFactory publishedModelFactory, + UrlSegmentProviderCollection urlSegmentProviders) : base(publishedSnapshotAccessor, variationContextAccessor) { //if (Interlocked.Increment(ref _singletonCheck) > 1) @@ -107,7 +109,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _defaultCultureAccessor = defaultCultureAccessor; _globalSettings = globalSettings; _siteDomainHelper = siteDomainHelper; - _publishedModelFactory = publishedModelFactory; + _urlSegmentProviders = urlSegmentProviders; // we need an Xml serializer here so that the member cache can support XPath, // for members this is done by navigating the serialized-to-xml member @@ -167,7 +169,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _domainStore = new SnapDictionary(); - _publishedModelFactory.WithSafeLiveFactory(LoadCaches); + publishedModelFactory.WithSafeLiveFactory(LoadCaches); Guid GetUid(ContentStore store, int id) => store.LiveSnapshot.Get(id)?.Uid ?? default; int GetId(ContentStore store, Guid uid) => store.LiveSnapshot.Get(uid)?.Id ?? default; @@ -1269,7 +1271,13 @@ namespace Umbraco.Web.PublishedCache.NuCache foreach (var cultureInfo in infos) { var cultureIsDraft = !published && content is IContent d && d.IsCultureEdited(cultureInfo.Culture); - cultureData[cultureInfo.Culture] = new CultureVariation { Name = cultureInfo.Name, Date = content.GetUpdateDate(cultureInfo.Culture) ?? DateTime.MinValue, IsDraft = cultureIsDraft }; + cultureData[cultureInfo.Culture] = new CultureVariation + { + Name = cultureInfo.Name, + UrlSegment = content.GetUrlSegment(_urlSegmentProviders, cultureInfo.Culture), + Date = content.GetUpdateDate(cultureInfo.Culture) ?? DateTime.MinValue, + IsDraft = cultureIsDraft + }; } } @@ -1277,7 +1285,8 @@ namespace Umbraco.Web.PublishedCache.NuCache var nestedData = new ContentNestedData { PropertyData = propertyData, - CultureData = cultureData + CultureData = cultureData, + UrlSegment = content.GetUrlSegment(_urlSegmentProviders) }; var dto = new ContentNuDto