Publishing in the Management API (#14774)

* make CoreScopeProvider available for derived classes

* Create publish controller

* Add publish functionality

* Remove unneeded using

* Implement publish for multiple cultures

* support multiple cultures in controler

* Dont validate properties

* Refactor to use PublishingOperationStatus

* refactor to use proper publish async methods

* Refactor publish logic into own service

* Commit some demo code

* Add notes about what errors can happen when publishing

* Rework ContentPublishingService and introduce explicit Publish and PublishBranch methods in ContentService

* Fix merge

* Allow the publishing strategy to do its job

* Improved check for unsaved changes

* Make the old content controller work (as best possible)

* Remove SaveAndPublish (SaveAndPublishBranch) from all tests

* Proper guards for invalid cultures when publishing

* Fix edge cases for property validation and content unpublishing + add unpublishing to ContentPublishingService

* Clear out a few TODOs - we'll accept the behavior for now

* Unpublish controller

* Fix merge

* Fix branch publish notifications

* Added extra test for publishing unpublished cultures and added FIXME comments for when we fix the state of published cultures in content

---------

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
Co-authored-by: Zeegaan <nge@umbraco.dk>
This commit is contained in:
Kenn Jacobsen
2023-11-22 12:52:08 +01:00
committed by GitHub
parent 42dd2da579
commit 012b43a1c2
41 changed files with 2017 additions and 567 deletions

View File

@@ -78,7 +78,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
protected override void CustomTestSetup(IUmbracoBuilder builder) => builder
.AddNotificationHandler<ContentPublishingNotification, ContentNotificationHandler>()
.AddNotificationHandler<ContentCopyingNotification, ContentNotificationHandler>()
.AddNotificationHandler<ContentCopiedNotification, ContentNotificationHandler>();
.AddNotificationHandler<ContentCopiedNotification, ContentNotificationHandler>()
.AddNotificationHandler<ContentSavingNotification, ContentNotificationHandler>();
[Test]
public void Create_Blueprint()
@@ -228,7 +229,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
}
else
{
var r = ContentService.SaveAndPublish(c);
ContentService.Save(c);
var r = ContentService.Publish(c, c.AvailableCultures.ToArray());
var contentSchedule =
ContentScheduleCollection.CreateWithEntry(null, now.AddSeconds(5)); // expire in 5 seconds
@@ -261,7 +263,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
}
else
{
var r = ContentService.SaveAndPublish(c);
ContentService.Save(c);
var r = ContentService.Publish(c, c.AvailableCultures.ToArray());
var contentSchedule =
ContentScheduleCollection.CreateWithEntry(alternatingCulture, null,
@@ -331,7 +334,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
contentSchedule = ContentService.GetContentScheduleByContentId(content.Id);
sched = contentSchedule.FullSchedule;
Assert.AreEqual(0, sched.Count);
Assert.IsTrue(ContentService.SaveAndPublish(content).Success);
Assert.IsTrue(ContentService.Publish(content, content.AvailableCultures.ToArray()).Success);
}
[Test]
@@ -344,7 +347,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
for (var i = 0; i < 20; i++)
{
content.SetValue("bodyText", "hello world " + Guid.NewGuid());
ContentService.SaveAndPublish(content);
ContentService.Save(content);
ContentService.Publish(content, content.AvailableCultures.ToArray());
}
// Assert
@@ -578,7 +582,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
{
var parent = ContentService.GetById(Textpage.Id);
Assert.IsFalse(parent.Published);
ContentService.SaveAndPublish(parent); // publishing parent, so Text Page 2 can be updated.
ContentService.Save(parent); // publishing parent, so Text Page 2 can be updated.
ContentService.Publish(parent, parent.AvailableCultures.ToArray());
var content = ContentService.GetById(Subpage.Id);
Assert.IsFalse(content.Published);
@@ -590,14 +595,16 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
content.Name = "Text Page 2 Updated";
content.SetValue("author", "Jane Doe");
ContentService.SaveAndPublish(content); // publishes the current version, creates a version
ContentService.Save(content); // publishes the current version, creates a version
ContentService.Publish(content, content.AvailableCultures.ToArray());
var version2 = content.VersionId;
Console.WriteLine($"2 e={content.VersionId} p={content.PublishedVersionId}");
content.Name = "Text Page 2 ReUpdated";
content.SetValue("author", "Bob Hope");
ContentService.SaveAndPublish(content); // publishes again, creates a version
ContentService.Save(content); // publishes again, creates a version
ContentService.Publish(content, content.AvailableCultures.ToArray());
var version3 = content.VersionId;
Console.WriteLine($"3 e={content.VersionId} p={content.PublishedVersionId}");
@@ -664,11 +671,11 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
{
// Arrange
var root = ContentService.GetById(Textpage.Id);
ContentService.SaveAndPublish(root);
ContentService.Publish(root!, root!.AvailableCultures.ToArray());
var content = ContentService.GetById(Subpage.Id);
var contentSchedule = ContentScheduleCollection.CreateWithEntry(null, DateTime.Now.AddSeconds(1));
ContentService.PersistContentSchedule(content, contentSchedule);
ContentService.SaveAndPublish(content);
ContentService.PersistContentSchedule(content!, contentSchedule);
ContentService.Publish(content, content.AvailableCultures.ToArray());
// Act
Thread.Sleep(new TimeSpan(0, 0, 0, 2));
@@ -712,7 +719,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
{
// Arrange
var content = ContentService.GetById(Textpage.Id);
var published = ContentService.SaveAndPublish(content, userId: -1);
Assert.IsNotNull(content);
var published = ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: -1);
// Act
var unpublished = ContentService.Unpublish(content, userId: -1);
@@ -730,17 +738,14 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
var content = CreateEnglishAndFrenchDocument(out var langUk, out var langFr,
out var contentType);
content.PublishCulture(CultureImpact.Explicit(langFr.IsoCode, langFr.IsDefault));
content.PublishCulture(CultureImpact.Explicit(langUk.IsoCode, langUk.IsDefault));
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
var published = ContentService.SaveAndPublish(content, new[] { langFr.IsoCode, langUk.IsoCode });
var saved = ContentService.Save(content);
var published = ContentService.Publish(content, new[] { langFr.IsoCode, langUk.IsoCode });
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
// re-get
content = ContentService.GetById(content.Id);
Assert.IsTrue(saved.Success);
Assert.IsTrue(published.Success);
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
@@ -764,7 +769,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
var content = CreateEnglishAndFrenchDocument(out var langUk, out var langFr,
out var contentType);
var published = ContentService.SaveAndPublish(content, new[] { langFr.IsoCode, langUk.IsoCode });
ContentService.Save(content);
var published = ContentService.Publish(content, new[] { langFr.IsoCode, langUk.IsoCode });
Assert.AreEqual(PublishedState.Published, content.PublishedState);
// re-get
@@ -786,7 +792,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
content = ContentService.GetById(content.Id);
published = ContentService.SaveAndPublish(content, langUk.IsoCode);
published = ContentService.Publish(content, new[] { langUk.IsoCode });
Assert.AreEqual(PublishedState.Published, content.PublishedState);
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
@@ -803,9 +809,11 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
var content = CreateEnglishAndFrenchDocument(out var langUk, out var langFr,
out var contentType);
var published = ContentService.SaveAndPublish(content, new[] { langFr.IsoCode, langUk.IsoCode });
var saved = ContentService.Save(content);
var published = ContentService.Publish(content, new[] { langFr.IsoCode, langUk.IsoCode });
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
Assert.IsTrue(saved.Success);
Assert.IsTrue(published.Success);
Assert.AreEqual(PublishedState.Published, content.PublishedState);
@@ -865,9 +873,11 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
content.SetCultureName("content-fr", langFr.IsoCode);
content.SetCultureName("content-en", langUk.IsoCode);
var published = ContentService.SaveAndPublish(content, new[] { langFr.IsoCode, langUk.IsoCode });
var saved = ContentService.Save(content);
var published = ContentService.Publish(content, new[] { langFr.IsoCode, langUk.IsoCode });
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
Assert.IsTrue(saved.Success);
Assert.IsTrue(published.Success);
Assert.AreEqual(PublishedState.Published, content.PublishedState);
@@ -888,9 +898,11 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
var content = CreateEnglishAndFrenchDocument(out var langUk, out var langFr,
out var contentType);
var published = ContentService.SaveAndPublish(content, new[] { langFr.IsoCode, langUk.IsoCode });
var saved = ContentService.Save(content);
var published = ContentService.Publish(content, new[] { langFr.IsoCode, langUk.IsoCode });
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
Assert.IsTrue(saved.Success);
Assert.IsTrue(published.Success);
Assert.AreEqual(PublishedState.Published, content.PublishedState);
@@ -924,9 +936,11 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
var content = CreateEnglishAndFrenchDocument(out var langUk, out var langFr,
out var contentType);
var published = ContentService.SaveAndPublish(content, new[] { langFr.IsoCode, langUk.IsoCode });
var saved = ContentService.Save(content);
var published = ContentService.Publish(content, new[] { langFr.IsoCode, langUk.IsoCode });
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
Assert.IsTrue(saved.Success);
Assert.IsTrue(published.Success);
Assert.AreEqual(PublishedState.Published, content.PublishedState);
@@ -936,8 +950,10 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
// Change some data since SaveAndPublish should always Save
content.SetCultureName("content-en-updated", langUk.IsoCode);
var saved = ContentService.SaveAndPublish(content, new string[] { }); // save without cultures
Assert.AreEqual(PublishResultType.FailedPublishNothingToPublish, saved.Result);
saved = ContentService.Save(content);
published = ContentService.Publish(content, new string[] { }); // publish without cultures
Assert.IsTrue(saved.Success);
Assert.AreEqual(PublishResultType.FailedPublishNothingToPublish, published.Result);
// re-get
content = ContentService.GetById(content.Id);
@@ -977,7 +993,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
content.SetCultureName("content-en", langGb.IsoCode);
content.SetCultureName("content-fr", langFr.IsoCode);
Assert.IsTrue(ContentService.SaveAndPublish(content, new[] { langGb.IsoCode, langFr.IsoCode }).Success);
Assert.IsTrue(ContentService.Save(content).Success);
Assert.IsTrue(ContentService.Publish(content, new[] { langGb.IsoCode, langFr.IsoCode }).Success);
// re-get
content = ContentService.GetById(content.Id);
@@ -1007,7 +1024,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
IContent content = new Content("content", Constants.System.Root, contentType);
content.SetCultureName("content-fr", langFr.IsoCode);
var published = ContentService.SaveAndPublish(content, langFr.IsoCode);
ContentService.Save(content);
var published = ContentService.Publish(content, new[] { langFr.IsoCode });
// audit log will only show that french was published
var lastLog = AuditService.GetLogs(content.Id).Last();
@@ -1016,7 +1034,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
// re-get
content = ContentService.GetById(content.Id);
content.SetCultureName("content-en", langUk.IsoCode);
published = ContentService.SaveAndPublish(content, langUk.IsoCode);
ContentService.Save(content);
published = ContentService.Publish(content, new[] { langUk.IsoCode });
// audit log will only show that english was published
lastLog = AuditService.GetLogs(content.Id).Last();
@@ -1046,7 +1065,9 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
IContent content = new Content("content", Constants.System.Root, contentType);
content.SetCultureName("content-fr", langFr.IsoCode);
content.SetCultureName("content-gb", langGb.IsoCode);
var published = ContentService.SaveAndPublish(content, new[] { langGb.IsoCode, langFr.IsoCode });
var saved = ContentService.Save(content);
var published = ContentService.Publish(content, new[] { langGb.IsoCode, langFr.IsoCode });
Assert.IsTrue(saved.Success);
Assert.IsTrue(published.Success);
// re-get
@@ -1073,9 +1094,10 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
{
// Arrange
var content = ContentService.GetById(Textpage.Id);
Assert.IsNotNull(content);
// Act
var published = ContentService.SaveAndPublish(content, userId: Constants.Security.SuperUserId);
var published = ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: Constants.Security.SuperUserId);
// Assert
Assert.That(published.Success, Is.True);
@@ -1087,9 +1109,10 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
{
// Arrange
var content = ContentService.GetById(Textpage.Id);
Assert.IsNotNull(content);
// Act
var published = ContentService.SaveAndPublish(content, userId: -1);
var published = ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: -1);
// Assert
Assert.That(published.Success, Is.True);
@@ -1102,7 +1125,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
// Arrange
var parent = ContentService.Create("parent", Constants.System.Root, "umbTextpage");
ContentService.SaveAndPublish(parent);
ContentService.Save(parent);
ContentService.Publish(parent, parent.AvailableCultures.ToArray());
var content = ContentService.Create("child", parent, "umbTextpage");
ContentService.Save(content);
@@ -1114,31 +1138,39 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
[Test]
public void Can_Publish_Content_WithEvents()
{
var savingWasCalled = false;
var publishingWasCalled = false;
ContentNotificationHandler.SavingContent = notification =>
{
Assert.AreEqual(1, notification.SavedEntities.Count());
var entity = notification.SavedEntities.First();
Assert.AreEqual("foo", entity.Name);
var e = ContentService.GetById(entity.Id);
Assert.AreEqual("Home", e.Name);
savingWasCalled = true;
};
ContentNotificationHandler.PublishingContent = notification =>
{
Assert.AreEqual(1, notification.PublishedEntities.Count());
var entity = notification.PublishedEntities.First();
Assert.AreEqual("foo", entity.Name);
var e = ContentService.GetById(entity.Id);
Assert.AreEqual("Home", e.Name);
publishingWasCalled = true;
};
// tests that during 'publishing' event, what we get from the repo is the 'old' content,
// because 'publishing' fires before the 'saved' event ie before the content is actually
// saved
try
{
var content = ContentService.GetById(Textpage.Id);
Assert.AreEqual("Home", content.Name);
content.Name = "foo";
ContentService.Save(content);
var published =
ContentService.SaveAndPublish(content, userId: Constants.Security.SuperUserId);
ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: Constants.Security.SuperUserId);
Assert.That(published.Success, Is.True);
Assert.That(content.Published, Is.True);
@@ -1146,29 +1178,49 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
var e = ContentService.GetById(content.Id);
Assert.AreEqual("foo", e.Name);
Assert.IsTrue(savingWasCalled);
Assert.IsTrue(publishingWasCalled);
}
finally
{
ContentNotificationHandler.SavingContent = null;
ContentNotificationHandler.PublishingContent = null;
}
}
[Test]
public void Can_Not_Publish_Invalid_Cultures()
public void Can_Not_Publish_Invalid_Cultures_For_Variant_Content()
{
var content = new ContentBuilder()
.AddContentType()
.WithContentVariation(ContentVariation.Culture)
.Done()
.Build();
var contentType = ContentTypeBuilder.CreateBasicContentType();
contentType.Variations = ContentVariation.Culture;
ContentTypeService.Save(contentType);
Assert.Throws<InvalidOperationException>(() => ContentService.SaveAndPublish(content, new[] { "*" }));
Assert.Throws<InvalidOperationException>(
() => ContentService.SaveAndPublish(content, new string[] { null }));
Assert.Throws<InvalidOperationException>(() => ContentService.SaveAndPublish(content, new[] { "*", null }));
Assert.Throws<InvalidOperationException>(() =>
ContentService.SaveAndPublish(content, new[] { "en-US", "*", "es-ES" }));
var content = ContentBuilder.CreateBasicContent(contentType);
content.SetCultureName("Name for en-US", "en-US");
ContentService.Save(content);
Assert.Throws<ArgumentNullException>(() => ContentService.Publish(content, null!));
Assert.Throws<ArgumentException>(() => ContentService.Publish(content, new string[] { null }));
Assert.Throws<ArgumentException>(() => ContentService.Publish(content, new [] { string.Empty }));
Assert.Throws<ArgumentException>(() => ContentService.Publish(content, new[] { "*", null }));
Assert.Throws<ArgumentException>(() => ContentService.Publish(content, new[] { "en-US", "*" }));
}
[Test]
public void Can_Not_Publish_Invalid_Cultures_For_Invariant_Content()
{
var contentType = ContentTypeBuilder.CreateBasicContentType();
ContentTypeService.Save(contentType);
var content = ContentBuilder.CreateBasicContent(contentType);
content.Name = "Content name";
ContentService.Save(content);
Assert.Throws<ArgumentNullException>(() => ContentService.Publish(content, null!));
Assert.Throws<ArgumentException>(() => ContentService.Publish(content, new string[] { null }));
Assert.Throws<ArgumentException>(() => ContentService.Publish(content, new[] { "*", null }));
Assert.Throws<ArgumentException>(() => ContentService.Publish(content, new[] { "en-US" }));
Assert.Throws<ArgumentException>(() => ContentService.Publish(content, new[] { "en-US", "*" }));
}
[Test]
@@ -1185,7 +1237,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
var parent = ContentService.GetById(parentId);
var parentPublished = ContentService.SaveAndPublish(parent);
ContentService.Save(parent);
var parentPublished = ContentService.Publish(parent, parent.AvailableCultures.ToArray());
// parent can publish values
// and therefore can be published
@@ -1206,7 +1259,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
// and therefore cannot be published,
// because it did not have a published version at all
var contentPublished = ContentService.SaveAndPublish(content);
ContentService.Save(content);
var contentPublished = ContentService.Publish(content, content.AvailableCultures.ToArray());
Assert.IsFalse(contentPublished.Success);
Assert.AreEqual(PublishResultType.FailedPublishContentInvalid, contentPublished.Result);
Assert.IsFalse(content.Published);
@@ -1305,7 +1359,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
// publish parent & its branch
// only those that are not already published
// only invariant/neutral values
var parentPublished = ContentService.SaveAndPublishBranch(parent, true);
var parentPublished = ContentService.PublishBranch(parent, true, parent.AvailableCultures.ToArray());
foreach (var result in parentPublished)
{
@@ -1334,13 +1388,15 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
ContentService.Save(content, contentSchedule: contentSchedule);
var parent = ContentService.GetById(Textpage.Id);
Assert.IsNotNull(parent);
var parentPublished =
ContentService.SaveAndPublish(parent,
ContentService.Publish(parent,
parent.AvailableCultures.ToArray(),
userId: Constants.Security
.SuperUserId); // Publish root Home node to enable publishing of 'Subpage.Id'
// Act
var published = ContentService.SaveAndPublish(content, userId: Constants.Security.SuperUserId);
var published = ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: Constants.Security.SuperUserId);
// Assert
Assert.That(parentPublished.Success, Is.True);
@@ -1361,7 +1417,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
var contentSchedule = ContentScheduleCollection.CreateWithEntry("en-US", null, DateTime.Now.AddMinutes(-5));
ContentService.Save(content, contentSchedule: contentSchedule);
var published = ContentService.SaveAndPublish(content, "en-US");
var published = ContentService.Publish(content, new[] { "en-US" });
Assert.IsFalse(published.Success);
Assert.AreEqual(PublishResultType.FailedPublishCultureHasExpired, published.Result);
@@ -1377,13 +1433,15 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
ContentService.Save(content, Constants.Security.SuperUserId, contentSchedule);
var parent = ContentService.GetById(Textpage.Id);
Assert.IsNotNull(parent);
var parentPublished =
ContentService.SaveAndPublish(parent,
ContentService.Publish(parent,
parent.AvailableCultures.ToArray(),
userId: Constants.Security
.SuperUserId); // Publish root Home node to enable publishing of 'Subpage.Id'
// Act
var published = ContentService.SaveAndPublish(content, userId: Constants.Security.SuperUserId);
var published = ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: Constants.Security.SuperUserId);
// Assert
Assert.That(parentPublished.Success, Is.True);
@@ -1422,7 +1480,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
.Done()
.Build();
contentService.SaveAndPublish(content);
contentService.Save(content);
contentService.Publish(content, Array.Empty<string>());
content.Properties[0].SetValue("Foo", string.Empty);
contentService.Save(content);
@@ -1430,7 +1489,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
ContentScheduleCollection.CreateWithEntry(DateTime.Now.AddHours(2), null));
// Act
var result = contentService.SaveAndPublish(content, userId: Constants.Security.SuperUserId);
var result = contentService.Publish(content, Array.Empty<string>(), userId: Constants.Security.SuperUserId);
// Assert
Assert.Multiple(() =>
@@ -1475,14 +1534,15 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
.Done()
.Build();
contentService.SaveAndPublish(content);
contentService.Save(content);
contentService.Publish(content, Array.Empty<string>());
contentService.PersistContentSchedule(content,
ContentScheduleCollection.CreateWithEntry(DateTime.Now.AddHours(2), null));
contentService.Save(content);
// Act
var result = contentService.SaveAndPublish(content, userId: Constants.Security.SuperUserId);
var result = contentService.Publish(content, Array.Empty<string>(), userId: Constants.Security.SuperUserId);
// Assert
Assert.Multiple(() =>
@@ -1509,7 +1569,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
var contentSchedule = ContentScheduleCollection.CreateWithEntry("en-US", DateTime.Now.AddHours(2), null);
ContentService.Save(content, contentSchedule: contentSchedule);
var published = ContentService.SaveAndPublish(content, "en-US");
var published = ContentService.Publish(content, new[] { "en-US" });
Assert.IsFalse(published.Success);
Assert.AreEqual(PublishResultType.FailedPublishCultureAwaitingRelease, published.Result);
@@ -1524,7 +1584,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
ContentService.Save(content);
// Act
var published = ContentService.SaveAndPublishBranch(content, true);
var published = ContentService.PublishBranch(content, true, content.AvailableCultures.ToArray());
// Assert
Assert.That(published.All(x => x.Success), Is.False);
@@ -1536,9 +1596,10 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
{
// Arrange
var content = ContentService.GetById(Trashed.Id);
Assert.IsNotNull(content);
// Act
var published = ContentService.SaveAndPublish(content, userId: Constants.Security.SuperUserId);
var published = ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: Constants.Security.SuperUserId);
// Assert
Assert.That(published.Success, Is.False);
@@ -1554,12 +1615,14 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
content.SetValue("author", "Barack Obama");
// Act
var published = ContentService.SaveAndPublish(content, userId: Constants.Security.SuperUserId);
var saved = ContentService.Save(content, userId: Constants.Security.SuperUserId);
var published = ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: Constants.Security.SuperUserId);
// Assert
Assert.That(content.HasIdentity, Is.True);
Assert.That(content.Published, Is.True);
Assert.IsTrue(published.Success);
Assert.IsTrue(saved.Success);
}
/// <summary>
@@ -1577,15 +1640,17 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
content.SetValue("author", "Barack Obama");
// Act
var published = ContentService.SaveAndPublish(content, userId: Constants.Security.SuperUserId);
var saved = ContentService.Save(content, userId: Constants.Security.SuperUserId);
var published = ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: Constants.Security.SuperUserId);
var childContent = ContentService.Create("Child", content.Id, "umbTextpage");
// Reset all identity properties
childContent.Id = 0;
childContent.Path = null;
((Content)childContent).ResetIdentity();
var childSaved = ContentService.Save(childContent, userId: Constants.Security.SuperUserId);
var childPublished =
ContentService.SaveAndPublish(childContent, userId: Constants.Security.SuperUserId);
ContentService.Publish(childContent, childContent.AvailableCultures.ToArray(), userId: Constants.Security.SuperUserId);
// Assert
Assert.That(content.HasIdentity, Is.True);
@@ -1594,6 +1659,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
Assert.That(childContent.Published, Is.True);
Assert.That(published.Success, Is.True);
Assert.That(childPublished.Success, Is.True);
Assert.That(saved.Success, Is.True);
Assert.That(childSaved.Success, Is.True);
}
[Test]
@@ -1601,12 +1668,13 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
public void Can_Get_Published_Descendant_Versions()
{
// Arrange
var root = ContentService.GetById(Textpage.Id);
var rootPublished = ContentService.SaveAndPublish(root);
var root = ContentService.GetById(Textpage.Id)!;
var rootPublished = ContentService.Publish(root, root.AvailableCultures.ToArray());
var content = ContentService.GetById(Subpage.Id);
content.Properties["title"].SetValue(content.Properties["title"].GetValue() + " Published");
var contentPublished = ContentService.SaveAndPublish(content);
ContentService.Save(content);
var contentPublished = ContentService.Publish(content, content.AvailableCultures.ToArray());
var publishedVersion = content.VersionId;
content.Properties["title"].SetValue(content.Properties["title"].GetValue() + " Saved");
@@ -1667,14 +1735,16 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
IContent content = ContentBuilder.CreateSimpleContent(contentType, "hello");
content.SetValue("title", "title of mine");
content.SetValue("bodyText", "hello world");
ContentService.SaveAndPublish(content);
ContentService.Save(content);
ContentService.Publish(content, content.AvailableCultures.ToArray());
// re-get
content = ContentService.GetById(content.Id);
content.SetValue("title", "another title of mine"); // Change a value
content.SetValue("bodyText", null); // Clear a value
content.SetValue("author", "new author"); // Add a value
ContentService.SaveAndPublish(content);
ContentService.Save(content);
ContentService.Publish(content, content.AvailableCultures.ToArray());
// re-get
content = ContentService.GetById(content.Id);
@@ -1979,12 +2049,12 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
content1.PropertyValues(obj);
content1.ResetDirtyProperties(false);
ContentService.Save(content1);
Assert.IsTrue(ContentService.SaveAndPublish(content1, userId: -1).Success);
Assert.IsTrue(ContentService.Publish(content1, content1.AvailableCultures.ToArray(), userId: -1).Success);
var content2 = ContentBuilder.CreateBasicContent(contentType);
content2.PropertyValues(obj);
content2.ResetDirtyProperties(false);
ContentService.Save(content2);
Assert.IsTrue(ContentService.SaveAndPublish(content2, userId: -1).Success);
Assert.IsTrue(ContentService.Publish(content2, content2.AvailableCultures.ToArray(), userId: -1).Success);
var editorGroup = UserService.GetUserGroupByAlias(Constants.Security.EditorGroupAlias);
editorGroup.StartContentId = content1.Id;
@@ -2188,7 +2258,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
Assert.AreEqual(0, contentTags.Length);
// publish
ContentService.SaveAndPublish(content);
ContentService.Publish(content, new []{ "*" });
// now tags have been set (published)
Assert.AreEqual("[\"hello\",\"world\"]", content.GetValue(propAlias));
@@ -2204,7 +2274,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
Assert.AreEqual(0, copiedTags.Length);
// publish
ContentService.SaveAndPublish(copy);
ContentService.Publish(copy, new []{ "*" });
// now tags have been set (published)
copiedTags = TagService.GetTagsForEntity(copy.Id).ToArray();
@@ -2220,7 +2290,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
// Arrange
var parent = ContentService.GetById(Textpage.Id);
Assert.IsFalse(parent.Published);
ContentService.SaveAndPublish(parent); // publishing parent, so Text Page 2 can be updated.
ContentService.Save(parent);
ContentService.Publish(parent, parent.AvailableCultures.ToArray()); // publishing parent, so Text Page 2 can be updated.
var content = ContentService.GetById(Subpage.Id);
Assert.IsFalse(content.Published);
@@ -2236,7 +2307,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
// non published = edited
Assert.IsTrue(content.Edited);
ContentService.SaveAndPublish(content); // new version
ContentService.Save(content);
ContentService.Publish(content, content.AvailableCultures.ToArray()); // new version
var version2 = content.VersionId;
Assert.AreNotEqual(version1, version2);
@@ -2261,7 +2333,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
content.Name = "Text Page 2 ReReUpdated";
ContentService.SaveAndPublish(content); // new version
ContentService.Save(content);
ContentService.Publish(content, content.AvailableCultures.ToArray()); // new version
var version3 = content.VersionId;
Assert.AreNotEqual(version2, version3);
@@ -2318,7 +2391,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
content = ContentService.GetById(content.Id);
Assert.AreEqual("Text Page 2 ReReUpdated", content.Name);
Assert.AreEqual("Jane Doe", content.GetValue<string>("author"));
ContentService.SaveAndPublish(content);
ContentService.Save(content);
ContentService.Publish(content, content.AvailableCultures.ToArray());
Assert.IsFalse(content.Edited);
content.Name = "Xxx";
content.SetValue("author", "Bob Doe");
@@ -2375,7 +2449,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
page.SetValue(p1.Alias, "v1fr", langFr.IsoCode);
page.SetValue(p1.Alias, "v1da", langDa.IsoCode);
Thread.Sleep(1);
ContentService.SaveAndPublish(page);
ContentService.Save(page);
ContentService.Publish(page, page.AvailableCultures.ToArray());
var versionId1 = page.VersionId;
Thread.Sleep(10);
@@ -2383,7 +2458,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
page.SetCultureName("fr2", langFr.IsoCode);
page.SetValue(p1.Alias, "v2fr", langFr.IsoCode);
Thread.Sleep(1);
ContentService.SaveAndPublish(page, langFr.IsoCode);
ContentService.Save(page);
ContentService.Publish(page, new[] { langFr.IsoCode });
var versionId2 = page.VersionId;
Thread.Sleep(10);
@@ -2391,7 +2467,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
page.SetCultureName("da2", langDa.IsoCode);
page.SetValue(p1.Alias, "v2da", langDa.IsoCode);
Thread.Sleep(1);
ContentService.SaveAndPublish(page, langDa.IsoCode);
ContentService.Save(page);
ContentService.Publish(page, new[] { langDa.IsoCode });
var versionId3 = page.VersionId;
Thread.Sleep(10);
@@ -2401,7 +2478,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
page.SetValue(p1.Alias, "v3fr", langFr.IsoCode);
page.SetValue(p1.Alias, "v3da", langDa.IsoCode);
Thread.Sleep(1);
ContentService.SaveAndPublish(page);
ContentService.Save(page);
ContentService.Publish(page, page.AvailableCultures.ToArray());
var versionId4 = page.VersionId;
// now get all versions
@@ -2778,7 +2856,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
// becomes Published, !Edited
// creates a new version
// can get published property values
ContentService.SaveAndPublish(content);
ContentService.Publish(content, new []{ "*" });
Assert.IsTrue(content.Published);
Assert.IsFalse(content.Edited);
@@ -2830,7 +2908,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
// and therefore we cannot "just" republish the content - we need to publish some values
// so... that's not really an option
//
// ContentService.SaveAndPublish(content);
// ContentService.Publish(content, new []{ "*" });
// TODO: what shall we do of all this?
/*
@@ -3161,7 +3239,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false));
// Act
ContentService.SaveAndPublish(content, new[] { langFr.IsoCode, langUk.IsoCode });
ContentService.Publish(content, new[] { langFr.IsoCode, langUk.IsoCode });
// both FR and UK have been published,
// and content has been published,
@@ -3211,7 +3289,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
// note that content and content2 culture published dates might be slightly different due to roundtrip to database
// Act
ContentService.SaveAndPublish(content);
ContentService.Publish(content, new []{ "*" });
// now it has publish name for invariant neutral
content2 = ContentService.GetById(content.Id);
@@ -3413,7 +3491,7 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
(langUk, false)); // FR, DE would throw
// Act
ContentService.SaveAndPublish(content, langUk.IsoCode);
ContentService.Publish(content, new[] { langUk.IsoCode });
content2 = ContentService.GetById(content.Id);
@@ -3453,6 +3531,78 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
Assert.IsTrue(content2.IsCultureEdited(langUk.IsoCode));
}
[Test]
public void Cannot_Publish_Newly_Created_Unsaved_Content()
{
var content = ContentService.Create("Test", Constants.System.Root, "umbTextpage");
var publishResult = ContentService.Publish(content, new[] { "*" });
Assert.AreEqual(PublishResultType.FailedPublishUnsavedChanges, publishResult.Result);
}
[Test]
public void Cannot_Publish_Unsaved_Content()
{
var content = ContentService.Create("Test", Constants.System.Root, "umbTextpage");
ContentService.Save(content);
content.Name = "Test2";
var publishResult = ContentService.Publish(content, new[] { "*" });
Assert.AreEqual(PublishResultType.FailedPublishUnsavedChanges, publishResult.Result);
}
[Test]
public async Task Cannot_Publish_Invalid_Variant_Content()
{
var (langEn, langDa, contentType) = await SetupVariantTest();
IContent content = new ContentBuilder()
.WithContentType(contentType)
.WithCultureName(langEn.IsoCode, "EN root")
.WithCultureName(langDa.IsoCode, "DA root")
.Build();
content.SetValue("title", "EN title", culture: langEn.IsoCode);
content.SetValue("title", null, culture: langDa.IsoCode);
ContentService.Save(content);
// reset any state and attempt a publish
content = ContentService.GetById(content.Key)!;
var result = ContentService.Publish(content, new[] { "*" });
Assert.IsFalse(result.Success);
Assert.AreEqual(PublishResultType.FailedPublishContentInvalid, result.Result);
// verify saved state
content = ContentService.GetById(content.Key)!;
Assert.IsEmpty(content.PublishedCultures);
}
[Test]
public async Task Can_Publish_Culture_With_Other_Culture_Invalid()
{
var (langEn, langDa, contentType) = await SetupVariantTest();
IContent content = new ContentBuilder()
.WithContentType(contentType)
.WithCultureName(langEn.IsoCode, "EN root")
.WithCultureName(langDa.IsoCode, "DA root")
.Build();
content.SetValue("title", "EN title", culture: langEn.IsoCode);
content.SetValue("title", null, culture: langDa.IsoCode);
ContentService.Save(content);
// reset any state and attempt a publish
content = ContentService.GetById(content.Key)!;
var result = ContentService.Publish(content, new[] { langEn.IsoCode });
Assert.IsTrue(result.Success);
Assert.AreEqual(PublishResultType.SuccessPublishCulture, result.Result);
// verify saved state
content = ContentService.GetById(content.Key)!;
Assert.AreEqual(1, content.PublishedCultures.Count());
Assert.AreEqual(langEn.IsoCode.ToLowerInvariant(), content.PublishedCultures.First());
}
private void AssertPerCulture<T>(IContent item, Func<IContent, string, T> getter,
params (ILanguage Language, bool Result)[] testCases)
{
@@ -3533,7 +3683,8 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
public class ContentNotificationHandler :
INotificationHandler<ContentCopyingNotification>,
INotificationHandler<ContentCopiedNotification>,
INotificationHandler<ContentPublishingNotification>
INotificationHandler<ContentPublishingNotification>,
INotificationHandler<ContentSavingNotification>
{
public static Action<ContentPublishingNotification> PublishingContent { get; set; }
@@ -3541,9 +3692,47 @@ public class ContentServiceTests : UmbracoIntegrationTestWithContent
public static Action<ContentCopiedNotification> CopiedContent { get; set; }
public static Action<ContentSavingNotification> SavingContent { get; set; }
public void Handle(ContentCopiedNotification notification) => CopiedContent?.Invoke(notification);
public void Handle(ContentCopyingNotification notification) => CopyingContent?.Invoke(notification);
public void Handle(ContentPublishingNotification notification) => PublishingContent?.Invoke(notification);
public void Handle(ContentSavingNotification notification) => SavingContent?.Invoke(notification);
}
private async Task<(ILanguage LangEn, ILanguage LangDa, IContentType contentType)> SetupVariantTest()
{
var langEn = (await LanguageService.GetAsync("en-US"))!;
var langDa = new LanguageBuilder()
.WithCultureInfo("da-DK")
.Build();
await LanguageService.CreateAsync(langDa, Constants.Security.SuperUserKey);
var template = TemplateBuilder.CreateTextPageTemplate();
FileService.SaveTemplate(template);
var contentType = new ContentTypeBuilder()
.WithAlias("variantContent")
.WithName("Variant Content")
.WithContentVariation(ContentVariation.Culture)
.AddPropertyGroup()
.WithAlias("content")
.WithName("Content")
.WithSupportsPublishing(true)
.AddPropertyType()
.WithAlias("title")
.WithName("Title")
.WithVariations(ContentVariation.Culture)
.WithMandatory(true)
.Done()
.Done()
.Build();
contentType.AllowedAsRoot = true;
ContentTypeService.Save(contentType);
return (langEn, langDa, contentType);
}
}

View File

@@ -71,7 +71,8 @@ public class ContentVariantAllowedActionTests : UmbracoTestServerTestBase
.Build();
var contentService = GetRequiredService<IContentService>();
contentService.SaveAndPublish(rootNode);
contentService.Save(rootNode);
contentService.Publish(rootNode, new[] { "*" });
ContentItemDisplay? display = UmbracoMapper.Map<ContentItemDisplay>(rootNode, context =>
{