Skip to content

Commit 997a079

Browse files
committed
Adding collection items compatibility
1 parent d7e8540 commit 997a079

File tree

10 files changed

+156
-14
lines changed

10 files changed

+156
-14
lines changed

src/Umbraco.Cms.Api.Management/Controllers/Content/ContentCollectionControllerBase.cs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
using Microsoft.AspNetCore.Http;
22
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.Extensions.DependencyInjection;
34
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
5+
using Umbraco.Cms.Api.Management.Services.Signs;
46
using Umbraco.Cms.Api.Management.ViewModels.Content;
7+
using Umbraco.Cms.Api.Management.ViewModels.Document.Collection;
8+
using Umbraco.Cms.Core.DependencyInjection;
59
using Umbraco.Cms.Core.Mapping;
610
using Umbraco.Cms.Core.Models;
711
using Umbraco.Cms.Core.Models.ContentEditing;
@@ -11,15 +15,27 @@
1115

1216
namespace Umbraco.Cms.Api.Management.Controllers.Content;
1317

14-
public abstract class ContentCollectionControllerBase<TContent, TCollectionResponseModel, TValueResponseModelBase, TVariantResponseModel> : ManagementApiControllerBase
18+
public abstract class ContentCollectionControllerBase<TContent, TCollectionResponseModel, TValueResponseModelBase, TVariantResponseModel, TItem> : ManagementApiControllerBase
1519
where TContent : class, IContentBase
1620
where TCollectionResponseModel : ContentResponseModelBase<TValueResponseModelBase, TVariantResponseModel>
1721
where TValueResponseModelBase : ValueResponseModelBase
1822
where TVariantResponseModel : VariantResponseModelBase
23+
where TItem : DocumentCollectionResponseModel, new()
1924
{
2025
private readonly IUmbracoMapper _mapper;
26+
private readonly SignProviderCollection _signProviders;
2127

22-
protected ContentCollectionControllerBase(IUmbracoMapper mapper) => _mapper = mapper;
28+
protected ContentCollectionControllerBase(IUmbracoMapper mapper, SignProviderCollection signProvider)
29+
{
30+
_mapper = mapper;
31+
_signProviders = signProvider;
32+
}
33+
34+
[Obsolete("Use the constructer with all parameters. To be removed in Umbraco 18")]
35+
protected ContentCollectionControllerBase(IUmbracoMapper mapper)
36+
: this(mapper, StaticServiceProvider.Instance.GetRequiredService<SignProviderCollection>())
37+
{
38+
}
2339

2440
[Obsolete("This method is no longer used and will be removed in Umbraco 17.")]
2541
protected IActionResult CollectionResult(ListViewPagedModel<TContent> result)
@@ -50,6 +66,10 @@ protected IActionResult CollectionResult(ListViewPagedModel<TContent> result)
5066

5167
protected IActionResult CollectionResult(List<TCollectionResponseModel> collectionResponseModels, long totalNumberOfItems)
5268
{
69+
70+
TItem[] signTargets = collectionResponseModels.OfType<TItem>().ToArray();
71+
PopulateSigns(signTargets).GetAwaiter().GetResult();
72+
5373
var pageViewModel = new PagedViewModel<TCollectionResponseModel>
5474
{
5575
Items = collectionResponseModels,
@@ -104,4 +124,12 @@ protected IActionResult ContentCollectionOperationStatusResult(ContentCollection
104124
StatusCode = StatusCodes.Status500InternalServerError,
105125
},
106126
});
127+
128+
protected async Task PopulateSigns(TItem[] collectionViewModel)
129+
{
130+
foreach (ISignProvider signProvider in _signProviders.Where(x => x.CanProvideSigns<TItem>()))
131+
{
132+
await signProvider.PopulateCollectionSignsAsync(collectionViewModel);
133+
}
134+
}
107135
}

