Merge remote-tracking branch 'origin/v9/9.0' into v9/dev

This commit is contained in:
Bjarke Berg
2021-10-18 06:43:50 +02:00
3 changed files with 138 additions and 23 deletions

View File

@@ -553,6 +553,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
protected override void PersistUpdatedItem(IContent entity)
{
var isEntityDirty = entity.IsDirty();
var editedSnapshot = entity.Edited;
// check if we need to make any database changes at all
if ((entity.PublishedState == PublishedState.Published || entity.PublishedState == PublishedState.Unpublished)
@@ -659,6 +660,19 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
edited = true;
}
// To establish the new value of "edited" we compare all properties publishedValue to editedValue and look
// for differences.
//
// If we SaveAndPublish but the publish fails (e.g. already scheduled for release)
// we have lost the publishedValue on IContent (in memory vs database) so we cannot correctly make that comparison.
//
// This is a slight change to behaviour, historically a publish, followed by change & save, followed by undo change & save
// would change edited back to false.
if (!publishing && editedSnapshot)
{
edited = true;
}
if (entity.ContentType.VariesByCulture())
{
// names also impact 'edited'

View File

@@ -1348,6 +1348,107 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services
Assert.AreEqual(PublishResultType.FailedPublishAwaitingRelease, published.Result);
}
[Test]
public void Failed_Publish_Should_Not_Update_Edited_State_When_Edited_True()
{
// Arrange
IContentService contentService = GetRequiredService<IContentService>();
IContentTypeService contentTypeService = GetRequiredService<IContentTypeService>();
IContentType contentType = new ContentTypeBuilder()
.WithId(0)
.AddPropertyType()
.WithAlias("header")
.WithValueStorageType(ValueStorageType.Integer)
.WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox)
.WithName("header")
.Done()
.WithContentVariation(ContentVariation.Nothing)
.Build();
contentTypeService.Save(contentType);
Content content = new ContentBuilder()
.WithId(0)
.WithName("Home")
.WithContentType(contentType)
.AddPropertyData()
.WithKeyValue("header", "Cool header")
.Done()
.Build();
contentService.SaveAndPublish(content);
content.Properties[0].SetValue("Foo", culture: string.Empty);
content.ContentSchedule.Add(DateTime.Now.AddHours(2), null);
contentService.Save(content);
// Act
var result = contentService.SaveAndPublish(content, userId: Constants.Security.SuperUserId);
// Assert
Assert.Multiple(() =>
{
Assert.IsFalse(result.Success);
Assert.IsTrue(result.Content.Published);
Assert.AreEqual(PublishResultType.FailedPublishAwaitingRelease, result.Result);
// We changed property data
Assert.IsTrue(result.Content.Edited, "result.Content.Edited");
});
}
// V9 - Tests.Integration
[Test]
public void Failed_Publish_Should_Not_Update_Edited_State_When_Edited_False()
{
// Arrange
IContentService contentService = GetRequiredService<IContentService>();
IContentTypeService contentTypeService = GetRequiredService<IContentTypeService>();
IContentType contentType = new ContentTypeBuilder()
.WithId(0)
.AddPropertyType()
.WithAlias("header")
.WithValueStorageType(ValueStorageType.Integer)
.WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox)
.WithName("header")
.Done()
.WithContentVariation(ContentVariation.Nothing)
.Build();
contentTypeService.Save(contentType);
Content content = new ContentBuilder()
.WithId(0)
.WithName("Home")
.WithContentType(contentType)
.AddPropertyData()
.WithKeyValue("header", "Cool header")
.Done()
.Build();
contentService.SaveAndPublish(content);
content.ContentSchedule.Add(DateTime.Now.AddHours(2), null);
contentService.Save(content);
// Act
var result = contentService.SaveAndPublish(content, userId: Constants.Security.SuperUserId);
// Assert
Assert.Multiple(() =>
{
Assert.IsFalse(result.Success);
Assert.IsTrue(result.Content.Published);
Assert.AreEqual(PublishResultType.FailedPublishAwaitingRelease, result.Result);
// We didn't change any property data
Assert.IsFalse(result.Content.Edited, "result.Content.Edited");
});
}
[Test]
public void Cannot_Publish_Culture_Awaiting_Release()
{
@@ -2151,7 +2252,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services
ContentService.Save(rollback2);
Assert.IsTrue(rollback2.Published);
Assert.IsFalse(rollback2.Edited); // all changes cleared!
Assert.IsTrue(rollback2.Edited); // Still edited, change of behaviour
Assert.AreEqual("Jane Doe", rollback2.GetValue<string>("author"));
Assert.AreEqual("Text Page 2 ReReUpdated", rollback2.Name);
@@ -2170,7 +2271,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services
content.CopyFrom(rollto);
content.Name = rollto.PublishName; // must do it explicitely AND must pick the publish one!
ContentService.Save(content);
Assert.IsFalse(content.Edited);
Assert.IsTrue(content.Edited); //Still edited, change of behaviour
Assert.AreEqual("Text Page 2 ReReUpdated", content.Name);
Assert.AreEqual("Jane Doe", content.GetValue("author"));
}

View File

@@ -109,7 +109,7 @@
}
]
};
editorService.contentTypePicker(settingsTypePicker);
});
@@ -179,7 +179,7 @@
},
close: () => editorService.close()
};
editorService.filePicker(filePicker);
});
@@ -206,26 +206,26 @@
};
vm.addStylesheetForBlock = function(block) {
localizationService.localize("blockEditor_headlineAddCustomStylesheet").then(localizedTitle => {
localizationService.localize("blockEditor_headlineAddCustomStylesheet").then(localizedTitle => {
const filePicker = {
title: localizedTitle,
isDialog: true,
filter: i => {
return !(i.name.indexOf(".css") !== -1);
},
filterCssClass: "not-allowed",
select: node => {
const filepath = decodeURIComponent(node.id.replace(/\+/g, " "));
block.stylesheet = "~/" + filepath;
editorService.close();
},
close: () => editorService.close()
};
const filePicker = {
title: localizedTitle,
isDialog: true,
filter: i => {
return !(i.name.indexOf(".css") !== -1);
},
filterCssClass: "not-allowed",
select: node => {
const filepath = decodeURIComponent(node.id.replace(/\+/g, " "));
block.stylesheet = "~/" + filepath.replace("wwwroot/", "");
editorService.close();
},
close: () => editorService.close()
};
editorService.filePicker(filePicker);
editorService.staticFilePicker(filePicker);
});
});
};
vm.requestRemoveStylesheetForBlock = function(block) {
@@ -251,7 +251,7 @@
vm.addThumbnailForBlock = function(block) {
localizationService.localize("blockEditor_headlineAddThumbnail").then(localizedTitle => {
let allowedFileExtensions = ['jpg', 'jpeg', 'png', 'svg', 'webp', 'gif'];
const thumbnailPicker = {
@@ -269,7 +269,7 @@
},
close: () => editorService.close()
};
editorService.staticFilePicker(thumbnailPicker);
});