* Started the implementation of the new date time property editor * Display picked time in local and UTC * Adjustments to the way the timezones are displayed and the picker is configured * Filter out `Etc/` (offset) timezones from the list * Additional adjustments * Introduced date format and time zone options (all, local or custom) * Adjustments to the property editor configuration and value converter * Use UUICombobox instead of UUISelect for displaying time zone options. Display UTC offset instead of short offset name in label. * Allow searching by offset * Ignore case when searching for time zone * Store dates consistently (always same format) * Add custom PropertyIndexValueFactory for the new property editor * Adjustments when switching between time zone modes * Small fixes and cleanup * Started improving time zone config selection * Small adjustments * Remove selected time zones from the list + display label instead of value * Localizing labels * Remove unwanted character * Fix incorrect order of custom time zones list * Small fixes (mostly validation) * Rename input time zone component * Small adjustments * Using model for stored value * Save examine value as ISO format * Adjusting class names for consistency * Small fixes * Add default data type configuration * Rename `TimeZone` to `UmbTimeZone` * Fix failing tests * Started adding unit tests for DateWithTimeZonePropertyEditor * Additional tests * Additional tests * Additional tests * Fixed searches with regex special characters throwing errors * Remove offset from generic UmbTimeZone type and added new type specific for the property editor * Adjust property editor to show error when selected time zone is no longer available, instead of pre-selecting another one * Do not preselect a time zone if a date is stored without time zone This most likely means that the configuration of the editor changed to add time zone support. In this case we want to force the editor to select the applicable time zone. * Fix failing backoffice build * Added tests for DateTimeWithTimeZonePropertyIndexValueFactory * Improved picker validation * Remove unused code * Move models to their corresponding places * Renaming `DateTimeWithTimeZone` to `DateTime2` * Fix data type count tests * Simplifying code + adjusting value converter to support old picker value * Adjustments to property editor unit tests * Fix validation issue * Fix default configuration for 'Date Time (Unspecified)' * Rename validator * Fix comment * Adjust database creator default DateTime2 data types * Update tests after adjusting default data types * Add integration test for DateTime2 returned value type * Apply suggestions from code review Co-authored-by: Andy Butland <abutland73@gmail.com> * Aligning DateTime2Validator with other JSON validators. Added new model for API. * Removed unused code and updated tests * Fix validation error message * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Splitting the new date time editor into multiple (per output type) * Adjust tests in DateTime2PropertyIndexValueFactoryTest * Update value converter tests * Group the new date time tests * Adjust new property editor tests * Adjust property editor integration tests * Update data editor count tests * Naming adjustments * Small fixes * Cleanup - Remove unused files - Remove 'None' option from configuration and update all the tests * Update luxon depedencies * Move GetValueFromSource to the value converter * Add new property editor examples to mock data * Re-organizing the code * Adjustments from code review * Place the date time property index value factories in their own files * Small adjustments for code consistency * Small adjustments * Minor adjustment * Small fix from copilot review * Completed the set of XML header comments. * use already existing query property * fail is form control element is null or undefined * using lit ref for querying and form control registration * state for timeZonePickerValue and remove _disableAddButton * Adjustments to form control registration * Remove unused declaration --------- Co-authored-by: Andy Butland <abutland73@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Niels Lyngsø <nsl@umbraco.dk> Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
3708 lines
150 KiB
C#
3708 lines
150 KiB
C#
// Copyright (c) Umbraco.
|
|
// See LICENSE for more details.
|
|
|
|
using System.Diagnostics;
|
|
using Microsoft.Extensions.Options;
|
|
using Moq;
|
|
using NUnit.Framework;
|
|
using Umbraco.Cms.Core;
|
|
using Umbraco.Cms.Core.Cache;
|
|
using Umbraco.Cms.Core.Configuration.Models;
|
|
using Umbraco.Cms.Core.Dictionary;
|
|
using Umbraco.Cms.Core.Events;
|
|
using Umbraco.Cms.Core.Models;
|
|
using Umbraco.Cms.Core.Models.ContentEditing;
|
|
using Umbraco.Cms.Core.Notifications;
|
|
using Umbraco.Cms.Core.Persistence.Repositories;
|
|
using Umbraco.Cms.Core.PropertyEditors;
|
|
using Umbraco.Cms.Core.Serialization;
|
|
using Umbraco.Cms.Core.Services;
|
|
using Umbraco.Cms.Infrastructure.Persistence;
|
|
using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
|
|
using Umbraco.Cms.Tests.Common.Attributes;
|
|
using Umbraco.Cms.Tests.Common.Builders;
|
|
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
|
using Umbraco.Cms.Tests.Common.Extensions;
|
|
using Umbraco.Cms.Tests.Common.Testing;
|
|
using Umbraco.Cms.Tests.Integration.Testing;
|
|
using Language = Umbraco.Cms.Core.Models.Language;
|
|
|
|
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
|
|
|
|
/// <summary>
|
|
/// Tests covering all methods in the ContentService class.
|
|
/// </summary>
|
|
[TestFixture]
|
|
[UmbracoTest(
|
|
Database = UmbracoTestOptions.Database.NewSchemaPerTest,
|
|
PublishedRepositoryEvents = true,
|
|
WithApplication = true)]
|
|
internal sealed class ContentServiceTests : UmbracoIntegrationTestWithContent
|
|
{
|
|
[SetUp]
|
|
public void Setup() => ContentRepositoryBase.ThrowOnWarning = true;
|
|
|
|
[TearDown]
|
|
public void Teardown() => ContentRepositoryBase.ThrowOnWarning = false;
|
|
// TODO: Add test to verify there is only ONE newest document/content in {Constants.DatabaseSchema.Tables.Document} table after updating.
|
|
// TODO: Add test to delete specific version (with and without deleting prior versions) and versions by date.
|
|
|
|
|
|
private IDataTypeService DataTypeService => GetRequiredService<IDataTypeService>();
|
|
private ILocalizedTextService LocalizedTextService => GetRequiredService<ILocalizedTextService>();
|
|
|
|
private ILanguageService LanguageService => GetRequiredService<ILanguageService>();
|
|
|
|
private IAuditService AuditService => GetRequiredService<IAuditService>();
|
|
|
|
private IUserService UserService => GetRequiredService<IUserService>();
|
|
|
|
private IUserGroupService UserGroupService => GetRequiredService<IUserGroupService>();
|
|
|
|
private IRelationService RelationService => GetRequiredService<IRelationService>();
|
|
|
|
private ITagService TagService => GetRequiredService<ITagService>();
|
|
|
|
private IPublicAccessService PublicAccessService => GetRequiredService<IPublicAccessService>();
|
|
|
|
private IDomainService DomainService => GetRequiredService<IDomainService>();
|
|
|
|
private INotificationService NotificationService => GetRequiredService<INotificationService>();
|
|
|
|
private PropertyEditorCollection PropertyEditorCollection => GetRequiredService<PropertyEditorCollection>();
|
|
|
|
private IDocumentRepository DocumentRepository => GetRequiredService<IDocumentRepository>();
|
|
|
|
private IJsonSerializer Serializer => GetRequiredService<IJsonSerializer>();
|
|
|
|
private IValueEditorCache ValueEditorCache => GetRequiredService<IValueEditorCache>();
|
|
|
|
protected override void CustomTestSetup(IUmbracoBuilder builder) => builder
|
|
.AddNotificationHandler<ContentPublishingNotification, ContentNotificationHandler>()
|
|
.AddNotificationHandler<ContentCopyingNotification, ContentNotificationHandler>()
|
|
.AddNotificationHandler<ContentCopiedNotification, ContentNotificationHandler>()
|
|
.AddNotificationHandler<ContentSavingNotification, ContentNotificationHandler>();
|
|
|
|
[Test]
|
|
public void Create_Blueprint()
|
|
{
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
|
|
var contentType = ContentTypeBuilder.CreateTextPageContentType(defaultTemplateId: template.Id);
|
|
ContentTypeService.Save(contentType);
|
|
|
|
var blueprint = ContentBuilder.CreateTextpageContent(contentType, "hello", Constants.System.Root);
|
|
blueprint.SetValue("title", "blueprint 1");
|
|
blueprint.SetValue("bodyText", "blueprint 2");
|
|
blueprint.SetValue("keywords", "blueprint 3");
|
|
blueprint.SetValue("description", "blueprint 4");
|
|
|
|
ContentService.SaveBlueprint(blueprint, null);
|
|
|
|
var found = ContentService.GetBlueprintsForContentTypes().ToArray();
|
|
Assert.AreEqual(1, found.Length);
|
|
|
|
// ensures it's not found by normal content
|
|
var contentFound = ContentService.GetById(found[0].Id);
|
|
Assert.IsNull(contentFound);
|
|
}
|
|
|
|
[Test]
|
|
public void Delete_Blueprint()
|
|
{
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
|
|
var contentType = ContentTypeBuilder.CreateTextPageContentType(defaultTemplateId: template.Id);
|
|
ContentTypeService.Save(contentType);
|
|
|
|
var blueprint = ContentBuilder.CreateTextpageContent(contentType, "hello", Constants.System.Root);
|
|
blueprint.SetValue("title", "blueprint 1");
|
|
blueprint.SetValue("bodyText", "blueprint 2");
|
|
blueprint.SetValue("keywords", "blueprint 3");
|
|
blueprint.SetValue("description", "blueprint 4");
|
|
|
|
ContentService.SaveBlueprint(blueprint, null);
|
|
|
|
ContentService.DeleteBlueprint(blueprint);
|
|
|
|
var found = ContentService.GetBlueprintsForContentTypes().ToArray();
|
|
Assert.AreEqual(0, found.Length);
|
|
}
|
|
|
|
[Test]
|
|
public void Create_Blueprint_From_Content()
|
|
{
|
|
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
|
{
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
|
|
var contentType = ContentTypeBuilder.CreateTextPageContentType(defaultTemplateId: template.Id);
|
|
ContentTypeService.Save(contentType);
|
|
|
|
var originalPage = ContentBuilder.CreateTextpageContent(contentType, "hello", Constants.System.Root);
|
|
originalPage.SetValue("title", "blueprint 1");
|
|
originalPage.SetValue("bodyText", "blueprint 2");
|
|
originalPage.SetValue("keywords", "blueprint 3");
|
|
originalPage.SetValue("description", "blueprint 4");
|
|
ContentService.Save(originalPage);
|
|
|
|
var fromContent = ContentService.CreateBlueprintFromContent(originalPage, "hello world");
|
|
ContentService.SaveBlueprint(fromContent, originalPage);
|
|
|
|
Assert.IsTrue(fromContent.HasIdentity);
|
|
Assert.AreEqual("blueprint 1", fromContent.Properties["title"]?.GetValue());
|
|
Assert.AreEqual("blueprint 2", fromContent.Properties["bodyText"]?.GetValue());
|
|
Assert.AreEqual("blueprint 3", fromContent.Properties["keywords"]?.GetValue());
|
|
Assert.AreEqual("blueprint 4", fromContent.Properties["description"]?.GetValue());
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public void Get_All_Blueprints()
|
|
{
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
|
|
var ct1 = ContentTypeBuilder.CreateTextPageContentType("ct1", defaultTemplateId: template.Id);
|
|
FileService.SaveTemplate(ct1.DefaultTemplate);
|
|
ContentTypeService.Save(ct1);
|
|
var ct2 = ContentTypeBuilder.CreateTextPageContentType("ct2", defaultTemplateId: template.Id);
|
|
FileService.SaveTemplate(ct2.DefaultTemplate);
|
|
ContentTypeService.Save(ct2);
|
|
|
|
for (var i = 0; i < 10; i++)
|
|
{
|
|
var blueprint =
|
|
ContentBuilder.CreateTextpageContent(i % 2 == 0 ? ct1 : ct2, "hello" + i, Constants.System.Root);
|
|
ContentService.SaveBlueprint(blueprint, null);
|
|
}
|
|
|
|
var found = ContentService.GetBlueprintsForContentTypes().ToArray();
|
|
Assert.AreEqual(10, found.Length);
|
|
|
|
found = ContentService.GetBlueprintsForContentTypes(ct1.Id).ToArray();
|
|
Assert.AreEqual(5, found.Length);
|
|
|
|
found = ContentService.GetBlueprintsForContentTypes(ct2.Id).ToArray();
|
|
Assert.AreEqual(5, found.Length);
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public async Task Perform_Scheduled_Publishing()
|
|
{
|
|
var langUk = new LanguageBuilder()
|
|
.WithCultureInfo("en-GB")
|
|
.WithIsDefault(true)
|
|
.Build();
|
|
var langFr = new LanguageBuilder()
|
|
.WithCultureInfo("fr-FR")
|
|
.Build();
|
|
|
|
await LanguageService.CreateAsync(langFr, Constants.Security.SuperUserKey);
|
|
await LanguageService.CreateAsync(langUk, Constants.Security.SuperUserKey);
|
|
|
|
var ctInvariant = ContentTypeBuilder.CreateBasicContentType("invariantPage");
|
|
ContentTypeService.Save(ctInvariant);
|
|
|
|
var ctVariant = ContentTypeBuilder.CreateBasicContentType("variantPage");
|
|
ctVariant.Variations = ContentVariation.Culture;
|
|
ContentTypeService.Save(ctVariant);
|
|
|
|
var now = DateTime.UtcNow;
|
|
|
|
// 10x invariant content, half is scheduled to be published in 5 seconds, the other half is scheduled to be unpublished in 5 seconds
|
|
var invariant = new List<IContent>();
|
|
for (var i = 0; i < 10; i++)
|
|
{
|
|
var c = ContentBuilder.CreateBasicContent(ctInvariant);
|
|
c.Name = "name" + i;
|
|
if (i % 2 == 0)
|
|
{
|
|
var contentSchedule =
|
|
ContentScheduleCollection.CreateWithEntry(now.AddSeconds(5), null); // release in 5 seconds
|
|
var r = ContentService.Save(c, contentSchedule: contentSchedule);
|
|
Assert.IsTrue(r.Success, r.Result.ToString());
|
|
}
|
|
else
|
|
{
|
|
ContentService.Save(c);
|
|
var r = ContentService.Publish(c, c.AvailableCultures.ToArray());
|
|
|
|
var contentSchedule =
|
|
ContentScheduleCollection.CreateWithEntry(null, now.AddSeconds(5)); // expire in 5 seconds
|
|
ContentService.PersistContentSchedule(c, contentSchedule);
|
|
|
|
Assert.IsTrue(r.Success, r.Result.ToString());
|
|
}
|
|
|
|
invariant.Add(c);
|
|
}
|
|
|
|
// 10x variant content, half is scheduled to be published in 5 seconds, the other half is scheduled to be unpublished in 5 seconds
|
|
var variant = new List<IContent>();
|
|
var alternatingCulture = langFr.IsoCode;
|
|
for (var i = 0; i < 10; i++)
|
|
{
|
|
var c = ContentBuilder.CreateBasicContent(ctVariant);
|
|
c.SetCultureName("name-uk" + i, langUk.IsoCode);
|
|
c.SetCultureName("name-fr" + i, langFr.IsoCode);
|
|
|
|
if (i % 2 == 0)
|
|
{
|
|
var contentSchedule =
|
|
ContentScheduleCollection.CreateWithEntry(alternatingCulture, now.AddSeconds(5),
|
|
null); // release in 5 seconds
|
|
var r = ContentService.Save(c, contentSchedule: contentSchedule);
|
|
Assert.IsTrue(r.Success, r.Result.ToString());
|
|
|
|
alternatingCulture = alternatingCulture == langFr.IsoCode ? langUk.IsoCode : langFr.IsoCode;
|
|
}
|
|
else
|
|
{
|
|
ContentService.Save(c);
|
|
var r = ContentService.Publish(c, c.AvailableCultures.ToArray());
|
|
|
|
var contentSchedule =
|
|
ContentScheduleCollection.CreateWithEntry(alternatingCulture, null,
|
|
now.AddSeconds(5)); // expire in 5 seconds
|
|
ContentService.PersistContentSchedule(c, contentSchedule);
|
|
|
|
Assert.IsTrue(r.Success, r.Result.ToString());
|
|
}
|
|
|
|
variant.Add(c);
|
|
}
|
|
|
|
var runSched = ContentService.PerformScheduledPublish(
|
|
now.AddMinutes(1)).ToList(); // process anything scheduled before a minute from now
|
|
|
|
// this is 21 because the test data installed before this test runs has a scheduled item!
|
|
Assert.AreEqual(21, runSched.Count);
|
|
Assert.AreEqual(
|
|
20,
|
|
runSched.Count(x => x.Success),
|
|
string.Join(Environment.NewLine, runSched.Select(x => $"{x.Entity.Name} - {x.Result}")));
|
|
|
|
Assert.AreEqual(
|
|
5,
|
|
runSched.Count(x => x.Result == PublishResultType.SuccessPublish),
|
|
string.Join(Environment.NewLine, runSched.Select(x => $"{x.Entity.Name} - {x.Result}")));
|
|
Assert.AreEqual(
|
|
5,
|
|
runSched.Count(x => x.Result == PublishResultType.SuccessUnpublish),
|
|
string.Join(Environment.NewLine, runSched.Select(x => $"{x.Entity.Name} - {x.Result}")));
|
|
Assert.AreEqual(
|
|
5,
|
|
runSched.Count(x => x.Result == PublishResultType.SuccessPublishCulture),
|
|
string.Join(Environment.NewLine, runSched.Select(x => $"{x.Entity.Name} - {x.Result}")));
|
|
Assert.AreEqual(
|
|
5,
|
|
runSched.Count(x => x.Result == PublishResultType.SuccessUnpublishCulture),
|
|
string.Join(Environment.NewLine, runSched.Select(x => $"{x.Entity.Name} - {x.Result}")));
|
|
|
|
// re-run the scheduled publishing, there should be no results
|
|
runSched = ContentService.PerformScheduledPublish(
|
|
now.AddMinutes(1)).ToList();
|
|
|
|
Assert.AreEqual(0, runSched.Count);
|
|
}
|
|
|
|
[Test]
|
|
public void Remove_Scheduled_Publishing_Date()
|
|
{
|
|
// Arrange
|
|
|
|
// Act
|
|
var content = ContentService.CreateAndSave("Test", Constants.System.Root, "umbTextpage");
|
|
|
|
var contentSchedule = ContentScheduleCollection.CreateWithEntry(null, DateTime.UtcNow.AddHours(2));
|
|
ContentService.Save(content, Constants.Security.SuperUserId, contentSchedule);
|
|
Assert.AreEqual(1, contentSchedule.FullSchedule.Count);
|
|
|
|
contentSchedule = ContentService.GetContentScheduleByContentId(content.Id);
|
|
var sched = contentSchedule.FullSchedule;
|
|
Assert.AreEqual(1, sched.Count);
|
|
Assert.AreEqual(1, sched.Count(x => x.Culture == Constants.System.InvariantCulture));
|
|
contentSchedule.Clear(ContentScheduleAction.Expire);
|
|
ContentService.Save(content, Constants.Security.SuperUserId, contentSchedule);
|
|
|
|
// Assert
|
|
contentSchedule = ContentService.GetContentScheduleByContentId(content.Id);
|
|
sched = contentSchedule.FullSchedule;
|
|
Assert.AreEqual(0, sched.Count);
|
|
Assert.IsTrue(ContentService.Publish(content, content.AvailableCultures.ToArray()).Success);
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public void Get_Top_Version_Ids()
|
|
{
|
|
// Arrange
|
|
// Act
|
|
var content = ContentService.CreateAndSave("Test", Constants.System.Root, "umbTextpage");
|
|
for (var i = 0; i < 20; i++)
|
|
{
|
|
content.SetValue("bodyText", "hello world " + Guid.NewGuid());
|
|
ContentService.Save(content);
|
|
ContentService.Publish(content, content.AvailableCultures.ToArray());
|
|
}
|
|
|
|
// Assert
|
|
var allVersions = ContentService.GetVersionIds(content.Id, int.MaxValue);
|
|
Assert.AreEqual(21, allVersions.Count());
|
|
|
|
var topVersions = ContentService.GetVersionIds(content.Id, 4);
|
|
Assert.AreEqual(4, topVersions.Count());
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public void Get_By_Ids_Sorted()
|
|
{
|
|
// Arrange
|
|
// Act
|
|
var results = new List<IContent>();
|
|
for (var i = 0; i < 20; i++)
|
|
{
|
|
results.Add(ContentService.CreateAndSave("Test", Constants.System.Root, "umbTextpage", -1));
|
|
}
|
|
|
|
var sortedGet = ContentService.GetByIds(new[] { results[10].Id, results[5].Id, results[12].Id })
|
|
.ToArray();
|
|
|
|
// Assert
|
|
Assert.AreEqual(sortedGet[0].Id, results[10].Id);
|
|
Assert.AreEqual(sortedGet[1].Id, results[5].Id);
|
|
Assert.AreEqual(sortedGet[2].Id, results[12].Id);
|
|
}
|
|
|
|
[Test]
|
|
public void Count_All()
|
|
{
|
|
// Arrange
|
|
// Act
|
|
for (var i = 0; i < 20; i++)
|
|
{
|
|
ContentService.CreateAndSave("Test", Constants.System.Root, "umbTextpage");
|
|
}
|
|
|
|
// Assert
|
|
Assert.AreEqual(25, ContentService.Count());
|
|
}
|
|
|
|
[Test]
|
|
public void Count_By_Content_Type()
|
|
{
|
|
// Arrange
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
|
|
var contentType =
|
|
ContentTypeBuilder.CreateSimpleContentType("umbBlah", "test Doc Type", defaultTemplateId: template.Id);
|
|
ContentTypeService.Save(contentType);
|
|
|
|
// Act
|
|
for (var i = 0; i < 20; i++)
|
|
{
|
|
ContentService.CreateAndSave("Test", Constants.System.Root, "umbBlah");
|
|
}
|
|
|
|
// Assert
|
|
Assert.AreEqual(20, ContentService.Count("umbBlah"));
|
|
}
|
|
|
|
[Test]
|
|
public void Count_Children()
|
|
{
|
|
// Arrange
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
|
|
var contentType =
|
|
ContentTypeBuilder.CreateSimpleContentType("umbBlah", "test Doc Type", defaultTemplateId: template.Id);
|
|
ContentTypeService.Save(contentType);
|
|
var parent = ContentService.CreateAndSave("Test", Constants.System.Root, "umbBlah");
|
|
|
|
// Act
|
|
for (var i = 0; i < 20; i++)
|
|
{
|
|
ContentService.CreateAndSave("Test", parent, "umbBlah");
|
|
}
|
|
|
|
// Assert
|
|
Assert.AreEqual(20, ContentService.CountChildren(parent.Id));
|
|
}
|
|
|
|
[Test]
|
|
public void Count_Descendants()
|
|
{
|
|
// Arrange
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
|
|
var contentType =
|
|
ContentTypeBuilder.CreateSimpleContentType("umbBlah", "test Doc Type", defaultTemplateId: template.Id);
|
|
ContentTypeService.Save(contentType);
|
|
var parent = ContentService.CreateAndSave("Test", Constants.System.Root, "umbBlah");
|
|
|
|
// Act
|
|
var current = parent;
|
|
for (var i = 0; i < 20; i++)
|
|
{
|
|
current = ContentService.CreateAndSave("Test", current, "umbBlah");
|
|
}
|
|
|
|
// Assert
|
|
Assert.AreEqual(20, ContentService.CountDescendants(parent.Id));
|
|
}
|
|
|
|
[Test]
|
|
public void GetAncestors_Returns_Empty_List_When_Path_Is_Null()
|
|
{
|
|
// Arrange
|
|
// Act
|
|
var current = new Mock<IContent>();
|
|
var res = ContentService.GetAncestors(current.Object);
|
|
|
|
// Assert
|
|
Assert.IsEmpty(res);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Remove_Property_Type()
|
|
{
|
|
// Arrange
|
|
// Act
|
|
var content = ContentService.Create("Test", Constants.System.Root, "umbTextpage");
|
|
|
|
// Assert
|
|
Assert.That(content, Is.Not.Null);
|
|
Assert.That(content.HasIdentity, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Create_Content()
|
|
{
|
|
// Arrange
|
|
// Act
|
|
var content = ContentService.Create("Test", Constants.System.Root, "umbTextpage");
|
|
|
|
// Assert
|
|
Assert.That(content, Is.Not.Null);
|
|
Assert.That(content.HasIdentity, Is.False);
|
|
}
|
|
|
|
public void Can_Create_Content_Without_Explicitly_Set_User()
|
|
{
|
|
// Arrange
|
|
// Act
|
|
var content = ContentService.Create("Test", Constants.System.Root, "umbTextpage");
|
|
|
|
// Assert
|
|
Assert.That(content, Is.Not.Null);
|
|
Assert.That(content.HasIdentity, Is.False);
|
|
Assert.That(content.CreatorId,
|
|
Is.EqualTo(Constants.Security
|
|
.SuperUserId)); // Default to -1 aka SuperUser (unknown) since we didn't explicitly set this in the Create call
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Save_New_Content_With_Explicit_User()
|
|
{
|
|
var user = new UserBuilder().Build();
|
|
UserService.Save(user);
|
|
var content = new Content("Test", Constants.System.Root, ContentTypeService.Get("umbTextpage"));
|
|
|
|
// Act
|
|
ContentService.Save(content, user.Id);
|
|
|
|
// Assert
|
|
Assert.That(content.CreatorId, Is.EqualTo(user.Id));
|
|
Assert.That(content.WriterId, Is.EqualTo(user.Id));
|
|
}
|
|
|
|
[Test]
|
|
public void Cannot_Create_Content_With_Non_Existing_ContentType_Alias() =>
|
|
Assert.Throws<Exception>(() => ContentService.Create("Test", Constants.System.Root, "umbAliasDoesntExist"));
|
|
|
|
[Test]
|
|
public void Cannot_Save_Content_With_Empty_Name()
|
|
{
|
|
// Arrange
|
|
var content = new Content(string.Empty, Constants.System.Root, ContentTypeService.Get("umbTextpage"));
|
|
|
|
// Act & Assert
|
|
Assert.Throws<InvalidOperationException>(() => ContentService.Save(content));
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Get_Content_By_Id()
|
|
{
|
|
// Arrange
|
|
// Act
|
|
var content = ContentService.GetById(Textpage.Id);
|
|
|
|
// Assert
|
|
Assert.That(content, Is.Not.Null);
|
|
Assert.That(content.Id, Is.EqualTo(Textpage.Id));
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Get_Content_By_Guid_Key()
|
|
{
|
|
// Arrange
|
|
// Act
|
|
var content = ContentService.GetById(new Guid("B58B3AD4-62C2-4E27-B1BE-837BD7C533E0"));
|
|
|
|
// Assert
|
|
Assert.That(content, Is.Not.Null);
|
|
Assert.That(content.Id, Is.EqualTo(Textpage.Id));
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Get_Content_By_Level()
|
|
{
|
|
// Arrange
|
|
// Act
|
|
var contents = ContentService.GetByLevel(2).ToList();
|
|
|
|
// Assert
|
|
Assert.That(contents, Is.Not.Null);
|
|
Assert.That(contents.Any(), Is.True);
|
|
Assert.That(contents.Count(), Is.GreaterThanOrEqualTo(2));
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public void Can_Get_All_Versions_Of_Content()
|
|
{
|
|
var parent = ContentService.GetById(Textpage.Id);
|
|
Assert.IsFalse(parent.Published);
|
|
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);
|
|
var versions = ContentService.GetVersions(Subpage.Id).ToList();
|
|
Assert.AreEqual(1, versions.Count);
|
|
|
|
var version1 = content.VersionId;
|
|
Console.WriteLine($"1 e={content.VersionId} p={content.PublishedVersionId}");
|
|
|
|
content.Name = "Text Page 2 Updated";
|
|
content.SetValue("author", "Jane Doe");
|
|
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.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}");
|
|
|
|
var content1 = ContentService.GetById(content.Id);
|
|
Assert.AreEqual("Bob Hope", content1.GetValue("author"));
|
|
Assert.AreEqual("Bob Hope", content1.GetValue("author", published: true));
|
|
|
|
content.Name = "Text Page 2 ReReUpdated";
|
|
content.SetValue("author", "John Farr");
|
|
ContentService.Save(content); // no new version
|
|
|
|
content1 = ContentService.GetById(content.Id);
|
|
Assert.AreEqual("John Farr", content1.GetValue("author"));
|
|
Assert.AreEqual("Bob Hope", content1.GetValue("author", published: true));
|
|
|
|
versions = ContentService.GetVersions(Subpage.Id).ToList();
|
|
Assert.AreEqual(3, versions.Count);
|
|
|
|
// versions come with most recent first
|
|
Assert.AreEqual(version3, versions[0].VersionId); // the edited version
|
|
Assert.AreEqual(version2, versions[1].VersionId); // the published version
|
|
Assert.AreEqual(version1, versions[2].VersionId); // the previously published version
|
|
|
|
// p is always the same, published version
|
|
// e is changing, actual version we're loading
|
|
Console.WriteLine();
|
|
foreach (var version in ((IEnumerable<IContent>)versions).Reverse())
|
|
{
|
|
Console.WriteLine($"+ e={((Content)version).VersionId} p={((Content)version).PublishedVersionId}");
|
|
}
|
|
|
|
// and proper values
|
|
// first, the current (edited) version, with edited and published versions
|
|
Assert.AreEqual("John Farr", versions[0].GetValue("author")); // current version has the edited value
|
|
Assert.AreEqual(
|
|
"Bob Hope",
|
|
versions[0].GetValue("author", published: true)); // and the published published value
|
|
|
|
// then, the current (published) version, with edited == published
|
|
Assert.AreEqual("Bob Hope", versions[1].GetValue("author")); // own edited version
|
|
Assert.AreEqual("Bob Hope", versions[1].GetValue("author", published: true)); // and published
|
|
|
|
// then, the first published version - with values as 'edited'
|
|
Assert.AreEqual("Jane Doe", versions[2].GetValue("author")); // own edited version
|
|
Assert.AreEqual("Bob Hope", versions[2].GetValue("author", published: true)); // and published
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Get_Root_Content()
|
|
{
|
|
// Arrange
|
|
// Act
|
|
var contents = ContentService.GetRootContent().ToList();
|
|
|
|
// Assert
|
|
Assert.That(contents, Is.Not.Null);
|
|
Assert.That(contents.Any(), Is.True);
|
|
Assert.That(contents.Count(), Is.EqualTo(1));
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public void Can_Get_Content_For_Expiration()
|
|
{
|
|
// Arrange
|
|
var root = ContentService.GetById(Textpage.Id);
|
|
ContentService.Publish(root!, root!.AvailableCultures.ToArray());
|
|
var content = ContentService.GetById(Subpage.Id);
|
|
var contentSchedule = ContentScheduleCollection.CreateWithEntry(null, DateTime.UtcNow.AddSeconds(1));
|
|
ContentService.PersistContentSchedule(content!, contentSchedule);
|
|
ContentService.Publish(content, content.AvailableCultures.ToArray());
|
|
|
|
// Act
|
|
Thread.Sleep(new TimeSpan(0, 0, 0, 2));
|
|
var contents = ContentService.GetContentForExpiration(DateTime.UtcNow).ToList();
|
|
|
|
// Assert
|
|
Assert.That(contents, Is.Not.Null);
|
|
Assert.That(contents.Any(), Is.True);
|
|
Assert.That(contents.Count(), Is.EqualTo(1));
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Get_Content_Schedules_By_Keys()
|
|
{
|
|
// Arrange
|
|
var root = ContentService.GetById(Textpage.Id);
|
|
ContentService.Publish(root!, root!.AvailableCultures.ToArray());
|
|
var content = ContentService.GetById(Subpage.Id);
|
|
var contentSchedule = ContentScheduleCollection.CreateWithEntry(DateTime.UtcNow.AddDays(1), null);
|
|
ContentService.PersistContentSchedule(content!, contentSchedule);
|
|
ContentService.Publish(content, content.AvailableCultures.ToArray());
|
|
|
|
// Act
|
|
var keys = ContentService.GetContentSchedulesByIds([Textpage.Key, Subpage.Key, Subpage2.Key]).ToList();
|
|
|
|
// Assert
|
|
Assert.AreEqual(1, keys.Count);
|
|
Assert.AreEqual(keys[0].Key, Subpage.Id);
|
|
Assert.AreEqual(keys[0].Value.First().Id, contentSchedule.FullSchedule.First().Id);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Get_Content_For_Release()
|
|
{
|
|
// Arrange
|
|
// Act
|
|
var contents = ContentService.GetContentForRelease(DateTime.UtcNow).ToList();
|
|
|
|
// Assert
|
|
Assert.That(DateTime.UtcNow.AddMinutes(-5) <= DateTime.UtcNow);
|
|
Assert.That(contents, Is.Not.Null);
|
|
Assert.That(contents.Any(), Is.True);
|
|
Assert.That(contents.Count(), Is.EqualTo(1));
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Get_Content_In_RecycleBin()
|
|
{
|
|
// Arrange
|
|
// Act
|
|
var contents = ContentService.GetPagedContentInRecycleBin(0, int.MaxValue, out var _).ToList();
|
|
|
|
// Assert
|
|
Assert.That(contents, Is.Not.Null);
|
|
Assert.That(contents.Any(), Is.True);
|
|
Assert.That(contents.Count(), Is.EqualTo(1));
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Unpublish_Content()
|
|
{
|
|
// Arrange
|
|
var content = ContentService.GetById(Textpage.Id);
|
|
Assert.IsNotNull(content);
|
|
var published = ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: -1);
|
|
|
|
// Act
|
|
var unpublished = ContentService.Unpublish(content, userId: -1);
|
|
|
|
// Assert
|
|
Assert.That(published.Success, Is.True);
|
|
Assert.That(unpublished.Success, Is.True);
|
|
Assert.That(content.Published, Is.False);
|
|
Assert.AreEqual(PublishResultType.SuccessUnpublish, unpublished.Result);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Unpublish_Content_Variation()
|
|
{
|
|
var content = CreateEnglishAndFrenchDocument(out var langUk, out var langFr, out var contentType);
|
|
|
|
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));
|
|
|
|
var unpublished = ContentService.Unpublish(content, langFr.IsoCode);
|
|
Assert.IsTrue(unpublished.Success);
|
|
Assert.AreEqual(PublishResultType.SuccessUnpublishCulture, unpublished.Result);
|
|
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
|
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
|
|
|
// re-get
|
|
content = ContentService.GetById(content.Id);
|
|
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
|
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public void Can_Publish_Culture_After_Last_Culture_Unpublished()
|
|
{
|
|
var content = CreateEnglishAndFrenchDocument(out var langUk, out var langFr,
|
|
out var contentType);
|
|
|
|
ContentService.Save(content);
|
|
var published = ContentService.Publish(content, new[] { langFr.IsoCode, langUk.IsoCode });
|
|
Assert.AreEqual(PublishedState.Published, content.PublishedState);
|
|
|
|
// re-get
|
|
content = ContentService.GetById(content.Id);
|
|
|
|
var unpublished = ContentService.Unpublish(content, langUk.IsoCode); // first culture
|
|
Assert.IsTrue(unpublished.Success);
|
|
Assert.AreEqual(PublishResultType.SuccessUnpublishCulture, unpublished.Result);
|
|
Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode));
|
|
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
|
|
|
|
content = ContentService.GetById(content.Id);
|
|
|
|
unpublished = ContentService.Unpublish(content, langFr.IsoCode); // last culture
|
|
Assert.IsTrue(unpublished.Success);
|
|
Assert.AreEqual(PublishResultType.SuccessUnpublishLastCulture, unpublished.Result);
|
|
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
|
Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode));
|
|
|
|
content = ContentService.GetById(content.Id);
|
|
|
|
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));
|
|
|
|
content = ContentService.GetById(content.Id); // reget
|
|
Assert.AreEqual(PublishedState.Published, content.PublishedState);
|
|
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
|
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
|
}
|
|
|
|
[Test]
|
|
public void Unpublish_All_Cultures_Has_Unpublished_State()
|
|
{
|
|
var content = CreateEnglishAndFrenchDocument(out var langUk, out var langFr,
|
|
out var contentType);
|
|
|
|
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);
|
|
|
|
// re-get
|
|
content = ContentService.GetById(content.Id);
|
|
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
|
|
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
|
Assert.AreEqual(PublishedState.Published, content.PublishedState);
|
|
|
|
var unpublished = ContentService.Unpublish(content, langFr.IsoCode); // first culture
|
|
Assert.IsTrue(unpublished.Success);
|
|
Assert.AreEqual(PublishResultType.SuccessUnpublishCulture, unpublished.Result);
|
|
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
|
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
|
Assert.AreEqual(PublishedState.Published, content.PublishedState); // still published
|
|
|
|
// re-get
|
|
content = ContentService.GetById(content.Id);
|
|
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
|
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
|
|
|
unpublished = ContentService.Unpublish(content, langUk.IsoCode); // last culture
|
|
Assert.IsTrue(unpublished.Success);
|
|
Assert.AreEqual(PublishResultType.SuccessUnpublishLastCulture, unpublished.Result);
|
|
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
|
Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode));
|
|
Assert.AreEqual(PublishedState.Unpublished,
|
|
content.PublishedState); // the last culture was unpublished so the document should also reflect this
|
|
|
|
// re-get
|
|
content = ContentService.GetById(content.Id);
|
|
Assert.AreEqual(PublishedState.Unpublished, content.PublishedState); // just double checking
|
|
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
|
Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode));
|
|
}
|
|
|
|
[Test]
|
|
public async Task Unpublishing_Mandatory_Language_Unpublishes_Document()
|
|
{
|
|
var langUk = new LanguageBuilder()
|
|
.WithCultureInfo("en-GB")
|
|
.WithIsDefault(true)
|
|
.WithIsMandatory(true)
|
|
.Build();
|
|
var langFr = new LanguageBuilder()
|
|
.WithCultureInfo("fr-FR")
|
|
.Build();
|
|
|
|
await LanguageService.CreateAsync(langFr, Constants.Security.SuperUserKey);
|
|
await LanguageService.CreateAsync(langUk, Constants.Security.SuperUserKey);
|
|
|
|
var contentType = ContentTypeBuilder.CreateBasicContentType();
|
|
contentType.Variations = ContentVariation.Culture;
|
|
ContentTypeService.Save(contentType);
|
|
|
|
IContent content = new Content("content", Constants.System.Root, contentType);
|
|
content.SetCultureName("content-fr", langFr.IsoCode);
|
|
content.SetCultureName("content-en", 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);
|
|
|
|
// re-get
|
|
content = ContentService.GetById(content.Id);
|
|
|
|
var unpublished = ContentService.Unpublish(content, langUk.IsoCode); // unpublish mandatory lang
|
|
Assert.IsTrue(unpublished.Success);
|
|
Assert.AreEqual(PublishResultType.SuccessUnpublishMandatoryCulture, unpublished.Result);
|
|
Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode));
|
|
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode)); // remains published
|
|
Assert.AreEqual(PublishedState.Unpublished, content.PublishedState);
|
|
}
|
|
|
|
[Test]
|
|
public void Unpublishing_Already_Unpublished_Culture()
|
|
{
|
|
var content = CreateEnglishAndFrenchDocument(out var langUk, out var langFr,
|
|
out var contentType);
|
|
|
|
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);
|
|
|
|
// re-get
|
|
content = ContentService.GetById(content.Id);
|
|
|
|
var unpublished = ContentService.Unpublish(content, langUk.IsoCode);
|
|
Assert.IsTrue(unpublished.Success);
|
|
Assert.AreEqual(PublishResultType.SuccessUnpublishCulture, unpublished.Result);
|
|
Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode));
|
|
|
|
content = ContentService.GetById(content.Id);
|
|
|
|
// Change some data since Unpublish should always Save
|
|
content.SetCultureName("content-en-updated", langUk.IsoCode);
|
|
|
|
unpublished = ContentService.Unpublish(content, langUk.IsoCode); // unpublish again
|
|
Assert.IsTrue(unpublished.Success);
|
|
Assert.AreEqual(PublishResultType.SuccessUnpublishAlready, unpublished.Result);
|
|
Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode));
|
|
|
|
content = ContentService.GetById(content.Id);
|
|
|
|
// ensure that even though the culture was already unpublished that the data was still persisted
|
|
Assert.AreEqual("content-en-updated", content.GetCultureName(langUk.IsoCode));
|
|
}
|
|
|
|
[Test]
|
|
public void Publishing_No_Cultures_Still_Saves()
|
|
{
|
|
var content = CreateEnglishAndFrenchDocument(out var langUk, out var langFr,
|
|
out var contentType);
|
|
|
|
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);
|
|
|
|
// re-get
|
|
content = ContentService.GetById(content.Id);
|
|
|
|
// Change some data since SaveAndPublish should always Save
|
|
content.SetCultureName("content-en-updated", langUk.IsoCode);
|
|
|
|
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);
|
|
|
|
// ensure that even though nothing was published that the data was still persisted
|
|
Assert.AreEqual("content-en-updated", content.GetCultureName(langUk.IsoCode));
|
|
}
|
|
|
|
[Test]
|
|
public async Task Pending_Invariant_Property_Changes_Affect_Default_Language_Edited_State()
|
|
{
|
|
// Arrange
|
|
var langGb = new LanguageBuilder()
|
|
.WithCultureInfo("en-GB")
|
|
.WithIsDefault(true)
|
|
.Build();
|
|
var langFr = new LanguageBuilder()
|
|
.WithCultureInfo("fr-FR")
|
|
.Build();
|
|
|
|
await LanguageService.CreateAsync(langFr, Constants.Security.SuperUserKey);
|
|
await LanguageService.CreateAsync(langGb, Constants.Security.SuperUserKey);
|
|
|
|
var contentType = ContentTypeBuilder.CreateMetaContentType();
|
|
contentType.Variations = ContentVariation.Culture;
|
|
foreach (var prop in contentType.PropertyTypes)
|
|
{
|
|
prop.Variations = ContentVariation.Culture;
|
|
}
|
|
|
|
var keywordsProp = contentType.PropertyTypes.Single(x => x.Alias == "metakeywords");
|
|
keywordsProp.Variations = ContentVariation.Nothing; // this one is invariant
|
|
|
|
ContentTypeService.Save(contentType);
|
|
|
|
IContent content = new Content("content", Constants.System.Root, contentType);
|
|
content.SetCultureName("content-en", langGb.IsoCode);
|
|
content.SetCultureName("content-fr", langFr.IsoCode);
|
|
|
|
Assert.IsTrue(ContentService.Save(content).Success);
|
|
Assert.IsTrue(ContentService.Publish(content, new[] { langGb.IsoCode, langFr.IsoCode }).Success);
|
|
|
|
// re-get
|
|
content = ContentService.GetById(content.Id);
|
|
Assert.AreEqual(PublishedState.Published, content.PublishedState);
|
|
Assert.IsTrue(content.IsCulturePublished(langGb.IsoCode));
|
|
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
|
|
Assert.IsFalse(content.IsCultureEdited(langGb.IsoCode));
|
|
Assert.IsFalse(content.IsCultureEdited(langFr.IsoCode));
|
|
|
|
// update the invariant property and save a pending version
|
|
content.SetValue("metakeywords", "hello");
|
|
ContentService.Save(content);
|
|
|
|
// re-get
|
|
content = ContentService.GetById(content.Id);
|
|
Assert.AreEqual(PublishedState.Published, content.PublishedState);
|
|
Assert.IsTrue(content.IsCulturePublished(langGb.IsoCode));
|
|
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
|
|
Assert.IsTrue(content.IsCultureEdited(langGb.IsoCode));
|
|
Assert.IsFalse(content.IsCultureEdited(langFr.IsoCode));
|
|
}
|
|
|
|
[Test]
|
|
public async Task Can_Publish_Content_Variation_And_Detect_Changed_Cultures()
|
|
{
|
|
CreateEnglishAndFrenchDocumentType(out var langUk, out var langFr, out var contentType);
|
|
|
|
IContent content = new Content("content", Constants.System.Root, contentType);
|
|
content.SetCultureName("content-fr", langFr.IsoCode);
|
|
ContentService.Save(content);
|
|
var published = ContentService.Publish(content, new[] { langFr.IsoCode });
|
|
|
|
// audit log will only show that french was published
|
|
var lastLog = (await AuditService.GetItemsByEntityAsync(content.Id, 0, 1)).Items.First();
|
|
Assert.AreEqual("Published languages: fr-FR", lastLog.Comment);
|
|
|
|
// re-get
|
|
content = ContentService.GetById(content.Id);
|
|
content.SetCultureName("content-en", langUk.IsoCode);
|
|
ContentService.Save(content);
|
|
published = ContentService.Publish(content, new[] { langUk.IsoCode });
|
|
|
|
// audit log will only show that english was published
|
|
lastLog = (await AuditService.GetItemsByEntityAsync(content.Id, 0, 1)).Items.First();
|
|
Assert.AreEqual("Published languages: en-GB", lastLog.Comment);
|
|
}
|
|
|
|
[Test]
|
|
public async Task Can_Unpublish_Content_Variation_And_Detect_Changed_Cultures()
|
|
{
|
|
// Arrange
|
|
var langGb = new LanguageBuilder()
|
|
.WithCultureInfo("en-GB")
|
|
.WithIsDefault(true)
|
|
.WithIsMandatory(true)
|
|
.Build();
|
|
var langFr = new LanguageBuilder()
|
|
.WithCultureInfo("fr-FR")
|
|
.Build();
|
|
|
|
await LanguageService.CreateAsync(langFr, Constants.Security.SuperUserKey);
|
|
await LanguageService.CreateAsync(langGb, Constants.Security.SuperUserKey);
|
|
|
|
var contentType = ContentTypeBuilder.CreateBasicContentType();
|
|
contentType.Variations = ContentVariation.Culture;
|
|
ContentTypeService.Save(contentType);
|
|
|
|
IContent content = new Content("content", Constants.System.Root, contentType);
|
|
content.SetCultureName("content-fr", langFr.IsoCode);
|
|
content.SetCultureName("content-gb", langGb.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
|
|
content = ContentService.GetById(content.Id);
|
|
var unpublished = ContentService.Unpublish(content, langFr.IsoCode);
|
|
|
|
// audit log will only show that french was unpublished
|
|
var lastLog = (await AuditService.GetItemsByEntityAsync(content.Id, 0, 1)).Items.First();
|
|
Assert.AreEqual("Unpublished languages: fr-FR", lastLog.Comment);
|
|
|
|
// re-get
|
|
content = ContentService.GetById(content.Id);
|
|
content.SetCultureName("content-en", langGb.IsoCode);
|
|
unpublished = ContentService.Unpublish(content, langGb.IsoCode);
|
|
|
|
// audit log will only show that english was published
|
|
var logs = (await AuditService.GetItemsByEntityAsync(content.Id, 0, int.MaxValue, Direction.Ascending)).Items.ToList();
|
|
Assert.AreEqual("Unpublished languages: en-GB", logs[^2].Comment);
|
|
Assert.AreEqual("Unpublished (mandatory language unpublished)", logs[^1].Comment);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Publish_Content_1()
|
|
{
|
|
// Arrange
|
|
var content = ContentService.GetById(Textpage.Id);
|
|
Assert.IsNotNull(content);
|
|
|
|
// Act
|
|
var published = ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: Constants.Security.SuperUserId);
|
|
|
|
// Assert
|
|
Assert.That(published.Success, Is.True);
|
|
Assert.That(content.Published, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Publish_Content_2()
|
|
{
|
|
// Arrange
|
|
var content = ContentService.GetById(Textpage.Id);
|
|
Assert.IsNotNull(content);
|
|
|
|
// Act
|
|
var published = ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: -1);
|
|
|
|
// Assert
|
|
Assert.That(published.Success, Is.True);
|
|
Assert.That(content.Published, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void IsPublishable()
|
|
{
|
|
// Arrange
|
|
var parent = ContentService.Create("parent", Constants.System.Root, "umbTextpage");
|
|
|
|
ContentService.Save(parent);
|
|
ContentService.Publish(parent, parent.AvailableCultures.ToArray());
|
|
var content = ContentService.Create("child", parent, "umbTextpage");
|
|
ContentService.Save(content);
|
|
|
|
Assert.IsTrue(ContentService.IsPathPublishable(content));
|
|
ContentService.Unpublish(parent);
|
|
Assert.IsFalse(ContentService.IsPathPublishable(content));
|
|
}
|
|
|
|
[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("Textpage", e.Name);
|
|
|
|
savingWasCalled = true;
|
|
};
|
|
|
|
ContentNotificationHandler.PublishingContent = notification =>
|
|
{
|
|
Assert.AreEqual(1, notification.PublishedEntities.Count());
|
|
var entity = notification.PublishedEntities.First();
|
|
Assert.AreEqual("foo", entity.Name);
|
|
|
|
publishingWasCalled = true;
|
|
};
|
|
|
|
try
|
|
{
|
|
var content = ContentService.GetById(Textpage.Id);
|
|
Assert.AreEqual("Textpage", content.Name);
|
|
|
|
content.Name = "foo";
|
|
ContentService.Save(content);
|
|
var published =
|
|
ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: Constants.Security.SuperUserId);
|
|
|
|
Assert.That(published.Success, Is.True);
|
|
Assert.That(content.Published, Is.True);
|
|
|
|
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_For_Variant_Content()
|
|
{
|
|
var contentType = ContentTypeBuilder.CreateBasicContentType();
|
|
contentType.Variations = ContentVariation.Culture;
|
|
ContentTypeService.Save(contentType);
|
|
|
|
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]
|
|
public void Can_Publish_Only_Valid_Content()
|
|
{
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
|
|
var contentType = ContentTypeBuilder.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type",
|
|
mandatoryProperties: true, defaultTemplateId: template.Id);
|
|
ContentTypeService.Save(contentType);
|
|
|
|
var parentId = Textpage.Id;
|
|
|
|
var parent = ContentService.GetById(parentId);
|
|
|
|
ContentService.Save(parent);
|
|
var parentPublished = ContentService.Publish(parent, parent.AvailableCultures.ToArray());
|
|
|
|
// parent can publish values
|
|
// and therefore can be published
|
|
Assert.IsTrue(parentPublished.Success);
|
|
Assert.IsTrue(parent.Published);
|
|
|
|
var content = ContentBuilder.CreateSimpleContent(contentType, "Invalid Content", parentId);
|
|
content.SetValue("author", string.Empty);
|
|
Assert.IsFalse(content.HasIdentity);
|
|
|
|
// content cannot publish values because they are invalid
|
|
var propertyValidationService = new PropertyValidationService(PropertyEditorCollection, DataTypeService, LocalizedTextService, ValueEditorCache, Mock.Of<ICultureDictionary>(), Mock.Of<ILanguageService>(), Mock.Of<IOptions<ContentSettings>>());
|
|
var isValid = propertyValidationService.IsPropertyDataValid(content, out var invalidProperties,
|
|
CultureImpact.Invariant);
|
|
Assert.IsFalse(isValid);
|
|
Assert.IsNotEmpty(invalidProperties);
|
|
|
|
// and therefore cannot be published,
|
|
// because it did not have a published version at all
|
|
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);
|
|
|
|
// Ensure it saved though
|
|
Assert.Greater(content.Id, 0);
|
|
Assert.IsTrue(content.HasIdentity);
|
|
}
|
|
|
|
[Test]
|
|
public async Task Can_Publish_And_Unpublish_Cultures_In_Single_Operation()
|
|
{
|
|
// TODO: This is using an internal API - we aren't exposing this publicly (at least for now) but we'll keep the test around
|
|
var langFr = new LanguageBuilder()
|
|
.WithCultureInfo("fr")
|
|
.Build();
|
|
var langDa = new LanguageBuilder()
|
|
.WithCultureInfo("da")
|
|
.Build();
|
|
await LanguageService.CreateAsync(langFr, Constants.Security.SuperUserKey);
|
|
await LanguageService.CreateAsync(langDa, Constants.Security.SuperUserKey);
|
|
|
|
var ct = ContentTypeBuilder.CreateBasicContentType();
|
|
ct.Variations = ContentVariation.Culture;
|
|
ContentTypeService.Save(ct);
|
|
|
|
IContent content = ContentBuilder.CreateBasicContent(ct);
|
|
content.SetCultureName("name-fr", langFr.IsoCode);
|
|
content.SetCultureName("name-da", langDa.IsoCode);
|
|
|
|
content.PublishCulture(CultureImpact.Explicit(langFr.IsoCode, langFr.IsDefault), DateTime.UtcNow, PropertyEditorCollection);
|
|
var result = ContentService.CommitDocumentChanges(content);
|
|
Assert.IsTrue(result.Success);
|
|
content = ContentService.GetById(content.Id);
|
|
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
|
|
Assert.IsFalse(content.IsCulturePublished(langDa.IsoCode));
|
|
|
|
content.UnpublishCulture(langFr.IsoCode);
|
|
content.PublishCulture(CultureImpact.Explicit(langDa.IsoCode, langDa.IsDefault), DateTime.UtcNow, PropertyEditorCollection);
|
|
|
|
result = ContentService.CommitDocumentChanges(content);
|
|
Assert.IsTrue(result.Success);
|
|
Assert.AreEqual(PublishResultType.SuccessMixedCulture, result.Result);
|
|
|
|
content = ContentService.GetById(content.Id);
|
|
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
|
Assert.IsTrue(content.IsCulturePublished(langDa.IsoCode));
|
|
}
|
|
|
|
// documents: an enumeration of documents, in tree order
|
|
// map: applies (if needed) PublishValue, returns a value indicating whether to proceed with the branch
|
|
private IEnumerable<IContent> MapPublishValues(IEnumerable<IContent> documents, Func<IContent, bool> map)
|
|
{
|
|
var exclude = new HashSet<int>();
|
|
foreach (var document in documents)
|
|
{
|
|
if (exclude.Contains(document.ParentId))
|
|
{
|
|
exclude.Add(document.Id);
|
|
continue;
|
|
}
|
|
|
|
if (!map(document))
|
|
{
|
|
exclude.Add(document.Id);
|
|
continue;
|
|
}
|
|
|
|
yield return document;
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Publish_Content_Children()
|
|
{
|
|
var parentId = Textpage.Id;
|
|
|
|
var parent = ContentService.GetById(parentId);
|
|
|
|
Console.WriteLine(" " + parent.Id);
|
|
const int pageSize = 500;
|
|
var page = 0;
|
|
var total = long.MaxValue;
|
|
while (page * pageSize < total)
|
|
{
|
|
var descendants =
|
|
ContentService.GetPagedDescendants(parent.Id, page++, pageSize, out total);
|
|
foreach (var x in descendants)
|
|
{
|
|
Console.WriteLine(" "[..x.Level] + x.Id);
|
|
}
|
|
}
|
|
|
|
Console.WriteLine();
|
|
|
|
// publish parent & its branch
|
|
// only those that are not already published
|
|
// only invariant/neutral values
|
|
var parentPublished = ContentService.PublishBranch(parent, PublishBranchFilter.IncludeUnpublished, parent.AvailableCultures.ToArray());
|
|
|
|
foreach (var result in parentPublished)
|
|
{
|
|
Console.WriteLine(" "[..result.Content.Level] +
|
|
$"{result.Content.Id}: {result.Result}");
|
|
}
|
|
|
|
// everything should be successful
|
|
Assert.IsTrue(parentPublished.All(x => x.Success));
|
|
Assert.IsTrue(parent.Published);
|
|
|
|
var
|
|
children = ContentService.GetPagedChildren(parentId, 0, 500,
|
|
out var totalChildren); // we only want the first so page size, etc.. is abitrary
|
|
|
|
// children are published including ... that was released 5 mins ago
|
|
Assert.IsTrue(children.First(x => x.Id == Subpage.Id).Published);
|
|
}
|
|
|
|
[Test]
|
|
public void Cannot_Publish_Expired_Content()
|
|
{
|
|
// Arrange
|
|
var content = ContentService.GetById(Subpage.Id); // This Content expired 5min ago
|
|
var contentSchedule = ContentScheduleCollection.CreateWithEntry(null, DateTime.UtcNow.AddMinutes(-5));
|
|
ContentService.Save(content, contentSchedule: contentSchedule);
|
|
|
|
var parent = ContentService.GetById(Textpage.Id);
|
|
Assert.IsNotNull(parent);
|
|
var parentPublished =
|
|
ContentService.Publish(parent,
|
|
parent.AvailableCultures.ToArray(),
|
|
userId: Constants.Security
|
|
.SuperUserId); // Publish root Home node to enable publishing of 'Subpage.Id'
|
|
|
|
// Act
|
|
var published = ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: Constants.Security.SuperUserId);
|
|
|
|
// Assert
|
|
Assert.That(parentPublished.Success, Is.True);
|
|
Assert.That(published.Success, Is.False);
|
|
Assert.That(content.Published, Is.False);
|
|
Assert.AreEqual(PublishResultType.FailedPublishHasExpired, published.Result);
|
|
}
|
|
|
|
[Test]
|
|
public void Cannot_Publish_Expired_Culture()
|
|
{
|
|
var contentType = ContentTypeBuilder.CreateBasicContentType();
|
|
contentType.Variations = ContentVariation.Culture;
|
|
ContentTypeService.Save(contentType);
|
|
|
|
var content = ContentBuilder.CreateBasicContent(contentType);
|
|
content.SetCultureName("Hello", "en-US");
|
|
var contentSchedule = ContentScheduleCollection.CreateWithEntry("en-US", null, DateTime.UtcNow.AddMinutes(-5));
|
|
ContentService.Save(content, contentSchedule: contentSchedule);
|
|
|
|
var published = ContentService.Publish(content, new[] { "en-US" });
|
|
|
|
Assert.IsFalse(published.Success);
|
|
Assert.AreEqual(PublishResultType.FailedPublishCultureHasExpired, published.Result);
|
|
Assert.That(content.Published, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void Cannot_Publish_Content_Awaiting_Release()
|
|
{
|
|
// Arrange
|
|
var content = ContentService.GetById(Subpage.Id);
|
|
var contentSchedule = ContentScheduleCollection.CreateWithEntry(DateTime.UtcNow.AddHours(2), null);
|
|
ContentService.Save(content, Constants.Security.SuperUserId, contentSchedule);
|
|
|
|
var parent = ContentService.GetById(Textpage.Id);
|
|
Assert.IsNotNull(parent);
|
|
var parentPublished =
|
|
ContentService.Publish(parent,
|
|
parent.AvailableCultures.ToArray(),
|
|
userId: Constants.Security
|
|
.SuperUserId); // Publish root Home node to enable publishing of 'Subpage.Id'
|
|
|
|
// Act
|
|
var published = ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: Constants.Security.SuperUserId);
|
|
|
|
// Assert
|
|
Assert.That(parentPublished.Success, Is.True);
|
|
Assert.That(published.Success, Is.False);
|
|
Assert.That(content.Published, Is.False);
|
|
Assert.AreEqual(PublishResultType.FailedPublishAwaitingRelease, published.Result);
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public void Failed_Publish_Should_Not_Update_Edited_State_When_Edited_True()
|
|
{
|
|
// Arrange
|
|
var contentService = GetRequiredService<IContentService>();
|
|
var contentTypeService = GetRequiredService<IContentTypeService>();
|
|
|
|
var 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);
|
|
|
|
var content = new ContentBuilder()
|
|
.WithId(0)
|
|
.WithName("Home")
|
|
.WithContentType(contentType)
|
|
.AddPropertyData()
|
|
.WithKeyValue("header", "Cool header")
|
|
.Done()
|
|
.Build();
|
|
|
|
contentService.Save(content);
|
|
contentService.Publish(content, Array.Empty<string>());
|
|
|
|
content.Properties[0].SetValue("Foo", string.Empty);
|
|
contentService.Save(content);
|
|
contentService.PersistContentSchedule(content,
|
|
ContentScheduleCollection.CreateWithEntry(DateTime.UtcNow.AddHours(2), null));
|
|
|
|
// Act
|
|
var result = contentService.Publish(content, Array.Empty<string>(), 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]
|
|
[LongRunning]
|
|
public void Failed_Publish_Should_Not_Update_Edited_State_When_Edited_False()
|
|
{
|
|
// Arrange
|
|
var contentService = GetRequiredService<IContentService>();
|
|
var contentTypeService = GetRequiredService<IContentTypeService>();
|
|
|
|
var 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);
|
|
|
|
var content = new ContentBuilder()
|
|
.WithId(0)
|
|
.WithName("Home")
|
|
.WithContentType(contentType)
|
|
.AddPropertyData()
|
|
.WithKeyValue("header", "Cool header")
|
|
.Done()
|
|
.Build();
|
|
|
|
contentService.Save(content);
|
|
contentService.Publish(content, Array.Empty<string>());
|
|
|
|
contentService.PersistContentSchedule(content,
|
|
ContentScheduleCollection.CreateWithEntry(DateTime.UtcNow.AddHours(2), null));
|
|
contentService.Save(content);
|
|
|
|
// Act
|
|
var result = contentService.Publish(content, Array.Empty<string>(), 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()
|
|
{
|
|
var contentType = ContentTypeBuilder.CreateBasicContentType();
|
|
contentType.Variations = ContentVariation.Culture;
|
|
ContentTypeService.Save(contentType);
|
|
|
|
var content = ContentBuilder.CreateBasicContent(contentType);
|
|
content.SetCultureName("Hello", "en-US");
|
|
var contentSchedule = ContentScheduleCollection.CreateWithEntry("en-US", DateTime.UtcNow.AddHours(2), null);
|
|
ContentService.Save(content, contentSchedule: contentSchedule);
|
|
|
|
var published = ContentService.Publish(content, new[] { "en-US" });
|
|
|
|
Assert.IsFalse(published.Success);
|
|
Assert.AreEqual(PublishResultType.FailedPublishCultureAwaitingRelease, published.Result);
|
|
Assert.That(content.Published, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void Cannot_Publish_Content_Where_Parent_Is_Unpublished()
|
|
{
|
|
// Arrange
|
|
var content = ContentService.Create("Subpage with Unpublished Parent", Textpage.Id, "umbTextpage");
|
|
ContentService.Save(content);
|
|
|
|
// Act
|
|
var published = ContentService.PublishBranch(content, PublishBranchFilter.IncludeUnpublished, content.AvailableCultures.ToArray());
|
|
|
|
// Assert
|
|
Assert.That(published.All(x => x.Success), Is.False);
|
|
Assert.That(content.Published, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void Cannot_Publish_Trashed_Content()
|
|
{
|
|
// Arrange
|
|
var content = ContentService.GetById(Trashed.Id);
|
|
Assert.IsNotNull(content);
|
|
|
|
// Act
|
|
var published = ContentService.Publish(content, content.AvailableCultures.ToArray(), userId: Constants.Security.SuperUserId);
|
|
|
|
// Assert
|
|
Assert.That(published.Success, Is.False);
|
|
Assert.That(content.Published, Is.False);
|
|
Assert.That(content.Trashed, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Save_And_Publish_Content()
|
|
{
|
|
// Arrange
|
|
var content = ContentService.Create("Home US", -1, "umbTextpage");
|
|
content.SetValue("author", "Barack Obama");
|
|
|
|
// Act
|
|
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>
|
|
/// Try to immitate a new child content item being created through the UI.
|
|
/// This content item will have no Id, Path or Identity.
|
|
/// It seems like this is wiped somewhere in the process when creating an item through the UI
|
|
/// and we need to make sure we handle nullchecks for these properties when creating content.
|
|
/// This is unfortunately not caught by the normal ContentService tests.
|
|
/// </summary>
|
|
[Test]
|
|
public void Can_Save_And_Publish_Content_And_Child_Without_Identity()
|
|
{
|
|
// Arrange
|
|
var content = ContentService.Create("Home US", Constants.System.Root, "umbTextpage");
|
|
content.SetValue("author", "Barack Obama");
|
|
|
|
// Act
|
|
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.Publish(childContent, childContent.AvailableCultures.ToArray(), userId: Constants.Security.SuperUserId);
|
|
|
|
// Assert
|
|
Assert.That(content.HasIdentity, Is.True);
|
|
Assert.That(content.Published, Is.True);
|
|
Assert.That(childContent.HasIdentity, Is.True);
|
|
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]
|
|
[LongRunning]
|
|
public void Can_Get_Published_Descendant_Versions()
|
|
{
|
|
// Arrange
|
|
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");
|
|
ContentService.Save(content);
|
|
var contentPublished = ContentService.Publish(content, content.AvailableCultures.ToArray());
|
|
var publishedVersion = content.VersionId;
|
|
|
|
content.Properties["title"].SetValue(content.Properties["title"].GetValue() + " Saved");
|
|
ContentService.Save(content);
|
|
Assert.AreEqual(publishedVersion, content.VersionId);
|
|
|
|
// Act
|
|
var publishedDescendants = ContentService.GetPublishedDescendants(root).ToList();
|
|
Assert.AreNotEqual(0, publishedDescendants.Count);
|
|
|
|
// Assert
|
|
Assert.IsTrue(rootPublished.Success);
|
|
Assert.IsTrue(contentPublished.Success);
|
|
|
|
// Console.WriteLine(publishedVersion);
|
|
// foreach (var d in publishedDescendants) Console.WriteLine(d.Version);
|
|
Assert.IsTrue(publishedDescendants.Any(x => x.VersionId == publishedVersion));
|
|
|
|
// Ensure that the published content version has the correct property value and is marked as published
|
|
var publishedContentVersion = publishedDescendants.First(x => x.VersionId == publishedVersion);
|
|
Assert.That(publishedContentVersion.Published, Is.True);
|
|
Assert.That(publishedContentVersion.Properties["title"].GetValue(published: true),
|
|
Contains.Substring("Published"));
|
|
|
|
// and has the correct draft properties
|
|
Assert.That(publishedContentVersion.Properties["title"].GetValue(), Contains.Substring("Saved"));
|
|
|
|
// Ensure that the latest version of the content is ok
|
|
var currentContent = ContentService.GetById(Subpage.Id);
|
|
Assert.That(currentContent.Published, Is.True);
|
|
Assert.That(currentContent.Properties["title"].GetValue(published: true), Contains.Substring("Published"));
|
|
Assert.That(currentContent.Properties["title"].GetValue(), Contains.Substring("Saved"));
|
|
Assert.That(currentContent.VersionId, Is.EqualTo(publishedContentVersion.VersionId));
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Save_Content()
|
|
{
|
|
// Arrange
|
|
var content = ContentService.Create("Home US", -1, "umbTextpage");
|
|
content.SetValue("author", "Barack Obama");
|
|
|
|
// Act
|
|
ContentService.Save(content);
|
|
|
|
// Assert
|
|
Assert.That(content.HasIdentity, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Update_Content_Property_Values()
|
|
{
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
|
|
IContentType contentType = ContentTypeBuilder.CreateSimpleContentType(defaultTemplateId: template.Id);
|
|
ContentTypeService.Save(contentType);
|
|
IContent content = ContentBuilder.CreateSimpleContent(contentType, "hello");
|
|
content.SetValue("title", "title of mine");
|
|
content.SetValue("bodyText", "hello world");
|
|
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.Save(content);
|
|
ContentService.Publish(content, content.AvailableCultures.ToArray());
|
|
|
|
// re-get
|
|
content = ContentService.GetById(content.Id);
|
|
Assert.AreEqual("another title of mine", content.GetValue("title"));
|
|
Assert.IsNull(content.GetValue("bodyText"));
|
|
Assert.AreEqual("new author", content.GetValue("author"));
|
|
|
|
content.SetValue("title", "new title");
|
|
content.SetValue("bodyText", "new body text");
|
|
content.SetValue("author", "new author text");
|
|
ContentService.Save(content); // new non-published version
|
|
|
|
// re-get
|
|
content = ContentService.GetById(content.Id);
|
|
content.SetValue("title", null); // Clear a value
|
|
content.SetValue("bodyText", null); // Clear a value
|
|
ContentService.Save(content); // saving non-published version
|
|
|
|
// re-get
|
|
content = ContentService.GetById(content.Id);
|
|
Assert.IsNull(content.GetValue("title")); // Test clearing the value worked with the non-published version
|
|
Assert.IsNull(content.GetValue("bodyText"));
|
|
Assert.AreEqual("new author text", content.GetValue("author"));
|
|
|
|
// make sure that the published version remained the same
|
|
var publishedContent = ContentService.GetVersion(content.PublishedVersionId);
|
|
Assert.AreEqual("another title of mine", publishedContent.GetValue("title"));
|
|
Assert.IsNull(publishedContent.GetValue("bodyText"));
|
|
Assert.AreEqual("new author", publishedContent.GetValue("author"));
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Bulk_Save_Content()
|
|
{
|
|
// Arrange
|
|
var contentType = ContentTypeService.Get("umbTextpage");
|
|
var subpage = ContentBuilder.CreateSimpleContent(contentType, "Text Subpage 1", Textpage.Id);
|
|
var subpage2 = ContentBuilder.CreateSimpleContent(contentType, "Text Subpage 2", Textpage.Id);
|
|
var list = new List<IContent> { subpage, subpage2 };
|
|
|
|
// Act
|
|
ContentService.Save(list);
|
|
|
|
// Assert
|
|
Assert.That(list.Any(x => !x.HasIdentity), Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Bulk_Save_New_Hierarchy_Content()
|
|
{
|
|
// Arrange
|
|
var hierarchy = CreateContentHierarchy().ToList();
|
|
|
|
// Act
|
|
ContentService.Save(hierarchy);
|
|
|
|
Assert.That(hierarchy.Any(), Is.True);
|
|
Assert.That(hierarchy.Any(x => x.HasIdentity == false), Is.False);
|
|
|
|
// all parent id's should be ok, they are lazy and if they equal zero an exception will be thrown
|
|
Assert.DoesNotThrow(() => hierarchy.Any(x => x.ParentId != 0));
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Delete_Content_Of_Specific_ContentType()
|
|
{
|
|
// Arrange
|
|
var contentType = ContentTypeService.Get("umbTextpage");
|
|
|
|
// Act
|
|
ContentService.DeleteOfType(contentType.Id);
|
|
var rootContent = ContentService.GetRootContent();
|
|
var contents = ContentService.GetPagedOfType(contentType.Id, 0, int.MaxValue, out var _);
|
|
|
|
// Assert
|
|
Assert.That(rootContent.Any(), Is.False);
|
|
Assert.That(contents.Any(x => !x.Trashed), Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Delete_Content()
|
|
{
|
|
// Arrange
|
|
var content = ContentService.GetById(Textpage.Id);
|
|
|
|
// Act
|
|
ContentService.Delete(content);
|
|
var deleted = ContentService.GetById(Textpage.Id);
|
|
|
|
// Assert
|
|
Assert.That(deleted, Is.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Move_Content_To_RecycleBin()
|
|
{
|
|
// Arrange
|
|
var content = ContentService.GetById(Textpage.Id);
|
|
|
|
// Act
|
|
ContentService.MoveToRecycleBin(content);
|
|
|
|
// Assert
|
|
Assert.That(content.ParentId, Is.EqualTo(-20));
|
|
Assert.That(content.Trashed, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public void Can_Move_Content_Structure_To_RecycleBin_And_Empty_RecycleBin()
|
|
{
|
|
var contentType = ContentTypeService.Get("umbTextpage");
|
|
|
|
var subsubpage = ContentBuilder.CreateSimpleContent(contentType, "Text Page 3", Subpage.Id);
|
|
ContentService.Save(subsubpage);
|
|
|
|
var content = ContentService.GetById(Textpage.Id);
|
|
const int pageSize = 500;
|
|
var page = 0;
|
|
var total = long.MaxValue;
|
|
var descendants = new List<IContent>();
|
|
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(4, descendants.Count);
|
|
Assert.IsFalse(descendants.Any(x => x.Path.StartsWith("-1,-20,")));
|
|
Assert.IsFalse(descendants.Any(x => x.Trashed));
|
|
|
|
ContentService.MoveToRecycleBin(content);
|
|
|
|
descendants.Clear();
|
|
page = 0;
|
|
while (page * pageSize < total)
|
|
{
|
|
descendants.AddRange(ContentService.GetPagedDescendants(content.Id, page++, pageSize, out total));
|
|
}
|
|
|
|
Assert.AreEqual(-20, content.ParentId);
|
|
Assert.IsTrue(content.Trashed);
|
|
Assert.AreEqual(4, descendants.Count);
|
|
Assert.IsTrue(descendants.All(x => x.Path.StartsWith("-1,-20,")));
|
|
Assert.True(descendants.All(x => x.Trashed));
|
|
|
|
ContentService.EmptyRecycleBin();
|
|
var trashed = ContentService.GetPagedContentInRecycleBin(0, int.MaxValue, out var _).ToList();
|
|
Assert.IsEmpty(trashed);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Empty_RecycleBin()
|
|
{
|
|
// Arrange
|
|
// Act
|
|
ContentService.EmptyRecycleBin();
|
|
var contents = ContentService.GetPagedContentInRecycleBin(0, int.MaxValue, out var _).ToList();
|
|
|
|
// Assert
|
|
Assert.That(contents.Any(), Is.False);
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public async Task Ensures_Permissions_Are_Retained_For_Copied_Descendants_With_Explicit_Permissions()
|
|
{
|
|
// Arrange
|
|
var userGroup = UserGroupBuilder.CreateUserGroup("1");
|
|
await UserGroupService.CreateAsync(userGroup, Constants.Security.SuperUserKey);
|
|
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
|
|
var contentType =
|
|
ContentTypeBuilder.CreateSimpleContentType("umbTextpage1", "Textpage", defaultTemplateId: template.Id);
|
|
contentType.AllowedContentTypes = new List<ContentTypeSort>
|
|
{
|
|
new(contentType.Key, 0, contentType.Alias)
|
|
};
|
|
ContentTypeService.Save(contentType);
|
|
|
|
var parentPage = ContentBuilder.CreateSimpleContent(contentType);
|
|
ContentService.Save(parentPage);
|
|
|
|
var childPage = ContentBuilder.CreateSimpleContent(contentType, "child", parentPage);
|
|
ContentService.Save(childPage);
|
|
|
|
// assign explicit permissions to the child
|
|
ContentService.SetPermission(childPage, "A", new[] { userGroup.Id });
|
|
|
|
// Ok, now copy, what should happen is the childPage will retain it's own permissions
|
|
var parentPage2 = ContentBuilder.CreateSimpleContent(contentType);
|
|
ContentService.Save(parentPage2);
|
|
|
|
var copy = ContentService.Copy(childPage, parentPage2.Id, false, true);
|
|
|
|
// get the permissions and verify
|
|
var permissions = UserService.GetPermissionsForPath(userGroup, copy.Path, true);
|
|
var allPermissions = permissions.GetAllPermissions().ToArray();
|
|
Assert.AreEqual(1, allPermissions.Length);
|
|
Assert.AreEqual("A", allPermissions[0]);
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public async Task Ensures_Permissions_Are_Inherited_For_Copied_Descendants()
|
|
{
|
|
// Arrange
|
|
var userGroup = UserGroupBuilder.CreateUserGroup("1");
|
|
await UserGroupService.CreateAsync(userGroup, Constants.Security.SuperUserKey);
|
|
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
|
|
var contentType =
|
|
ContentTypeBuilder.CreateSimpleContentType("umbTextpage1", "Textpage", defaultTemplateId: template.Id);
|
|
contentType.AllowedContentTypes = new List<ContentTypeSort>
|
|
{
|
|
new(contentType.Key, 0, contentType.Alias)
|
|
};
|
|
await ContentTypeService.UpdateAsync(contentType, Constants.Security.SuperUserKey);
|
|
|
|
var parentPage = ContentBuilder.CreateSimpleContent(contentType);
|
|
ContentService.Save(parentPage);
|
|
ContentService.SetPermission(parentPage, "A", new[] { userGroup.Id });
|
|
|
|
var childPage1 = ContentBuilder.CreateSimpleContent(contentType, "child1", parentPage);
|
|
ContentService.Save(childPage1);
|
|
var childPage2 = ContentBuilder.CreateSimpleContent(contentType, "child2", childPage1);
|
|
ContentService.Save(childPage2);
|
|
var childPage3 = ContentBuilder.CreateSimpleContent(contentType, "child3", childPage2);
|
|
ContentService.Save(childPage3);
|
|
|
|
// Verify that the children have the inherited permissions
|
|
var descendants = new List<IContent>();
|
|
const int pageSize = 500;
|
|
var page = 0;
|
|
var total = long.MaxValue;
|
|
while (page * pageSize < total)
|
|
{
|
|
descendants.AddRange(ContentService.GetPagedDescendants(parentPage.Id, page++, pageSize, out total));
|
|
}
|
|
|
|
Assert.AreEqual(3, descendants.Count);
|
|
|
|
foreach (var descendant in descendants)
|
|
{
|
|
var permissions = UserService.GetPermissionsForPath(userGroup, descendant.Path, true);
|
|
var allPermissions = permissions.GetAllPermissions().ToArray();
|
|
Assert.AreEqual(1, allPermissions.Length);
|
|
Assert.AreEqual("A", allPermissions[0]);
|
|
}
|
|
|
|
// create a new parent with a new permission structure
|
|
var parentPage2 = ContentBuilder.CreateSimpleContent(contentType);
|
|
ContentService.Save(parentPage2);
|
|
ContentService.SetPermission(parentPage2, "B", new[] { userGroup.Id });
|
|
|
|
// Now copy, what should happen is the child pages will now have permissions inherited from the new parent
|
|
var copy = ContentService.Copy(childPage1, parentPage2.Id, false, true);
|
|
|
|
descendants.Clear();
|
|
page = 0;
|
|
while (page * pageSize < total)
|
|
{
|
|
descendants.AddRange(ContentService.GetPagedDescendants(parentPage2.Id, page++, pageSize, out total));
|
|
}
|
|
|
|
Assert.AreEqual(3, descendants.Count);
|
|
|
|
foreach (var descendant in descendants)
|
|
{
|
|
var permissions = UserService.GetPermissionsForPath(userGroup, descendant.Path, true);
|
|
var allPermissions = permissions.GetAllPermissions().ToArray();
|
|
Assert.AreEqual(1, allPermissions.Length);
|
|
Assert.AreEqual("B", allPermissions[0]);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public async Task Can_Empty_RecycleBin_With_Content_That_Has_All_Related_Data()
|
|
{
|
|
// Arrange
|
|
// need to:
|
|
// * add relations
|
|
// * add permissions
|
|
// * add notifications
|
|
// * public access
|
|
// * tags
|
|
// * domain
|
|
// * published & preview data
|
|
// * multiple versions
|
|
var contentType = ContentTypeBuilder.CreateAllTypesContentType("test", "test");
|
|
ContentTypeService.Save(contentType);
|
|
|
|
object obj =
|
|
new { tags = "[\"Hello\",\"World\"]" };
|
|
var content1 = ContentBuilder.CreateBasicContent(contentType);
|
|
content1.PropertyValues(obj);
|
|
content1.ResetDirtyProperties(false);
|
|
ContentService.Save(content1);
|
|
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.Publish(content2, content2.AvailableCultures.ToArray(), userId: -1).Success);
|
|
|
|
var editorGroup = await UserGroupService.GetAsync(Constants.Security.EditorGroupKey);
|
|
editorGroup.StartContentId = content1.Id;
|
|
await UserGroupService.UpdateAsync(editorGroup, Constants.Security.SuperUserKey);
|
|
|
|
var admin = await UserService.GetAsync(Constants.Security.SuperUserKey);
|
|
admin.StartContentIds = new[] { content1.Id };
|
|
UserService.Save(admin);
|
|
|
|
RelationService.Save(new RelationType("test", "test", false, Constants.ObjectTypes.Document,
|
|
Constants.ObjectTypes.Document, false));
|
|
Assert.IsNotNull(RelationService.Relate(content1, content2, "test"));
|
|
|
|
PublicAccessService.Save(new PublicAccessEntry(content1, content2, content2,
|
|
new List<PublicAccessRule> { new() { RuleType = "test", RuleValue = "test" } }));
|
|
Assert.IsTrue(PublicAccessService.AddRule(content1, "test2", "test2").Success);
|
|
|
|
var user = await UserService.GetAsync(Constants.Security.SuperUserKey);
|
|
var userGroup = await UserGroupService.GetAsync(user.Groups.First().Alias);
|
|
NotificationService.TryCreateNotification(user, content1, "X", out Notification? notification);
|
|
Assert.IsNotNull(notification);
|
|
|
|
ContentService.SetPermission(content1, "A", new[] { userGroup.Id });
|
|
var updateDomainResult = await DomainService.UpdateDomainsAsync(
|
|
content1.Key,
|
|
new DomainsUpdateModel
|
|
{
|
|
Domains = new[] { new DomainModel { DomainName = "www.test.com", IsoCode = "en-US" } }
|
|
});
|
|
Assert.IsTrue(updateDomainResult.Success);
|
|
|
|
// Act
|
|
ContentService.MoveToRecycleBin(content1);
|
|
ContentService.EmptyRecycleBin();
|
|
var contents = ContentService.GetPagedContentInRecycleBin(0, int.MaxValue, out var _).ToList();
|
|
|
|
// Assert
|
|
Assert.That(contents.Any(), Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Move_Content()
|
|
{
|
|
// Arrange
|
|
var content = ContentService.GetById(Trashed.Id);
|
|
|
|
// Act - moving out of recycle bin
|
|
ContentService.Move(content, Textpage.Id);
|
|
|
|
// Assert
|
|
Assert.That(content.ParentId, Is.EqualTo(Textpage.Id));
|
|
Assert.That(content.Trashed, Is.False);
|
|
Assert.That(content.Published, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Copy_Content()
|
|
{
|
|
// Arrange
|
|
var temp = ContentService.GetById(Subpage.Id);
|
|
|
|
// Act
|
|
var copy = ContentService.Copy(temp, temp.ParentId, false);
|
|
var content = ContentService.GetById(Subpage.Id);
|
|
|
|
// Assert
|
|
Assert.That(copy, Is.Not.Null);
|
|
Assert.That(copy.Id, Is.Not.EqualTo(content.Id));
|
|
Assert.AreNotSame(content, copy);
|
|
foreach (var property in copy.Properties)
|
|
{
|
|
Assert.AreEqual(property.GetValue(), content.Properties[property.Alias].GetValue());
|
|
}
|
|
|
|
// Assert.AreNotEqual(content.Name, copy.Name);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Copy_And_Modify_Content_With_Events()
|
|
{
|
|
// see https://github.com/umbraco/Umbraco-CMS/issues/5513
|
|
|
|
var copyingWasCalled = false;
|
|
var copiedWasCalled = false;
|
|
|
|
ContentNotificationHandler.CopyingContent = notification =>
|
|
{
|
|
notification.Copy.SetValue("title", "1");
|
|
notification.Original.SetValue("title", "2");
|
|
|
|
copyingWasCalled = true;
|
|
};
|
|
|
|
ContentNotificationHandler.CopiedContent = notification =>
|
|
{
|
|
var copyVal = notification.Copy.GetValue<string>("title");
|
|
var origVal = notification.Original.GetValue<string>("title");
|
|
|
|
Assert.AreEqual("1", copyVal);
|
|
Assert.AreEqual("2", origVal);
|
|
|
|
copiedWasCalled = true;
|
|
};
|
|
|
|
try
|
|
{
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
|
|
var contentType = ContentTypeBuilder.CreateSimpleContentType(defaultTemplateId: template.Id);
|
|
ContentTypeService.Save(contentType);
|
|
var content = ContentBuilder.CreateSimpleContent(contentType);
|
|
content.SetValue("title", "New Value");
|
|
ContentService.Save(content);
|
|
|
|
var copy = ContentService.Copy(content, content.ParentId, false);
|
|
Assert.AreEqual("1", copy.GetValue("title"));
|
|
|
|
Assert.IsTrue(copyingWasCalled);
|
|
Assert.IsTrue(copiedWasCalled);
|
|
}
|
|
finally
|
|
{
|
|
ContentNotificationHandler.CopyingContent = null;
|
|
ContentNotificationHandler.CopiedContent = null;
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Copy_Recursive()
|
|
{
|
|
// Arrange
|
|
var temp = ContentService.GetById(Textpage.Id);
|
|
Assert.AreEqual("Textpage", temp.Name);
|
|
Assert.AreEqual(3, ContentService.CountChildren(temp.Id));
|
|
|
|
// Act
|
|
var copy = ContentService.Copy(temp, temp.ParentId, false, true);
|
|
var content = ContentService.GetById(Textpage.Id);
|
|
|
|
// Assert
|
|
Assert.That(copy, Is.Not.Null);
|
|
Assert.That(copy.Id, Is.Not.EqualTo(content.Id));
|
|
Assert.AreNotSame(content, copy);
|
|
Assert.AreEqual(3, ContentService.CountChildren(copy.Id));
|
|
|
|
var child = ContentService.GetById(Subpage.Id);
|
|
var childCopy = ContentService.GetPagedChildren(copy.Id, 0, 500, out var total).First();
|
|
Assert.AreEqual(childCopy.Name, child.Name);
|
|
Assert.AreNotEqual(childCopy.Id, child.Id);
|
|
Assert.AreNotEqual(childCopy.Key, child.Key);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Copy_NonRecursive()
|
|
{
|
|
// Arrange
|
|
var temp = ContentService.GetById(Textpage.Id);
|
|
Assert.AreEqual("Textpage", temp.Name);
|
|
Assert.AreEqual(3, ContentService.CountChildren(temp.Id));
|
|
|
|
// Act
|
|
var copy = ContentService.Copy(temp, temp.ParentId, false, false);
|
|
var content = ContentService.GetById(Textpage.Id);
|
|
|
|
// Assert
|
|
Assert.That(copy, Is.Not.Null);
|
|
Assert.That(copy.Id, Is.Not.EqualTo(content.Id));
|
|
Assert.AreNotSame(content, copy);
|
|
Assert.AreEqual(0, ContentService.CountChildren(copy.Id));
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Copy_Content_With_Tags()
|
|
{
|
|
const string propAlias = "tags";
|
|
|
|
// create a content type that has a 'tags' property
|
|
// the property needs to support tags, else nothing works of course!
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
var contentType =
|
|
ContentTypeBuilder.CreateSimpleTagsContentType("umbTagsPage", "TagsPage",
|
|
defaultTemplateId: template.Id);
|
|
contentType.Key = new Guid("78D96D30-1354-4A1E-8450-377764200C58");
|
|
ContentTypeService.Save(contentType);
|
|
|
|
var content = ContentBuilder.CreateSimpleContent(contentType, "Simple Tags Page");
|
|
content.AssignTags(PropertyEditorCollection, DataTypeService, Serializer, propAlias,
|
|
new[] { "hello", "world" });
|
|
ContentService.Save(content);
|
|
|
|
// value has been set but no tags have been created (not published)
|
|
Assert.AreEqual("[\"hello\",\"world\"]", content.GetValue(propAlias));
|
|
var contentTags = TagService.GetTagsForEntity(content.Id).ToArray();
|
|
Assert.AreEqual(0, contentTags.Length);
|
|
|
|
// reloading the content yields the same result
|
|
content = (Content)ContentService.GetById(content.Id);
|
|
Assert.AreEqual("[\"hello\",\"world\"]", content.GetValue(propAlias));
|
|
contentTags = TagService.GetTagsForEntity(content.Id).ToArray();
|
|
Assert.AreEqual(0, contentTags.Length);
|
|
|
|
// publish
|
|
ContentService.Publish(content, new []{ "*" });
|
|
|
|
// now tags have been set (published)
|
|
Assert.AreEqual("[\"hello\",\"world\"]", content.GetValue(propAlias));
|
|
contentTags = TagService.GetTagsForEntity(content.Id).ToArray();
|
|
Assert.AreEqual(2, contentTags.Length);
|
|
|
|
// copy
|
|
var copy = ContentService.Copy(content, content.ParentId, false);
|
|
|
|
// copy is not published, so property has value, but no tags have been created
|
|
Assert.AreEqual("[\"hello\",\"world\"]", copy.GetValue(propAlias));
|
|
var copiedTags = TagService.GetTagsForEntity(copy.Id).ToArray();
|
|
Assert.AreEqual(0, copiedTags.Length);
|
|
|
|
// publish
|
|
ContentService.Publish(copy, new []{ "*" });
|
|
|
|
// now tags have been set (published)
|
|
copiedTags = TagService.GetTagsForEntity(copy.Id).ToArray();
|
|
|
|
Assert.AreEqual(2, copiedTags.Length);
|
|
Assert.AreEqual("hello", copiedTags[0].Text);
|
|
Assert.AreEqual("world", copiedTags[1].Text);
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Rollback_Version_On_Content()
|
|
{
|
|
// Arrange
|
|
var parent = ContentService.GetById(Textpage.Id);
|
|
Assert.IsFalse(parent.Published);
|
|
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);
|
|
|
|
var versions = ContentService.GetVersions(Subpage.Id).ToList();
|
|
Assert.AreEqual(1, versions.Count);
|
|
|
|
var version1 = content.VersionId;
|
|
|
|
content.Name = "Text Page 2 Updated";
|
|
content.SetValue("author", "Francis Doe");
|
|
|
|
// non published = edited
|
|
Assert.IsTrue(content.Edited);
|
|
|
|
ContentService.Save(content);
|
|
ContentService.Publish(content, content.AvailableCultures.ToArray()); // new version
|
|
var version2 = content.VersionId;
|
|
Assert.AreNotEqual(version1, version2);
|
|
|
|
Assert.IsTrue(content.Published);
|
|
Assert.IsFalse(content.Edited);
|
|
Assert.AreEqual("Francis Doe",
|
|
ContentService.GetById(content.Id).GetValue<string>("author")); // version2 author is Francis
|
|
|
|
Assert.AreEqual("Text Page 2 Updated", content.Name);
|
|
Assert.AreEqual("Text Page 2 Updated", content.PublishName);
|
|
|
|
content.Name = "Text Page 2 ReUpdated";
|
|
content.SetValue("author", "Jane Doe");
|
|
|
|
// is not actually 'edited' until changes have been saved
|
|
Assert.IsFalse(content.Edited);
|
|
ContentService.Save(content); // just save changes
|
|
Assert.IsTrue(content.Edited);
|
|
|
|
Assert.AreEqual("Text Page 2 ReUpdated", content.Name);
|
|
Assert.AreEqual("Text Page 2 Updated", content.PublishName);
|
|
|
|
content.Name = "Text Page 2 ReReUpdated";
|
|
|
|
ContentService.Save(content);
|
|
ContentService.Publish(content, content.AvailableCultures.ToArray()); // new version
|
|
var version3 = content.VersionId;
|
|
Assert.AreNotEqual(version2, version3);
|
|
|
|
Assert.IsTrue(content.Published);
|
|
Assert.IsFalse(content.Edited);
|
|
Assert.AreEqual("Jane Doe",
|
|
ContentService.GetById(content.Id).GetValue<string>("author")); // version3 author is Jane
|
|
|
|
Assert.AreEqual("Text Page 2 ReReUpdated", content.Name);
|
|
Assert.AreEqual("Text Page 2 ReReUpdated", content.PublishName);
|
|
|
|
// here we have
|
|
// version1, first published version
|
|
// version2, second published version
|
|
// version3, third and current published version
|
|
|
|
// rollback all values to version1
|
|
var rollback = ContentService.GetById(Subpage.Id);
|
|
var rollto = ContentService.GetVersion(version1);
|
|
rollback.CopyFrom(rollto);
|
|
rollback.Name = rollto.Name; // must do it explicitly
|
|
ContentService.Save(rollback);
|
|
|
|
Assert.IsNotNull(rollback);
|
|
Assert.IsTrue(rollback.Published);
|
|
Assert.IsTrue(rollback.Edited);
|
|
Assert.AreEqual("Francis Doe",
|
|
ContentService.GetById(content.Id).GetValue<string>("author")); // author is now Francis again
|
|
Assert.AreEqual(version3, rollback.VersionId); // same version but with edits
|
|
|
|
// props and name have rolled back
|
|
Assert.AreEqual("Francis Doe", rollback.GetValue<string>("author"));
|
|
Assert.AreEqual("Text Page 2 Updated", rollback.Name);
|
|
|
|
// published props and name are still there
|
|
Assert.AreEqual("Jane Doe", rollback.GetValue<string>("author", published: true));
|
|
Assert.AreEqual("Text Page 2 ReReUpdated", rollback.PublishName);
|
|
|
|
// rollback all values to current version
|
|
// special because... current has edits... this really is equivalent to rolling back to version2
|
|
var rollback2 = ContentService.GetById(Subpage.Id);
|
|
var rollto2 = ContentService.GetVersion(version3);
|
|
rollback2.CopyFrom(rollto2);
|
|
rollback2.Name = rollto2.PublishName; // must do it explicitely AND must pick the publish one!
|
|
ContentService.Save(rollback2);
|
|
|
|
Assert.IsTrue(rollback2.Published);
|
|
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);
|
|
|
|
// test rollback to self, again
|
|
content = ContentService.GetById(content.Id);
|
|
Assert.AreEqual("Text Page 2 ReReUpdated", content.Name);
|
|
Assert.AreEqual("Jane Doe", content.GetValue<string>("author"));
|
|
ContentService.Save(content);
|
|
ContentService.Publish(content, content.AvailableCultures.ToArray());
|
|
Assert.IsFalse(content.Edited);
|
|
content.Name = "Xxx";
|
|
content.SetValue("author", "Bob Doe");
|
|
ContentService.Save(content);
|
|
Assert.IsTrue(content.Edited);
|
|
rollto = ContentService.GetVersion(content.VersionId);
|
|
content.CopyFrom(rollto);
|
|
content.Name = rollto.PublishName; // must do it explicitely AND must pick the publish one!
|
|
ContentService.Save(content);
|
|
Assert.IsTrue(content.Edited); //Still edited, change of behaviour
|
|
Assert.AreEqual("Text Page 2 ReReUpdated", content.Name);
|
|
Assert.AreEqual("Jane Doe", content.GetValue("author"));
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public async Task Can_Rollback_Version_On_Multilingual()
|
|
{
|
|
var langFr = new LanguageBuilder()
|
|
.WithCultureInfo("fr")
|
|
.Build();
|
|
var langDa = new LanguageBuilder()
|
|
.WithCultureInfo("da")
|
|
.Build();
|
|
await LanguageService.CreateAsync(langFr, Constants.Security.SuperUserKey);
|
|
await LanguageService.CreateAsync(langDa, Constants.Security.SuperUserKey);
|
|
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
|
|
var contentType =
|
|
ContentTypeBuilder.CreateSimpleContentType("multi", "Multi", defaultTemplateId: template.Id);
|
|
contentType.Key = new Guid("45FF9A70-9C5F-448D-A476-DCD23566BBF8");
|
|
contentType.Variations = ContentVariation.Culture;
|
|
var p1 = contentType.PropertyTypes.First();
|
|
p1.Variations = ContentVariation.Culture;
|
|
ContentTypeService.Save(contentType);
|
|
|
|
var page = new Content("Page", Constants.System.Root, contentType)
|
|
{
|
|
Level = 1,
|
|
SortOrder = 1,
|
|
CreatorId = 0,
|
|
WriterId = 0,
|
|
Key = new Guid("D7B84CC9-14AE-4D92-A042-023767AD3304")
|
|
};
|
|
|
|
page.SetCultureName("fr1", langFr.IsoCode);
|
|
page.SetCultureName("da1", langDa.IsoCode);
|
|
Thread.Sleep(1);
|
|
ContentService.Save(page);
|
|
var versionId0 = page.VersionId;
|
|
|
|
page.SetValue(p1.Alias, "v1fr", langFr.IsoCode);
|
|
page.SetValue(p1.Alias, "v1da", langDa.IsoCode);
|
|
Thread.Sleep(1);
|
|
ContentService.Save(page);
|
|
ContentService.Publish(page, page.AvailableCultures.ToArray());
|
|
var versionId1 = page.VersionId;
|
|
|
|
Thread.Sleep(10);
|
|
|
|
page.SetCultureName("fr2", langFr.IsoCode);
|
|
page.SetValue(p1.Alias, "v2fr", langFr.IsoCode);
|
|
Thread.Sleep(1);
|
|
ContentService.Save(page);
|
|
ContentService.Publish(page, new[] { langFr.IsoCode });
|
|
var versionId2 = page.VersionId;
|
|
|
|
Thread.Sleep(10);
|
|
|
|
page.SetCultureName("da2", langDa.IsoCode);
|
|
page.SetValue(p1.Alias, "v2da", langDa.IsoCode);
|
|
Thread.Sleep(1);
|
|
ContentService.Save(page);
|
|
ContentService.Publish(page, new[] { langDa.IsoCode });
|
|
var versionId3 = page.VersionId;
|
|
|
|
Thread.Sleep(10);
|
|
|
|
page.SetCultureName("fr3", langFr.IsoCode);
|
|
page.SetCultureName("da3", langDa.IsoCode);
|
|
page.SetValue(p1.Alias, "v3fr", langFr.IsoCode);
|
|
page.SetValue(p1.Alias, "v3da", langDa.IsoCode);
|
|
Thread.Sleep(1);
|
|
ContentService.Save(page);
|
|
ContentService.Publish(page, page.AvailableCultures.ToArray());
|
|
var versionId4 = page.VersionId;
|
|
|
|
// now get all versions
|
|
var versions = ContentService.GetVersions(page.Id).ToArray();
|
|
|
|
Assert.AreEqual(5, versions.Length);
|
|
|
|
// current version
|
|
Assert.AreEqual(versionId4, versions[0].VersionId);
|
|
Assert.AreEqual(versionId3, versions[0].PublishedVersionId);
|
|
|
|
// published version
|
|
Assert.AreEqual(versionId3, versions[1].VersionId);
|
|
Assert.AreEqual(versionId3, versions[1].PublishedVersionId);
|
|
|
|
// previous version
|
|
Assert.AreEqual(versionId2, versions[2].VersionId);
|
|
Assert.AreEqual(versionId3, versions[2].PublishedVersionId);
|
|
|
|
// previous version
|
|
Assert.AreEqual(versionId1, versions[3].VersionId);
|
|
Assert.AreEqual(versionId3, versions[3].PublishedVersionId);
|
|
|
|
// previous version
|
|
Assert.AreEqual(versionId0, versions[4].VersionId);
|
|
Assert.AreEqual(versionId3, versions[4].PublishedVersionId);
|
|
|
|
Assert.AreEqual("fr3", versions[4].GetPublishName(langFr.IsoCode));
|
|
Assert.AreEqual("fr3", versions[3].GetPublishName(langFr.IsoCode));
|
|
Assert.AreEqual("fr3", versions[2].GetPublishName(langFr.IsoCode));
|
|
Assert.AreEqual("fr3", versions[1].GetPublishName(langFr.IsoCode));
|
|
Assert.AreEqual("fr3", versions[0].GetPublishName(langFr.IsoCode));
|
|
|
|
Assert.AreEqual("fr1", versions[4].GetCultureName(langFr.IsoCode));
|
|
Assert.AreEqual("fr2", versions[3].GetCultureName(langFr.IsoCode));
|
|
Assert.AreEqual("fr2", versions[2].GetCultureName(langFr.IsoCode));
|
|
Assert.AreEqual("fr3", versions[1].GetCultureName(langFr.IsoCode));
|
|
Assert.AreEqual("fr3", versions[0].GetCultureName(langFr.IsoCode));
|
|
|
|
Assert.AreEqual("da3", versions[4].GetPublishName(langDa.IsoCode));
|
|
Assert.AreEqual("da3", versions[3].GetPublishName(langDa.IsoCode));
|
|
Assert.AreEqual("da3", versions[2].GetPublishName(langDa.IsoCode));
|
|
Assert.AreEqual("da3", versions[1].GetPublishName(langDa.IsoCode));
|
|
Assert.AreEqual("da3", versions[0].GetPublishName(langDa.IsoCode));
|
|
|
|
Assert.AreEqual("da1", versions[4].GetCultureName(langDa.IsoCode));
|
|
Assert.AreEqual("da1", versions[3].GetCultureName(langDa.IsoCode));
|
|
Assert.AreEqual("da2", versions[2].GetCultureName(langDa.IsoCode));
|
|
Assert.AreEqual("da3", versions[1].GetCultureName(langDa.IsoCode));
|
|
Assert.AreEqual("da3", versions[0].GetCultureName(langDa.IsoCode));
|
|
|
|
// all versions have the same publish infos
|
|
for (var i = 0; i < 5; i++)
|
|
{
|
|
Assert.AreEqual(versions[0].PublishDate, versions[i].PublishDate);
|
|
Assert.AreEqual(versions[0].GetPublishDate(langFr.IsoCode), versions[i].GetPublishDate(langFr.IsoCode));
|
|
Assert.AreEqual(versions[0].GetPublishDate(langDa.IsoCode), versions[i].GetPublishDate(langDa.IsoCode));
|
|
}
|
|
|
|
for (var i = 0; i < 5; i++)
|
|
{
|
|
Console.Write("[{0}] ", i);
|
|
Console.WriteLine(versions[i].UpdateDate.ToString("O")[11..]);
|
|
Console.WriteLine(" fr: {0}", versions[i].GetUpdateDate(langFr.IsoCode)?.ToString("O")[11..]);
|
|
Console.WriteLine(" da: {0}", versions[i].GetUpdateDate(langDa.IsoCode)?.ToString("O")[11..]);
|
|
}
|
|
|
|
Console.WriteLine("-");
|
|
|
|
// for all previous versions, UpdateDate is the published date
|
|
Assert.AreEqual(versions[4].UpdateDate, versions[4].GetUpdateDate(langFr.IsoCode));
|
|
Assert.AreEqual(versions[4].UpdateDate, versions[4].GetUpdateDate(langDa.IsoCode));
|
|
|
|
Assert.AreEqual(versions[3].UpdateDate, versions[3].GetUpdateDate(langFr.IsoCode));
|
|
Assert.AreEqual(versions[4].UpdateDate, versions[3].GetUpdateDate(langDa.IsoCode));
|
|
|
|
Assert.AreEqual(versions[3].UpdateDate, versions[2].GetUpdateDate(langFr.IsoCode));
|
|
Assert.AreEqual(versions[2].UpdateDate, versions[2].GetUpdateDate(langDa.IsoCode));
|
|
|
|
// for the published version, UpdateDate is the published date
|
|
Assert.AreEqual(versions[1].UpdateDate, versions[1].GetUpdateDate(langFr.IsoCode));
|
|
Assert.AreEqual(versions[1].UpdateDate, versions[1].GetUpdateDate(langDa.IsoCode));
|
|
Assert.AreEqual(versions[1].PublishDate, versions[1].UpdateDate);
|
|
|
|
// for the current version, things are different
|
|
// UpdateDate is the date it was last saved
|
|
Assert.AreEqual(versions[0].UpdateDate, versions[0].GetUpdateDate(langFr.IsoCode));
|
|
Assert.AreEqual(versions[0].UpdateDate, versions[0].GetUpdateDate(langDa.IsoCode));
|
|
|
|
// so if we save again...
|
|
page.SetCultureName("fr4", langFr.IsoCode);
|
|
|
|
// page.SetCultureName("da4", langDa.IsoCode);
|
|
page.SetValue(p1.Alias, "v4fr", langFr.IsoCode);
|
|
page.SetValue(p1.Alias, "v4da", langDa.IsoCode);
|
|
|
|
// This sleep ensures the save is called on later ticks then the SetValue and SetCultureName. Therefore
|
|
// we showcase the currect lack of handling dirty on variants on save. When this is implemented the sleep
|
|
// helps showcase the functionality is actually working
|
|
Thread.Sleep(5);
|
|
ContentService.Save(page);
|
|
var versionId5 = page.VersionId;
|
|
|
|
versions = ContentService.GetVersions(page.Id).ToArray();
|
|
|
|
// we just update the current version
|
|
Assert.AreEqual(5, versions.Length);
|
|
Assert.AreEqual(versionId4, versionId5);
|
|
|
|
for (var i = 0; i < 5; i++)
|
|
{
|
|
Console.Write("[{0}] ", i);
|
|
Console.WriteLine(versions[i].UpdateDate.ToString("O")[11..]);
|
|
Console.WriteLine(" fr: {0}", versions[i].GetUpdateDate(langFr.IsoCode)?.ToString("O")[11..]);
|
|
Console.WriteLine(" da: {0}", versions[i].GetUpdateDate(langDa.IsoCode)?.ToString("O")[11..]);
|
|
}
|
|
|
|
Console.WriteLine("-");
|
|
|
|
var versionsSlim = ContentService.GetVersionsSlim(page.Id, 0, 50).ToArray();
|
|
Assert.AreEqual(5, versionsSlim.Length);
|
|
|
|
for (var i = 0; i < 5; i++)
|
|
{
|
|
Console.Write("[{0}] ", i);
|
|
Console.WriteLine(versionsSlim[i].UpdateDate.Ticks);
|
|
Console.WriteLine(" fr: {0}", versionsSlim[i].GetUpdateDate(langFr.IsoCode)?.Ticks);
|
|
Console.WriteLine(" da: {0}", versionsSlim[i].GetUpdateDate(langDa.IsoCode)?.Ticks);
|
|
}
|
|
|
|
Console.WriteLine("-");
|
|
|
|
// what we do in the controller to get rollback versions
|
|
var versionsSlimFr =
|
|
versionsSlim.Where(x => x.UpdateDate == x.GetUpdateDate(langFr.IsoCode)).ToArray();
|
|
|
|
Assert.AreEqual(4, versionsSlimFr.Length);
|
|
|
|
// alas, at the moment we do *not* properly track 'dirty' for cultures, meaning
|
|
// that we cannot synchronize dates the way we do with publish dates - and so this
|
|
// would fail - the version UpdateDate is greater than the cultures'.
|
|
Assert.AreEqual(versions[0].UpdateDate, versions[0].GetUpdateDate(langFr.IsoCode));
|
|
Assert.AreEqual(versions[0].UpdateDate, versions[0].GetUpdateDate(langDa.IsoCode));
|
|
|
|
// now roll french back to its very first version
|
|
page.CopyFrom(versions[4], langFr.IsoCode); // only the pure FR values
|
|
page.CopyFrom(versions[4], null); // so, must explicitly do the INVARIANT values too
|
|
page.SetCultureName(versions[4].GetPublishName(langFr.IsoCode), langFr.IsoCode);
|
|
ContentService.Save(page);
|
|
|
|
// and voila, rolled back!
|
|
Assert.AreEqual(versions[4].GetPublishName(langFr.IsoCode), page.GetCultureName(langFr.IsoCode));
|
|
Assert.AreEqual(versions[4].GetValue(p1.Alias, langFr.IsoCode), page.GetValue(p1.Alias, langFr.IsoCode));
|
|
|
|
// note that rolling back invariant values means we also rolled back... DA... at least partially
|
|
// bah?
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Save_Lazy_Content()
|
|
{
|
|
var contentType = ContentTypeService.Get("umbTextpage");
|
|
var root = ContentService.GetById(Textpage.Id);
|
|
|
|
var c = new Lazy<IContent>(() =>
|
|
ContentBuilder.CreateSimpleContent(contentType, "Hierarchy Simple Text Page", root.Id));
|
|
var c2 = new Lazy<IContent>(() =>
|
|
ContentBuilder.CreateSimpleContent(contentType, "Hierarchy Simple Text Subpage", c.Value.Id));
|
|
var list = new List<Lazy<IContent>> { c, c2 };
|
|
|
|
using (var scope = ScopeProvider.CreateScope())
|
|
{
|
|
var repository = DocumentRepository;
|
|
|
|
foreach (var content in list)
|
|
{
|
|
repository.Save(content.Value);
|
|
}
|
|
|
|
Assert.That(c.Value.HasIdentity, Is.True);
|
|
Assert.That(c2.Value.HasIdentity, Is.True);
|
|
|
|
Assert.That(c.Value.Id > 0, Is.True);
|
|
Assert.That(c2.Value.Id > 0, Is.True);
|
|
|
|
Assert.That(c.Value.ParentId > 0, Is.True);
|
|
Assert.That(c2.Value.ParentId > 0, Is.True);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void Can_Verify_Property_Types_On_Content()
|
|
{
|
|
// Arrange
|
|
var contentTypeService = ContentTypeService;
|
|
var contentType = ContentTypeBuilder.CreateAllTypesContentType("allDataTypes", "All DataTypes");
|
|
ContentTypeService.Save(contentType);
|
|
var content =
|
|
ContentBuilder.CreateAllTypesContent(contentType, "Random Content", Constants.System.Root);
|
|
ContentService.Save(content);
|
|
var id = content.Id;
|
|
|
|
// Act
|
|
var sut = ContentService.GetById(id);
|
|
|
|
// Arrange
|
|
Assert.That(sut.GetValue<bool>("isTrue"), Is.True);
|
|
Assert.That(sut.GetValue<int>("number"), Is.EqualTo(42));
|
|
Assert.That(sut.GetValue<string>("bodyText"), Is.EqualTo("Lorem Ipsum Body Text Test"));
|
|
Assert.That(sut.GetValue<string>("singleLineText"), Is.EqualTo("Single Line Text Test"));
|
|
Assert.That(sut.GetValue<string>("multilineText"), Is.EqualTo("Multiple lines \n in one box"));
|
|
Assert.That(sut.GetValue<string>("upload"), Is.EqualTo("/media/1234/koala.jpg"));
|
|
Assert.That(sut.GetValue<string>("label"), Is.EqualTo("Non-editable label"));
|
|
|
|
// SD: This is failing because the 'content' call to GetValue<DateTime> always has empty milliseconds
|
|
// MCH: I'm guessing this is an issue because of the format the date is actually stored as, right? Cause we don't do any formatting when saving or loading
|
|
Assert.That(sut.GetValue<DateTime>("dateTime").ToString("G"),
|
|
Is.EqualTo(content.GetValue<DateTime>("dateTime").ToString("G")));
|
|
Assert.That(sut.GetValue<string>("colorPicker"), Is.EqualTo("black"));
|
|
Assert.That(sut.GetValue<string>("ddlMultiple"), Is.EqualTo("1234,1235"));
|
|
Assert.That(sut.GetValue<string>("rbList"), Is.EqualTo("random"));
|
|
Assert.That(sut.GetValue<DateTime>("date").ToString("G"),
|
|
Is.EqualTo(content.GetValue<DateTime>("date").ToString("G")));
|
|
Assert.That(sut.GetValue<string>("ddl"), Is.EqualTo("1234"));
|
|
Assert.That(sut.GetValue<string>("chklist"), Is.EqualTo("randomc"));
|
|
Assert.That(sut.GetValue<Udi>("contentPicker"),
|
|
Is.EqualTo(Udi.Create(Constants.UdiEntityType.Document,
|
|
new Guid("74ECA1D4-934E-436A-A7C7-36CC16D4095C"))));
|
|
Assert.That(sut.GetValue<Udi>("memberPicker"),
|
|
Is.EqualTo(Udi.Create(Constants.UdiEntityType.Member,
|
|
new Guid("9A50A448-59C0-4D42-8F93-4F1D55B0F47D"))));
|
|
Assert.That(sut.GetValue<string>("multiUrlPicker"),
|
|
Is.EqualTo("[{\"name\":\"https://test.com\",\"url\":\"https://test.com\"}]"));
|
|
Assert.That(sut.GetValue<string>("tags"), Is.EqualTo("this,is,tags"));
|
|
Assert.That(
|
|
sut.GetValue<string>("dateTimeWithTimeZone"),
|
|
Is.EqualTo("{\"date\":\"2025-01-22T18:33:01.0000000+01:00\",\"timeZone\":\"Europe/Copenhagen\"}"));
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public void Can_Delete_Previous_Versions_Not_Latest()
|
|
{
|
|
// Arrange
|
|
var content = ContentService.GetById(Trashed.Id);
|
|
var version = content.VersionId;
|
|
|
|
// Act
|
|
ContentService.DeleteVersion(Trashed.Id, version, true);
|
|
var sut = ContentService.GetById(Trashed.Id);
|
|
|
|
// Assert
|
|
Assert.That(sut.VersionId, Is.EqualTo(version));
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public void Can_Get_Paged_Children()
|
|
{
|
|
// Start by cleaning the "db"
|
|
var umbTextPage = ContentService.GetById(new Guid("B58B3AD4-62C2-4E27-B1BE-837BD7C533E0"));
|
|
ContentService.Delete(umbTextPage);
|
|
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
|
|
var contentType = ContentTypeBuilder.CreateSimpleContentType(defaultTemplateId: template.Id);
|
|
ContentTypeService.Save(contentType);
|
|
for (var i = 0; i < 10; i++)
|
|
{
|
|
var c1 = ContentBuilder.CreateSimpleContent(contentType);
|
|
ContentService.Save(c1);
|
|
}
|
|
|
|
var entities = ContentService.GetPagedChildren(Constants.System.Root, 0, 6, out var total).ToArray();
|
|
Assert.That(entities.Length, Is.EqualTo(6));
|
|
Assert.That(total, Is.EqualTo(10));
|
|
entities = ContentService.GetPagedChildren(Constants.System.Root, 1, 6, out total).ToArray();
|
|
Assert.That(entities.Length, Is.EqualTo(4));
|
|
Assert.That(total, Is.EqualTo(10));
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public void Can_Get_Paged_Children_Dont_Get_Descendants()
|
|
{
|
|
// Start by cleaning the "db"
|
|
var umbTextPage = ContentService.GetById(new Guid("B58B3AD4-62C2-4E27-B1BE-837BD7C533E0"));
|
|
ContentService.Delete(umbTextPage);
|
|
|
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
|
FileService.SaveTemplate(template);
|
|
|
|
var contentType = ContentTypeBuilder.CreateSimpleContentType(defaultTemplateId: template.Id);
|
|
ContentTypeService.Save(contentType);
|
|
|
|
// Only add 9 as we also add a content with children
|
|
for (var i = 0; i < 9; i++)
|
|
{
|
|
var c1 = ContentBuilder.CreateSimpleContent(contentType);
|
|
ContentService.Save(c1);
|
|
}
|
|
|
|
var willHaveChildren = ContentBuilder.CreateSimpleContent(contentType);
|
|
ContentService.Save(willHaveChildren);
|
|
for (var i = 0; i < 10; i++)
|
|
{
|
|
var c1 = ContentBuilder.CreateSimpleContent(contentType, "Content" + i, willHaveChildren.Id);
|
|
ContentService.Save(c1);
|
|
}
|
|
|
|
// children in root including the folder - not the descendants in the folder
|
|
var entities = ContentService.GetPagedChildren(Constants.System.Root, 0, 6, out var total).ToArray();
|
|
Assert.That(entities.Length, Is.EqualTo(6));
|
|
Assert.That(total, Is.EqualTo(10));
|
|
entities = ContentService.GetPagedChildren(Constants.System.Root, 1, 6, out total).ToArray();
|
|
Assert.That(entities.Length, Is.EqualTo(4));
|
|
Assert.That(total, Is.EqualTo(10));
|
|
|
|
// children in folder
|
|
entities = ContentService.GetPagedChildren(willHaveChildren.Id, 0, 6, out total).ToArray();
|
|
Assert.That(entities.Length, Is.EqualTo(6));
|
|
Assert.That(total, Is.EqualTo(10));
|
|
entities = ContentService.GetPagedChildren(willHaveChildren.Id, 1, 6, out total).ToArray();
|
|
Assert.That(entities.Length, Is.EqualTo(4));
|
|
Assert.That(total, Is.EqualTo(10));
|
|
}
|
|
|
|
[Test]
|
|
public void PublishingTest()
|
|
{
|
|
var contentType = new ContentType(ShortStringHelper, Constants.System.Root) { Alias = "foo", Name = "Foo" };
|
|
|
|
var properties = new PropertyTypeCollection(true)
|
|
{
|
|
new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext)
|
|
{
|
|
Alias = "title", Name = "Title", Mandatory = false, DataTypeId = -88
|
|
}
|
|
};
|
|
|
|
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Alias = "content", Name = "content" });
|
|
|
|
contentType.SetDefaultTemplate(new Template(ShortStringHelper, "Textpage", "textpage"));
|
|
FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType!
|
|
ContentTypeService.Save(contentType);
|
|
|
|
var content = ContentService.Create("foo", Constants.System.Root, "foo");
|
|
ContentService.Save(content);
|
|
|
|
Assert.IsFalse(content.Published);
|
|
Assert.IsTrue(content.Edited);
|
|
|
|
content = ContentService.GetById(content.Id);
|
|
Assert.IsFalse(content.Published);
|
|
Assert.IsTrue(content.Edited);
|
|
|
|
content.SetValue("title", "foo");
|
|
Assert.IsTrue(content.Edited);
|
|
|
|
ContentService.Save(content);
|
|
|
|
Assert.IsFalse(content.Published);
|
|
Assert.IsTrue(content.Edited);
|
|
|
|
content = ContentService.GetById(content.Id);
|
|
Assert.IsFalse(content.Published);
|
|
Assert.IsTrue(content.Edited);
|
|
|
|
var versions = ContentService.GetVersions(content.Id);
|
|
Assert.AreEqual(1, versions.Count());
|
|
|
|
// publish content
|
|
// becomes Published, !Edited
|
|
// creates a new version
|
|
// can get published property values
|
|
ContentService.Publish(content, new []{ "*" });
|
|
|
|
Assert.IsTrue(content.Published);
|
|
Assert.IsFalse(content.Edited);
|
|
|
|
content = ContentService.GetById(content.Id);
|
|
Assert.IsTrue(content.Published);
|
|
Assert.IsFalse(content.Edited);
|
|
|
|
versions = ContentService.GetVersions(content.Id);
|
|
Assert.AreEqual(2, versions.Count());
|
|
|
|
Assert.AreEqual("foo", content.GetValue("title", published: true));
|
|
Assert.AreEqual("foo", content.GetValue("title"));
|
|
|
|
// unpublish content
|
|
// becomes !Published, Edited
|
|
ContentService.Unpublish(content);
|
|
|
|
Assert.IsFalse(content.Published);
|
|
Assert.IsTrue(content.Edited);
|
|
|
|
Assert.IsNull(content.GetValue("title", published: true));
|
|
Assert.AreEqual("foo", content.GetValue("title"));
|
|
|
|
var vpk = ((Content)content).VersionId;
|
|
var ppk = ((Content)content).PublishedVersionId;
|
|
|
|
content = ContentService.GetById(content.Id);
|
|
Assert.IsFalse(content.Published);
|
|
Assert.IsTrue(content.Edited);
|
|
|
|
// TODO: depending on 1 line in ContentBaseFactory.BuildEntity
|
|
// the published infos can be gone or not
|
|
// if gone, it's not consistent with above
|
|
Assert.AreEqual(vpk, ((Content)content).VersionId);
|
|
Assert.AreEqual(ppk, ((Content)content).PublishedVersionId); // still there
|
|
|
|
// TODO: depending on 1 line in ContentRepository.MapDtoToContent
|
|
// the published values can be null or not
|
|
// if null, it's not consistent with above
|
|
// Assert.IsNull(content.GetValue("title", published: true));
|
|
Assert.AreEqual("foo", content.GetValue("title", published: true)); // still there
|
|
Assert.AreEqual("foo", content.GetValue("title"));
|
|
|
|
versions = ContentService.GetVersions(content.Id);
|
|
Assert.AreEqual(2, versions.Count());
|
|
|
|
// ah - we have a problem here - since we're not published we don't have published values
|
|
// and therefore we cannot "just" republish the content - we need to publish some values
|
|
// so... that's not really an option
|
|
//
|
|
// ContentService.Publish(content, new []{ "*" });
|
|
|
|
// TODO: what shall we do of all this?
|
|
/*
|
|
// this basically republishes a content
|
|
// what if it never was published?
|
|
// what if it has changes?
|
|
// do we want to "publish" only some variants, or the entire content?
|
|
ContentService.Publish(content);
|
|
|
|
Assert.IsTrue(content.Published);
|
|
Assert.IsFalse(content.Edited);
|
|
|
|
// TODO: should it be 2 or 3
|
|
versions = ContentService.GetVersions(content.Id);
|
|
Assert.AreEqual(2, versions.Count());
|
|
|
|
// TODO: now test rollbacks
|
|
var version = ContentService.GetByVersion(content.Id); // test that it gets a version - should be GetVersion
|
|
var previousVersion = ContentService.GetVersions(content.Id).Skip(1).FirstOrDefault(); // need an optimized way to do this
|
|
content.CopyValues(version); // copies the edited value - always
|
|
content.Template = version.Template;
|
|
content.Name = version.Name;
|
|
ContentService.Save(content); // this is effectively a rollback?
|
|
ContentService.Rollback(content); // just kill the method and offer options on values + template + name...
|
|
*/
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public async Task Ensure_Invariant_Name()
|
|
{
|
|
var languageService = LanguageService;
|
|
|
|
var langUk = new LanguageBuilder()
|
|
.WithCultureInfo("en-GB")
|
|
.WithIsDefault(true)
|
|
.Build();
|
|
var langFr = new LanguageBuilder()
|
|
.WithCultureInfo("fr-FR")
|
|
.Build();
|
|
|
|
await languageService.CreateAsync(langFr, Constants.Security.SuperUserKey);
|
|
await languageService.CreateAsync(langUk, Constants.Security.SuperUserKey);
|
|
|
|
var contentTypeService = ContentTypeService;
|
|
|
|
var contentType = ContentTypeService.Get("umbTextpage");
|
|
contentType.Variations = ContentVariation.Culture;
|
|
contentType.AddPropertyType(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextBox,
|
|
ValueStorageType.Nvarchar, "prop")
|
|
{ Variations = ContentVariation.Culture });
|
|
ContentTypeService.Save(contentType);
|
|
|
|
var content = new Content(null, Constants.System.Root, contentType);
|
|
|
|
content.SetCultureName("name-us", langUk.IsoCode);
|
|
content.SetCultureName("name-fr", langFr.IsoCode);
|
|
ContentService.Save(content);
|
|
|
|
// the name will be set to the default culture variant name
|
|
Assert.AreEqual("name-us", content.Name);
|
|
|
|
// TODO: should we always sync the invariant name even on update? see EnsureInvariantNameValues
|
|
////updating the default culture variant name should also update the invariant name so they stay in sync
|
|
// content.SetName("name-us-2", langUk.IsoCode);
|
|
// ContentService.Save(content);
|
|
// Assert.AreEqual("name-us-2", content.Name);
|
|
}
|
|
|
|
[Test]
|
|
public async Task Ensure_Unique_Culture_Names()
|
|
{
|
|
var languageService = LanguageService;
|
|
|
|
var langUk = new LanguageBuilder()
|
|
.WithCultureInfo("en-GB")
|
|
.WithIsDefault(true)
|
|
.Build();
|
|
var langFr = new LanguageBuilder()
|
|
.WithCultureInfo("fr-FR")
|
|
.Build();
|
|
|
|
await languageService.CreateAsync(langFr, Constants.Security.SuperUserKey);
|
|
await languageService.CreateAsync(langUk, Constants.Security.SuperUserKey);
|
|
|
|
var contentTypeService = ContentTypeService;
|
|
|
|
var contentType = ContentTypeService.Get("umbTextpage");
|
|
contentType.Variations = ContentVariation.Culture;
|
|
ContentTypeService.Save(contentType);
|
|
|
|
var content = new Content(null, Constants.System.Root, contentType);
|
|
content.SetCultureName("root", langUk.IsoCode);
|
|
ContentService.Save(content);
|
|
|
|
for (var i = 0; i < 5; i++)
|
|
{
|
|
var child = new Content(null, content, contentType);
|
|
child.SetCultureName("child", langUk.IsoCode);
|
|
ContentService.Save(child);
|
|
|
|
Assert.AreEqual("child" + (i == 0 ? string.Empty : " (" + i + ")"),
|
|
child.GetCultureName(langUk.IsoCode));
|
|
|
|
// Save it again to ensure that the unique check is not performed again against it's own name
|
|
ContentService.Save(child);
|
|
Assert.AreEqual("child" + (i == 0 ? string.Empty : " (" + i + ")"),
|
|
child.GetCultureName(langUk.IsoCode));
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public async Task Can_Get_Paged_Children_WithFilterAndOrder()
|
|
{
|
|
var languageService = LanguageService;
|
|
|
|
var langUk = new LanguageBuilder()
|
|
.WithCultureInfo("en-GB")
|
|
.WithIsDefault(true)
|
|
.WithIsMandatory(true)
|
|
.Build();
|
|
var langFr = new LanguageBuilder()
|
|
.WithCultureInfo("fr-FR")
|
|
.Build();
|
|
var langDa = new LanguageBuilder()
|
|
.WithCultureInfo("da-DK")
|
|
.Build();
|
|
|
|
await languageService.CreateAsync(langFr, Constants.Security.SuperUserKey);
|
|
await languageService.CreateAsync(langUk, Constants.Security.SuperUserKey);
|
|
await languageService.CreateAsync(langDa, Constants.Security.SuperUserKey);
|
|
|
|
var contentTypeService = ContentTypeService;
|
|
|
|
var contentType = ContentTypeService.Get("umbTextpage");
|
|
contentType.Variations = ContentVariation.Culture;
|
|
ContentTypeService.Save(contentType);
|
|
|
|
int[] o = { 2, 1, 3, 0, 4 }; // randomly different
|
|
for (var i = 0; i < 5; i++)
|
|
{
|
|
var contentA = new Content(null, Constants.System.Root, contentType);
|
|
contentA.SetCultureName("contentA" + i + "uk", langUk.IsoCode);
|
|
contentA.SetCultureName("contentA" + o[i] + "fr", langFr.IsoCode);
|
|
contentA.SetCultureName("contentX" + i + "da", langDa.IsoCode);
|
|
ContentService.Save(contentA);
|
|
|
|
var contentB = new Content(null, Constants.System.Root, contentType);
|
|
contentB.SetCultureName("contentB" + i + "uk", langUk.IsoCode);
|
|
contentB.SetCultureName("contentB" + o[i] + "fr", langFr.IsoCode);
|
|
contentB.SetCultureName("contentX" + i + "da", langDa.IsoCode);
|
|
ContentService.Save(contentB);
|
|
}
|
|
|
|
// get all
|
|
var list = ContentService.GetPagedChildren(Constants.System.Root, 0, 100, out var total).ToList();
|
|
|
|
Console.WriteLine("ALL");
|
|
WriteList(list);
|
|
|
|
// 10 items (there's already a Home content in there...)
|
|
Assert.AreEqual(11, total);
|
|
Assert.AreEqual(11, list.Count);
|
|
|
|
var sqlContext = GetRequiredService<ISqlContext>();
|
|
|
|
// filter
|
|
list = ContentService.GetPagedChildren(
|
|
Constants.System.Root,
|
|
0,
|
|
100,
|
|
out total,
|
|
sqlContext.Query<IContent>().Where(x => x.Name.Contains("contentX")),
|
|
Ordering.By("name", culture: langFr.IsoCode)).ToList();
|
|
|
|
Assert.AreEqual(0, total);
|
|
Assert.AreEqual(0, list.Count);
|
|
|
|
// filter
|
|
list = ContentService.GetPagedChildren(
|
|
Constants.System.Root,
|
|
0,
|
|
100,
|
|
out total,
|
|
sqlContext.Query<IContent>().Where(x => x.Name.Contains("contentX")),
|
|
Ordering.By("name", culture: langDa.IsoCode)).ToList();
|
|
|
|
Console.WriteLine("FILTER BY NAME da:'contentX'");
|
|
WriteList(list);
|
|
|
|
Assert.AreEqual(10, total);
|
|
Assert.AreEqual(10, list.Count);
|
|
|
|
// filter
|
|
list = ContentService.GetPagedChildren(
|
|
Constants.System.Root,
|
|
0,
|
|
100,
|
|
out total,
|
|
sqlContext.Query<IContent>().Where(x => x.Name.Contains("contentA")),
|
|
Ordering.By("name", culture: langFr.IsoCode)).ToList();
|
|
|
|
Console.WriteLine("FILTER BY NAME fr:'contentA', ORDER ASC");
|
|
WriteList(list);
|
|
|
|
Assert.AreEqual(5, total);
|
|
Assert.AreEqual(5, list.Count);
|
|
|
|
for (var i = 0; i < 5; i++)
|
|
{
|
|
Assert.AreEqual("contentA" + i + "fr", list[i].GetCultureName(langFr.IsoCode));
|
|
}
|
|
|
|
list = ContentService.GetPagedChildren(
|
|
Constants.System.Root,
|
|
0,
|
|
100,
|
|
out total,
|
|
sqlContext.Query<IContent>().Where(x => x.Name.Contains("contentA")),
|
|
Ordering.By("name", Direction.Descending, langFr.IsoCode)).ToList();
|
|
|
|
Console.WriteLine("FILTER BY NAME fr:'contentA', ORDER DESC");
|
|
WriteList(list);
|
|
|
|
Assert.AreEqual(5, total);
|
|
Assert.AreEqual(5, list.Count);
|
|
|
|
for (var i = 0; i < 5; i++)
|
|
{
|
|
Assert.AreEqual("contentA" + (4 - i) + "fr", list[i].GetCultureName(langFr.IsoCode));
|
|
}
|
|
}
|
|
|
|
private void WriteList(List<IContent> list)
|
|
{
|
|
foreach (var content in list)
|
|
{
|
|
Console.WriteLine("[{0}] {1} {2} {3} {4}", content.Id, content.Name, content.GetCultureName("en-GB"),
|
|
content.GetCultureName("fr-FR"), content.GetCultureName("da-DK"));
|
|
}
|
|
|
|
Console.WriteLine("-");
|
|
}
|
|
|
|
[Test]
|
|
[LongRunning]
|
|
public async Task Can_SaveRead_Variations()
|
|
{
|
|
var languageService = LanguageService;
|
|
var langPt = new LanguageBuilder()
|
|
.WithCultureInfo("pt-PT")
|
|
.WithIsDefault(true)
|
|
.Build();
|
|
var langFr = new LanguageBuilder()
|
|
.WithCultureInfo("fr-FR")
|
|
.Build();
|
|
var langUk = new LanguageBuilder()
|
|
.WithCultureInfo("en-GB")
|
|
.Build();
|
|
var langDe = new LanguageBuilder()
|
|
.WithCultureInfo("de-DE")
|
|
.Build();
|
|
|
|
await languageService.CreateAsync(langFr, Constants.Security.SuperUserKey);
|
|
await languageService.CreateAsync(langUk, Constants.Security.SuperUserKey);
|
|
await languageService.CreateAsync(langDe, Constants.Security.SuperUserKey);
|
|
|
|
var contentTypeService = ContentTypeService;
|
|
|
|
var contentType = ContentTypeService.Get("umbTextpage");
|
|
contentType.Variations = ContentVariation.Culture;
|
|
contentType.AddPropertyType(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextBox,
|
|
ValueStorageType.Nvarchar, "prop")
|
|
{ Variations = ContentVariation.Culture });
|
|
|
|
// TODO: add test w/ an invariant prop
|
|
ContentTypeService.Save(contentType);
|
|
|
|
var content = ContentService.Create("Home US", Constants.System.Root, "umbTextpage");
|
|
|
|
// creating content with a name but no culture - will set the invariant name
|
|
// but, because that content is variant, as soon as we save, we'll need to
|
|
// replace the invariant name with whatever we have in cultures - always
|
|
//
|
|
// in fact, that would throw, because there is no name
|
|
// ContentService.Save(content);
|
|
|
|
// Act
|
|
content.SetValue("author", "Barack Obama");
|
|
content.SetValue("prop", "value-fr1", langFr.IsoCode);
|
|
content.SetValue("prop", "value-uk1", langUk.IsoCode);
|
|
content.SetCultureName("name-fr", langFr.IsoCode); // and then we can save
|
|
content.SetCultureName("name-uk", langUk.IsoCode);
|
|
ContentService.Save(content);
|
|
|
|
// content has been saved,
|
|
// it has names, but no publishNames, and no published cultures
|
|
var content2 = ContentService.GetById(content.Id);
|
|
|
|
Assert.AreEqual("name-fr", content2.Name); // got the default culture name when saved
|
|
Assert.AreEqual("name-fr", content2.GetCultureName(langFr.IsoCode));
|
|
Assert.AreEqual("name-uk", content2.GetCultureName(langUk.IsoCode));
|
|
|
|
Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode));
|
|
Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode));
|
|
Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true));
|
|
Assert.IsNull(content2.GetValue("prop", langUk.IsoCode, published: true));
|
|
|
|
Assert.IsNull(content2.PublishName);
|
|
Assert.IsNull(content2.GetPublishName(langFr.IsoCode));
|
|
Assert.IsNull(content2.GetPublishName(langUk.IsoCode));
|
|
|
|
// only fr and uk have a name, and are available
|
|
AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
|
|
// nothing has been published yet
|
|
AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, false),
|
|
(langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, false),
|
|
(langDe, false));
|
|
|
|
// not published => must be edited, if available
|
|
AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false));
|
|
|
|
// Act
|
|
ContentService.Publish(content, new[] { langFr.IsoCode, langUk.IsoCode });
|
|
|
|
// both FR and UK have been published,
|
|
// and content has been published,
|
|
// it has names, publishNames, and published cultures
|
|
content2 = ContentService.GetById(content.Id);
|
|
|
|
Assert.AreEqual("name-fr", content2.Name); // got the default culture name when saved
|
|
Assert.AreEqual("name-fr", content2.GetCultureName(langFr.IsoCode));
|
|
Assert.AreEqual("name-uk", content2.GetCultureName(langUk.IsoCode));
|
|
|
|
// we haven't published InvariantNeutral, but a document cannot be published without an invariant name,
|
|
// so when we tried and published for the first time above the french culture, the french name was used
|
|
// to populate the invariant name
|
|
Assert.AreEqual("name-fr", content2.PublishName);
|
|
|
|
Assert.AreEqual("name-fr", content2.GetPublishName(langFr.IsoCode));
|
|
Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode));
|
|
|
|
Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode));
|
|
Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode));
|
|
Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode, published: true));
|
|
Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true));
|
|
|
|
// no change
|
|
AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
|
|
// fr and uk have been published now
|
|
AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
|
|
// fr and uk, published without changes, not edited
|
|
AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, false), (langUk, false),
|
|
(langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, false), (langUk, false),
|
|
(langDe, false));
|
|
|
|
AssertPerCulture(content, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langFr, false),
|
|
(langUk, false)); // DE would throw
|
|
AssertPerCulture(content2, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langFr, false),
|
|
(langUk, false)); // DE would throw
|
|
|
|
// note that content and content2 culture published dates might be slightly different due to roundtrip to database
|
|
|
|
// Act
|
|
ContentService.Publish(content, new []{ "*" });
|
|
|
|
// now it has publish name for invariant neutral
|
|
content2 = ContentService.GetById(content.Id);
|
|
|
|
Assert.AreEqual("name-fr", content2.PublishName);
|
|
|
|
content.SetCultureName("Home US2", null);
|
|
content.SetCultureName("name-fr2", langFr.IsoCode);
|
|
content.SetCultureName("name-uk2", langUk.IsoCode);
|
|
content.SetValue("author", "Barack Obama2");
|
|
content.SetValue("prop", "value-fr2", langFr.IsoCode);
|
|
content.SetValue("prop", "value-uk2", langUk.IsoCode);
|
|
ContentService.Save(content);
|
|
|
|
// content has been saved,
|
|
// it has updated names, unchanged publishNames, and published cultures
|
|
content2 = ContentService.GetById(content.Id);
|
|
|
|
Assert.AreEqual("name-fr2", content2.Name); // got the default culture name when saved
|
|
Assert.AreEqual("name-fr2", content2.GetCultureName(langFr.IsoCode));
|
|
Assert.AreEqual("name-uk2", content2.GetCultureName(langUk.IsoCode));
|
|
|
|
Assert.AreEqual("name-fr", content2.PublishName);
|
|
Assert.AreEqual("name-fr", content2.GetPublishName(langFr.IsoCode));
|
|
Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode));
|
|
|
|
Assert.AreEqual("Barack Obama2", content2.GetValue("author"));
|
|
Assert.AreEqual("Barack Obama", content2.GetValue("author", published: true));
|
|
|
|
Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode));
|
|
Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode));
|
|
Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode, published: true));
|
|
Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true));
|
|
|
|
// no change
|
|
AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
|
|
// no change
|
|
AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
|
|
// we have changed values so now fr and uk are edited
|
|
AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false));
|
|
|
|
AssertPerCulture(content, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langFr, false),
|
|
(langUk, false)); // DE would throw
|
|
AssertPerCulture(content2, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langFr, false),
|
|
(langUk, false)); // DE would throw
|
|
|
|
// Act
|
|
// cannot just 'save' since we are changing what's published!
|
|
ContentService.Unpublish(content, langFr.IsoCode);
|
|
|
|
// content has been published,
|
|
// the french culture is gone
|
|
// (only if french is not mandatory, else everything would be gone!)
|
|
content2 = ContentService.GetById(content.Id);
|
|
|
|
Assert.AreEqual("name-fr2", content2.Name); // got the default culture name when saved
|
|
Assert.AreEqual("name-fr2", content2.GetCultureName(langFr.IsoCode));
|
|
Assert.AreEqual("name-uk2", content2.GetCultureName(langUk.IsoCode));
|
|
|
|
Assert.AreEqual("name-fr2", content2.PublishName);
|
|
Assert.IsNull(content2.GetPublishName(langFr.IsoCode));
|
|
Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode));
|
|
|
|
Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode));
|
|
Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode));
|
|
Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true));
|
|
Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true));
|
|
|
|
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
|
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
|
|
|
// no change
|
|
AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
|
|
// fr is not published anymore
|
|
AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true),
|
|
(langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true),
|
|
(langDe, false));
|
|
|
|
// and so, fr has to be edited
|
|
AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false));
|
|
|
|
AssertPerCulture(content, (x, c) => x.GetPublishDate(c) == DateTime.MinValue,
|
|
(langUk, false)); // FR, DE would throw
|
|
AssertPerCulture(content2, (x, c) => x.GetPublishDate(c) == DateTime.MinValue,
|
|
(langUk, false)); // FR, DE would throw
|
|
|
|
// Act
|
|
ContentService.Unpublish(content);
|
|
|
|
// content has been unpublished,
|
|
// but properties, names, etc. retain their 'published' values so the content
|
|
// can be re-published in its exact original state (before being unpublished)
|
|
//
|
|
// BEWARE!
|
|
// in order for a content to be unpublished as a whole, and then republished in
|
|
// its exact previous state, properties and names etc. retain their published
|
|
// values even though the content is not published - hence many things being
|
|
// non-null or true below - always check against content.Published to be sure
|
|
content2 = ContentService.GetById(content.Id);
|
|
|
|
Assert.IsFalse(content2.Published);
|
|
|
|
Assert.AreEqual("name-fr2", content2.Name); // got the default culture name when saved
|
|
Assert.AreEqual("name-fr2", content2.GetCultureName(langFr.IsoCode));
|
|
Assert.AreEqual("name-uk2", content2.GetCultureName(langUk.IsoCode));
|
|
|
|
Assert.IsNull(content2.PublishName);
|
|
Assert.IsNull(content2.GetPublishName(langFr.IsoCode));
|
|
Assert.IsNull(content2.GetPublishName(langUk.IsoCode));
|
|
|
|
Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode));
|
|
Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode));
|
|
Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true));
|
|
Assert.AreEqual("value-uk1",
|
|
content2.GetValue("prop", langUk.IsoCode, published: true)); // has value, see note above
|
|
|
|
// no change
|
|
AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
|
|
// Everything should be unpublished
|
|
AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, false), (langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, false), (langDe, false));
|
|
|
|
// and so, fr has to be edited - uk still is
|
|
AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false));
|
|
|
|
AssertPerCulture(content, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw
|
|
AssertPerCulture(content2, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw
|
|
|
|
// Act
|
|
ContentService.Publish(content, new[] { langUk.IsoCode });
|
|
|
|
content2 = ContentService.GetById(content.Id);
|
|
|
|
// no change
|
|
AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true),
|
|
(langDe, false));
|
|
|
|
// no change
|
|
AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true),
|
|
(langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true),
|
|
(langDe, false));
|
|
|
|
// now, uk is no more edited
|
|
AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, false), (langDe, false));
|
|
AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, false),
|
|
(langDe, false));
|
|
|
|
AssertPerCulture(content, (x, c) => x.GetPublishDate(c) == DateTime.MinValue,
|
|
(langUk, false)); // FR, DE would throw
|
|
AssertPerCulture(content2, (x, c) => x.GetPublishDate(c) == DateTime.MinValue,
|
|
(langUk, false)); // FR, DE would throw
|
|
|
|
// Act
|
|
content.SetCultureName("name-uk3", langUk.IsoCode);
|
|
ContentService.Save(content);
|
|
|
|
content2 = ContentService.GetById(content.Id);
|
|
|
|
// note that the 'edited' flags only change once saved - not immediately
|
|
// but they change, on what's being saved, and when getting it back
|
|
|
|
// changing the name = edited!
|
|
Assert.IsTrue(content.IsCultureEdited(langUk.IsoCode));
|
|
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, content.PublishedCultures.First());
|
|
}
|
|
|
|
private void AssertPerCulture<T>(IContent item, Func<IContent, string, T> getter,
|
|
params (ILanguage Language, bool Result)[] testCases)
|
|
{
|
|
foreach (var testCase in testCases)
|
|
{
|
|
var value = getter(item, testCase.Language.IsoCode);
|
|
Assert.AreEqual(testCase.Result, value,
|
|
$"Expected {testCase.Result} and got {value} for culture {testCase.Language.IsoCode}.");
|
|
}
|
|
}
|
|
|
|
private IEnumerable<IContent> CreateContentHierarchy()
|
|
{
|
|
var contentType = ContentTypeService.Get("umbTextpage");
|
|
var root = ContentService.GetById(Textpage.Id);
|
|
|
|
var list = new List<IContent>();
|
|
|
|
for (var i = 0; i < 10; i++)
|
|
{
|
|
var content =
|
|
ContentBuilder.CreateSimpleContent(contentType, "Hierarchy Simple Text Page " + i, root);
|
|
|
|
list.Add(content);
|
|
list.AddRange(CreateChildrenOf(contentType, content, 4));
|
|
|
|
Debug.Print("Created: 'Hierarchy Simple Text Page {0}'", i);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
private IEnumerable<IContent> CreateChildrenOf(IContentType contentType, IContent content, int depth)
|
|
{
|
|
var list = new List<IContent>();
|
|
for (var i = 0; i < depth; i++)
|
|
{
|
|
var c = ContentBuilder.CreateSimpleContent(contentType, "Hierarchy Simple Text Subpage " + i,
|
|
content);
|
|
list.Add(c);
|
|
|
|
Debug.Print("Created: 'Hierarchy Simple Text Subpage {0}' - Depth: {1}", i, depth);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
private void CreateEnglishAndFrenchDocumentType(out Language langUk, out Language langFr,
|
|
out ContentType contentType)
|
|
{
|
|
langUk = (Language)new LanguageBuilder()
|
|
.WithCultureInfo("en-GB")
|
|
.WithIsDefault(true)
|
|
.Build();
|
|
langFr = (Language)new LanguageBuilder()
|
|
.WithCultureInfo("fr-FR")
|
|
.Build();
|
|
LanguageService.CreateAsync(langFr, Constants.Security.SuperUserKey).GetAwaiter().GetResult();
|
|
LanguageService.CreateAsync(langUk, Constants.Security.SuperUserKey).GetAwaiter().GetResult();
|
|
|
|
contentType = ContentTypeBuilder.CreateBasicContentType();
|
|
contentType.Variations = ContentVariation.Culture;
|
|
ContentTypeService.Save(contentType);
|
|
}
|
|
|
|
private IContent CreateEnglishAndFrenchDocument(out Language langUk, out Language langFr,
|
|
out ContentType contentType)
|
|
{
|
|
CreateEnglishAndFrenchDocumentType(out langUk, out langFr, out contentType);
|
|
|
|
IContent content = new Content("content", Constants.System.Root, contentType);
|
|
content.SetCultureName("content-fr", langFr.IsoCode);
|
|
content.SetCultureName("content-en", langUk.IsoCode);
|
|
|
|
return content;
|
|
}
|
|
|
|
public class ContentNotificationHandler :
|
|
INotificationHandler<ContentCopyingNotification>,
|
|
INotificationHandler<ContentCopiedNotification>,
|
|
INotificationHandler<ContentPublishingNotification>,
|
|
INotificationHandler<ContentSavingNotification>
|
|
{
|
|
public static Action<ContentPublishingNotification> PublishingContent { get; set; }
|
|
|
|
public static Action<ContentCopyingNotification> CopyingContent { get; set; }
|
|
|
|
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);
|
|
}
|
|
}
|