src/Umbraco.Cms.Api.Management/Controllers/Document/Collection/DocumentCollectionControllerBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace Umbraco.Cms.Api.Management.Controllers.Document.Collection;
1515
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Collection}/{Constants.UdiEntityType.Document}")]
1616
[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Document))]
1717
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocuments)]
18-
public abstract class DocumentCollectionControllerBase : ContentCollectionControllerBase<IContent, DocumentCollectionResponseModel, DocumentValueResponseModel, DocumentVariantResponseModel>
18+
public abstract class DocumentCollectionControllerBase : ContentCollectionControllerBase<IContent, DocumentCollectionResponseModel, DocumentValueResponseModel, DocumentVariantResponseModel, DocumentCollectionResponseModel>
1919
{
2020
protected DocumentCollectionControllerBase(IUmbracoMapper mapper)
2121
: base(mapper)

src/Umbraco.Cms.Api.Management/Controllers/Media/Collection/MediaCollectionControllerBase.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Microsoft.AspNetCore.Mvc;
33
using Umbraco.Cms.Api.Management.Controllers.Content;
44
using Umbraco.Cms.Api.Management.Routing;
5+
using Umbraco.Cms.Api.Management.ViewModels.Document.Collection;
56
using Umbraco.Cms.Api.Management.ViewModels.Media;
67
using Umbraco.Cms.Api.Management.ViewModels.Media.Collection;
78
using Umbraco.Cms.Core;
@@ -15,7 +16,7 @@ namespace Umbraco.Cms.Api.Management.Controllers.Media.Collection;
1516
[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Collection}/{Constants.UdiEntityType.Media}")]
1617
[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Media))]
1718
[Authorize(Policy = AuthorizationPolicies.SectionAccessMedia)]
18-
public abstract class MediaCollectionControllerBase : ContentCollectionControllerBase<IMedia, MediaCollectionResponseModel, MediaValueResponseModel, MediaVariantResponseModel>
19+
public abstract class MediaCollectionControllerBase : ContentCollectionControllerBase<IMedia, MediaCollectionResponseModel, MediaValueResponseModel, MediaVariantResponseModel, DocumentCollectionResponseModel>
1920
{
2021
protected MediaCollectionControllerBase(IUmbracoMapper mapper)
2122
: base(mapper)

src/Umbraco.Cms.Api.Management/Controllers/Tree/EntityTreeControllerBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ protected virtual TItem[] MapTreeItemViewModels(Guid? parentKey, IEntitySlim[] e
164164

165165
protected virtual async Task PopulateSigns(TItem[] treeItemViewModels, IEnumerable<IEntitySlim> entities)
166166
{
167-
foreach (ISignProvider signProvider in _signProviders.Where(x => x.CanProvideTreeSigns<TItem>()))
167+
foreach (ISignProvider signProvider in _signProviders.Where(x => x.CanProvideSigns<TItem>()))
168168
{
169169
await signProvider.PopulateTreeSignsAsync(treeItemViewModels, entities);
170170
}

src/Umbraco.Cms.Api.Management/Services/Signs/HasScheduleSignProvider.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Serilog.Core;
2+
using Umbraco.Cms.Api.Management.ViewModels.Document.Collection;
23
using Umbraco.Cms.Api.Management.ViewModels.Tree;
34
using Umbraco.Cms.Core.Models.Entities;
45
using Umbraco.Cms.Core.Services;
@@ -21,7 +22,10 @@ internal class HasScheduleSignProvider : ISignProvider
2122
public HasScheduleSignProvider(IContentService contentService) => _contentService = contentService;
2223

2324
/// <inheritdoc/>
24-
public bool CanProvideTreeSigns<TItem>() => typeof(TItem) == typeof(DocumentTreeItemResponseModel);
25+
public bool CanProvideSigns<TItem>() =>
26+
typeof(TItem) == typeof(DocumentTreeItemResponseModel) ||
27+
typeof(TItem) == typeof(DocumentCollectionResponseModel);
28+
2529

2630
/// <inheritdoc/>
2731
public Task PopulateTreeSignsAsync<TItem>(TItem[] treeItemViewModels, IEnumerable<IEntitySlim> entities)
@@ -35,4 +39,17 @@ public Task PopulateTreeSignsAsync<TItem>(TItem[] treeItemViewModels, IEnumerabl
3539

3640
return Task.CompletedTask;
3741
}
42+
43+
/// <inheritdoc/>
44+
public Task PopulateCollectionSignsAsync<TItem>(TItem[] collectionItemViewModel)
45+
where TItem : DocumentCollectionResponseModel, new()
46+
{
47+
IEnumerable<Guid> contentKeysScheduledForPublishing = _contentService.GetScheduledContentKeys(collectionItemViewModel.Select(x => x.Id));
48+
foreach (Guid key in contentKeysScheduledForPublishing)
49+
{
50+
collectionItemViewModel.First(x => x.Id == key).AddSign(Alias);
51+
}
52+
53+
return Task.CompletedTask;
54+
}
3855
}

src/Umbraco.Cms.Api.Management/Services/Signs/ISignProvider.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using Umbraco.Cms.Api.Management.ViewModels.Document.Collection;
12
using Umbraco.Cms.Api.Management.ViewModels.Tree;
23
using Umbraco.Cms.Core.Models.Entities;
34

@@ -12,8 +13,7 @@ public interface ISignProvider
1213
/// Gets a value indicating whether this provider can provide tree signs for the specified item type.
1314
/// </summary>
1415
/// <typeparam name="TItem">Type of tree item view model.</typeparam>
15-
/// <returns></returns>
16-
bool CanProvideTreeSigns<TItem>();
16+
bool CanProvideSigns<TItem>();
1717

1818
/// <summary>
1919
/// Populates the provided tree item view models with signs.
@@ -23,4 +23,12 @@ public interface ISignProvider
2323
/// <param name="entities">The entities from which the collection of tree item view models was populated.</param>
2424
Task PopulateTreeSignsAsync<TItem>(TItem[] treeItemViewModels, IEnumerable<IEntitySlim> entities)
2525
where TItem : EntityTreeItemResponseModel, new();
26+
27+
/// <summary>
28+
/// Populates the provided collection item view models with signs.
29+
/// </summary>
30+
/// <typeparam name="TItem">Type of collection item view model.</typeparam>
31+
/// <param name="collectionItemViewModel">The collection of collection item view models populated with signs.</param>
32+
Task PopulateCollectionSignsAsync<TItem>(TItem[] collectionItemViewModel)
33+
where TItem : DocumentCollectionResponseModel, new();
2634
}

src/Umbraco.Cms.Api.Management/Services/Signs/IsProtectedSignProvider.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Umbraco.Cms.Api.Management.ViewModels.Tree;
1+
using Umbraco.Cms.Api.Management.ViewModels.Document.Collection;
2+
using Umbraco.Cms.Api.Management.ViewModels.Tree;
23
using Umbraco.Cms.Core;
34
using Umbraco.Cms.Core.Models.Entities;
45

@@ -9,7 +10,9 @@ internal class IsProtectedSignProvider : ISignProvider
910
private const string Alias = Constants.Conventions.Signs.Prefix + "IsProtected";
1011

1112
/// <inheritdoc/>>
12-
public bool CanProvideTreeSigns<TItem>() => typeof(TItem) == typeof(DocumentTreeItemResponseModel);
13+
public bool CanProvideSigns<TItem>() =>
14+
typeof(TItem) == typeof(DocumentTreeItemResponseModel) ||
15+
typeof(TItem) == typeof(DocumentCollectionResponseModel);
1316

1417
/// <inheritdoc/>>
1518
public Task PopulateTreeSignsAsync<TItem>(TItem[] treeItemViewModels, IEnumerable<IEntitySlim> entities)
@@ -25,4 +28,19 @@ public Task PopulateTreeSignsAsync<TItem>(TItem[] treeItemViewModels, IEnumerabl
2528

2629
return Task.CompletedTask;
2730
}
31+
32+
/// <inheritdoc/>
33+
public Task PopulateCollectionSignsAsync<TItem>(TItem[] collectionItemViewModel)
34+
where TItem : DocumentCollectionResponseModel, new()
35+
{
36+
foreach (TItem item in collectionItemViewModel)
37+
{
38+
if (item is DocumentCollectionResponseModel { IsProtected: true })
39+
{
40+
item.AddSign(Alias);
41+
}
42+
}
43+
44+
return Task.CompletedTask;
45+
}
2846
}

