diff --git a/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs index ee88029ef7..b91e87907a 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs @@ -456,6 +456,100 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Web.BackOffice.Controllers Assert.AreEqual(expectedMessage, display.Notifications.FirstOrDefault(x => x.NotificationType == NotificationStyle.Warning)?.Message); }); } + + [Test] + public async Task PostSave_Validates_All_Ancestor_Cultures_Are_Considered() + { + var sweIso = "sv-SE"; + ILocalizationService localizationService = GetRequiredService(); + //Create 2 new languages + localizationService.Save(new LanguageBuilder() + .WithCultureInfo(DkIso) + .WithIsDefault(false) + .Build()); + + localizationService.Save(new LanguageBuilder() + .WithCultureInfo(sweIso) + .WithIsDefault(false) + .Build()); + + IContentTypeService contentTypeService = GetRequiredService(); + IContentType contentType = new ContentTypeBuilder().WithContentVariation(ContentVariation.Culture).Build(); + contentTypeService.Save(contentType); + + Content content = new ContentBuilder() + .WithoutIdentity() + .WithContentType(contentType) + .WithCultureName(UsIso, "Root") + .Build(); + + IContentService contentService = GetRequiredService(); + contentService.SaveAndPublish(content); + + Content childContent = new ContentBuilder() + .WithoutIdentity() + .WithContentType(contentType) + .WithParent(content) + .WithCultureName(DkIso, "Barn") + .WithCultureName(UsIso, "Child") + .Build(); + + contentService.SaveAndPublish(childContent); + + Content grandChildContent = new ContentBuilder() + .WithoutIdentity() + .WithContentType(contentType) + .WithParent(childContent) + .WithCultureName(sweIso, "Bjarn") + .Build(); + + + ContentItemSave model = new ContentItemSaveBuilder() + .WithContent(grandChildContent) + .WithParentId(childContent.Id) + .WithAction(ContentSaveAction.PublishNew) + .Build(); + + ILanguage enLanguage = localizationService.GetLanguageByIsoCode(UsIso); + IDomainService domainService = GetRequiredService(); + var enDomain = new UmbracoDomain("/en") + { + RootContentId = content.Id, + LanguageId = enLanguage.Id + }; + domainService.Save(enDomain); + + ILanguage dkLanguage = localizationService.GetLanguageByIsoCode(DkIso); + var dkDomain = new UmbracoDomain("/dk") + { + RootContentId = childContent.Id, + LanguageId = dkLanguage.Id + }; + domainService.Save(dkDomain); + + var url = PrepareApiControllerUrl(x => x.PostSave(null)); + + HttpResponseMessage response = await Client.PostAsync(url, new MultipartFormDataContent + { + { new StringContent(JsonConvert.SerializeObject(model)), "contentItem" } + }); + + var body = await response.Content.ReadAsStringAsync(); + body = body.TrimStart(AngularJsonMediaTypeFormatter.XsrfPrefix); + ContentItemDisplay display = JsonConvert.DeserializeObject(body); + + + ILocalizedTextService localizedTextService = GetRequiredService(); + var expectedMessage = localizedTextService.Localize("speechBubbles", "publishWithMissingDomain", new []{"sv-SE"}); + + Assert.Multiple(() => + { + Assert.NotNull(display); + Assert.AreEqual(1, display.Notifications.Count(x => x.NotificationType == NotificationStyle.Warning)); + Assert.AreEqual(expectedMessage, display.Notifications.FirstOrDefault(x => x.NotificationType == NotificationStyle.Warning)?.Message); + }); + } + [Test] public async Task PostSave_Validates_All_Cultures_Has_Domains() { diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs index 3f051d0a35..70678545d9 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs @@ -1456,7 +1456,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return; } - var publishedCultures = persistedContent.PublishedCultures.ToList(); + var publishedCultures = GetPublishedCulturesFromAncestors(persistedContent).ToList(); // If only a single culture is published we shouldn't have any routing issues if (publishedCultures.Count < 2) { @@ -1583,6 +1583,27 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return true; } + private IEnumerable GetPublishedCulturesFromAncestors(IContent content) + { + if (content.ParentId == -1) + { + return content.PublishedCultures; + } + + HashSet publishedCultures = new (); + publishedCultures.UnionWith(content.PublishedCultures); + + IEnumerable ancestorIds = content.GetAncestorIds(); + + foreach (var id in ancestorIds) + { + IEnumerable cultures = _contentService.GetById(id).PublishedCultures; + publishedCultures.UnionWith(cultures); + } + + return publishedCultures; + + } /// /// Adds a generic culture error for use in displaying the culture validation error in the save/publish/etc... dialogs ///