src/Umbraco.Cms.Api.Management/ViewModels/Document/Collection/DocumentCollectionResponseModel.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace Umbraco.Cms.Api.Management.ViewModels.Document.Collection;
55

66
public class DocumentCollectionResponseModel : ContentCollectionResponseModelBase<DocumentValueResponseModel, DocumentVariantResponseModel>
77
{
8+
private readonly List<SignModel> _signs = [];
9+
810
public DocumentTypeCollectionReferenceResponseModel DocumentType { get; set; } = new();
911

1012
public bool IsTrashed { get; set; }
@@ -14,4 +16,10 @@ public class DocumentCollectionResponseModel : ContentCollectionResponseModelBas
1416
public IEnumerable<ReferenceByIdModel> Ancestors { get; set; } = [];
1517

1618
public string? Updater { get; set; }
19+
20+
public IEnumerable<SignModel> Signs => _signs.AsEnumerable();
21+
22+
public void AddSign(string alias) => _signs.Add(new SignModel { Alias = alias });
23+
24+
public void RemoveSign(string alias) => _signs.RemoveAll(x => x.Alias == alias);
1725
}

tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Management/Services/Signs/HasScheduleSignProviderTests.cs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Moq;
22
using NUnit.Framework;
33
using Umbraco.Cms.Api.Management.Services.Signs;
4+
using Umbraco.Cms.Api.Management.ViewModels.Document.Collection;
45
using Umbraco.Cms.Api.Management.ViewModels.Tree;
56
using Umbraco.Cms.Core.Models.Entities;
67
using Umbraco.Cms.Core.Services;
@@ -11,7 +12,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Cms.Api.Management.Services.Signs;
1112
internal class HasScheduleSignProviderTests
1213
{
1314
[Test]
14-
public async Task HasScheduleSignProvider_Should_Populate_Signs()
15+
public async Task HasScheduleSignProvider_Should_Populate_Tree_Signs()
1516
{
1617
var entities = new List<EntitySlim>
1718
{
@@ -25,7 +26,7 @@ public async Task HasScheduleSignProvider_Should_Populate_Signs()
2526
.Returns([entities[1].Key]);
2627
var sut = new HasScheduleSignProvider(contentServiceMock.Object);
2728

28-
Assert.IsTrue(sut.CanProvideTreeSigns<DocumentTreeItemResponseModel>());
29+
Assert.IsTrue(sut.CanProvideSigns<DocumentTreeItemResponseModel>());
2930

3031
var viewModels = new List<DocumentTreeItemResponseModel>
3132
{
@@ -41,4 +42,36 @@ public async Task HasScheduleSignProvider_Should_Populate_Signs()
4142
var signModel = viewModels[1].Signs.First();
4243
Assert.AreEqual("Umb.ScheduledForPublish", signModel.Alias);
4344
}
45+
46+
[Test]
47+
public async Task HasScheduleSignProvider_Should_Populate_Collection_Signs()
48+
{
49+
var entities = new List<EntitySlim>
50+
{
51+
new() { Key = Guid.NewGuid(), Name = "Item 1" },
52+
new() { Key = Guid.NewGuid(), Name = "Item 2" },
53+
};
54+
55+
var contentServiceMock = new Mock<IContentService>();
56+
contentServiceMock
57+
.Setup(x => x.GetScheduledContentKeys(It.IsAny<IEnumerable<Guid>>()))
58+
.Returns([entities[1].Key]);
59+
var sut = new HasScheduleSignProvider(contentServiceMock.Object);
60+
61+
Assert.IsTrue(sut.CanProvideSigns<DocumentCollectionResponseModel>());
62+
63+
var viewModels = new List<DocumentCollectionResponseModel>
64+
{
65+
new() { Id = entities[0].Key },
66+
new() { Id = entities[1].Key },
67+
};
68+
69+
await sut.PopulateCollectionSignsAsync(viewModels.ToArray());
70+
71+
Assert.AreEqual(viewModels[0].Signs.Count(), 0);
72+
Assert.AreEqual(viewModels[1].Signs.Count(), 1);
73+
74+
var signModel = viewModels[1].Signs.First();
75+
Assert.AreEqual("Umb.ScheduledForPublish", signModel.Alias);
76+
}
4477
}

tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Management/Services/Signs/IsProtectedSignProviderTests.cs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using NUnit.Framework;
22
using Umbraco.Cms.Api.Management.Services.Signs;
3+
using Umbraco.Cms.Api.Management.ViewModels.Document.Collection;
34
using Umbraco.Cms.Api.Management.ViewModels.Tree;
45
using Umbraco.Cms.Core.Models.Entities;
56

@@ -9,7 +10,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Cms.Api.Management.Services.Signs;
910
internal class IsProtectedSignProviderTests
1011
{
1112
[Test]
12-
public async Task IsProtectedSignProvider_Should_Populate_Signs()
13+
public async Task IsProtectedSignProvider_Should_Populate_Tree_Signs()
1314
{
1415
var entities = new List<EntitySlim>
1516
{
@@ -19,7 +20,7 @@ public async Task IsProtectedSignProvider_Should_Populate_Signs()
1920

2021
var sut = new IsProtectedSignProvider();
2122

22-
Assert.IsTrue(sut.CanProvideTreeSigns<DocumentTreeItemResponseModel>());
23+
Assert.IsTrue(sut.CanProvideSigns<DocumentTreeItemResponseModel>());
2324

2425
var viewModels = new List<DocumentTreeItemResponseModel>
2526
{
@@ -35,4 +36,32 @@ public async Task IsProtectedSignProvider_Should_Populate_Signs()
3536
var signModel = viewModels[1].Signs.First();
3637
Assert.AreEqual("Umb.IsProtected", signModel.Alias);
3738
}
39+
40+
[Test]
41+
public async Task IsProtectedSignProvider_Should_Populate_Collection_Signs()
42+
{
43+
var entities = new List<EntitySlim>
44+
{
45+
new() { Name = "Item 1" },
46+
new() { Name = "Item 2" },
47+
};
48+
49+
var sut = new IsProtectedSignProvider();
50+
51+
Assert.IsTrue(sut.CanProvideSigns<DocumentCollectionResponseModel>());
52+
53+
var viewModels = new List<DocumentCollectionResponseModel>
54+
{
55+
new(),
56+
new() { IsProtected = true },
57+
};
58+
59+
await sut.PopulateCollectionSignsAsync(viewModels.ToArray());
60+
61+
Assert.AreEqual(viewModels[0].Signs.Count(), 0);
62+
Assert.AreEqual(viewModels[1].Signs.Count(), 1);
63+
64+
var signModel = viewModels[1].Signs.First();
65+
Assert.AreEqual("Umb.IsProtected", signModel.Alias);
66+
}
3867
}

0 commit comments

Comments
 (0)