Skip to content

Commit b0a4a92

Browse files
bergmaniaPaul Johnson
andauthored
ContentVersion cleanup backoffice UI (#11637) (#11644)
* ContentVersion cleanup backoffice UI (#11637) * init rollback ui prototype * add busy state to button, deselect version, add pagination status * add localisation * style current version * disable rollback button when nothing is selected * stop click event * Endpoints for paginated content versions. Light on tests, tight on time. * Endpoints to "pin" content versions * camel case json output. Not sure why json formatter not set for controller, bit risky to add it now * wire up paging * wire up pin/unpin * rename getPagedRollbackVersions to getPagedContentVersions * prevent selection of current version and current draft * add current draft and current version to UI * remove pointer if the row is not selectable * Improve warning for globally disabled cleanup feature. * Fix current loses prevent cleanup state on publish. * Added umbracoLog audit entries for "pin" / "unpin" * Match v9 defaults for keepVersions settings * Fix - losing preventCleanup on save current with content changes * update pin/unpin button labels * fix pagination bug * add missing " * always send culture when a doc type can vary Co-authored-by: Mads Rasmussen <[email protected]> # Conflicts: # src/Umbraco.Core/ContentEditing/ContentVersionMetaViewModel.cs # src/Umbraco.Core/Models/HistoricContentVersionMeta.cs # src/Umbraco.Infrastructure/Services/Implement/ContentService.cs # src/Umbraco.Tests/Persistence/Repositories/DocumentVersionRepository_Tests_Integration.cs # src/Umbraco.Tests/Services/ContentVersionCleanupService_Tests_UnitTests.cs # src/Umbraco.Web.BackOffice/Controllers/ContentController.cs # src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js # src/Umbraco.Web.UI/config/umbracoSettings.Release.config # src/Umbraco.Web.UI/umbraco/config/lang/en.xml # src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml # src/Umbraco.Web/Umbraco.Web.csproj * Added tests * Misc - missed translation update * Bugfix - DocumentVersionRepository.Get should not join culture variation * Bugfix - Missing write lock * Removed unnecessary view model * Misc - kill some warnings * Misc - Kill some more warnings * Fixed cypress rollback test * Bugfix - Policy returns items to delete not items to keep. Switch to inverse behavior. # Conflicts: # src/Umbraco.Tests/Services/DefaultContentVersionCleanupPolicy_Tests_UnitTests.cs Co-authored-by: Paul Johnson <[email protected]>
1 parent e2fdf43 commit b0a4a92

File tree

26 files changed

+906
-220
lines changed

26 files changed

+906
-220
lines changed

src/Umbraco.Core/Models/AuditType.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,16 @@ public enum AuditType
113113
/// <summary>
114114
/// Custom audit message.
115115
/// </summary>
116-
Custom
116+
Custom,
117+
118+
/// <summary>
119+
/// Content version preventCleanup set to true
120+
/// </summary>
121+
ContentVersionPreventCleanup,
122+
123+
/// <summary>
124+
/// Content version preventCleanup set to false
125+
/// </summary>
126+
ContentVersionEnableCleanup
117127
}
118128
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
3+
namespace Umbraco.Cms.Core.Models
4+
{
5+
public class ContentVersionMeta
6+
{
7+
public int ContentId { get; }
8+
public int ContentTypeId { get; }
9+
public int VersionId { get; }
10+
public int UserId { get; }
11+
12+
public DateTime VersionDate { get; }
13+
public bool CurrentPublishedVersion { get; }
14+
public bool CurrentDraftVersion { get; }
15+
public bool PreventCleanup { get; }
16+
public string Username { get; }
17+
18+
public ContentVersionMeta() { }
19+
20+
public ContentVersionMeta(
21+
int versionId,
22+
int contentId,
23+
int contentTypeId,
24+
int userId,
25+
DateTime versionDate,
26+
bool currentPublishedVersion,
27+
bool currentDraftVersion,
28+
bool preventCleanup,
29+
string username)
30+
{
31+
VersionId = versionId;
32+
ContentId = contentId;
33+
ContentTypeId = contentTypeId;
34+
35+
UserId = userId;
36+
VersionDate = versionDate;
37+
CurrentPublishedVersion = currentPublishedVersion;
38+
CurrentDraftVersion = currentDraftVersion;
39+
PreventCleanup = preventCleanup;
40+
Username = username;
41+
}
42+
43+
public override string ToString() => $"ContentVersionMeta(versionId: {VersionId}, versionDate: {VersionDate:s}";
44+
}
45+
}

src/Umbraco.Core/Models/HistoricContentVersionMeta.cs

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/Umbraco.Core/Persistence/Repositories/IDocumentVersionRepository.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,31 @@ public interface IDocumentVersionRepository : IRepository
88
/// <summary>
99
/// Gets a list of all historic content versions.
1010
/// </summary>
11-
public IReadOnlyCollection<HistoricContentVersionMeta> GetDocumentVersionsEligibleForCleanup();
11+
public IReadOnlyCollection<ContentVersionMeta> GetDocumentVersionsEligibleForCleanup();
1212

1313
/// <summary>
1414
/// Gets cleanup policy override settings per content type.
1515
/// </summary>
1616
public IReadOnlyCollection<ContentVersionCleanupPolicySettings> GetCleanupPolicies();
1717

18+
/// <summary>
19+
/// Gets paginated content versions for given content id paginated.
20+
/// </summary>
21+
public IEnumerable<ContentVersionMeta> GetPagedItemsByContentId(int contentId, long pageIndex, int pageSize, out long totalRecords, int? languageId = null);
22+
1823
/// <summary>
1924
/// Deletes multiple content versions by ID.
2025
/// </summary>
2126
void DeleteVersions(IEnumerable<int> versionIds);
27+
28+
/// <summary>
29+
/// Updates the prevent cleanup flag on a content version.
30+
/// </summary>
31+
void SetPreventCleanup(int versionId, bool preventCleanup);
32+
33+
/// <summary>
34+
/// Gets the content version metadata for a specific version.
35+
/// </summary>
36+
ContentVersionMeta Get(int versionId);
2237
}
2338
}

src/Umbraco.Core/Services/IContentVersionCleanupPolicy.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ public interface IContentVersionCleanupPolicy
1212
/// <summary>
1313
/// Filters a set of candidates historic content versions for cleanup according to policy settings.
1414
/// </summary>
15-
IEnumerable<HistoricContentVersionMeta> Apply(DateTime asAtDate, IEnumerable<HistoricContentVersionMeta> items);
15+
IEnumerable<ContentVersionMeta> Apply(DateTime asAtDate, IEnumerable<ContentVersionMeta> items);
1616
}
1717
}

src/Umbraco.Core/Services/IContentVersionService.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ public interface IContentVersionService
99
/// <summary>
1010
/// Removes historic content versions according to a policy.
1111
/// </summary>
12-
IReadOnlyCollection<HistoricContentVersionMeta> PerformContentVersionCleanup(DateTime asAtDate);
12+
IReadOnlyCollection<ContentVersionMeta> PerformContentVersionCleanup(DateTime asAtDate);
13+
14+
/// <summary>
15+
/// Gets paginated content versions for given content id paginated.
16+
/// </summary>
17+
/// <exception cref="ArgumentException">Thrown when <paramref name="culture"/> is invalid.</exception>
18+
IEnumerable<ContentVersionMeta> GetPagedContentVersions(int contentId, long pageIndex, int pageSize, out long totalRecords, string culture = null);
19+
20+
/// <summary>
21+
/// Updates preventCleanup value for given content version.
22+
/// </summary>
23+
void SetPreventCleanup(int versionId, bool preventCleanup, int userId = -1);
1324
}
1425
}

src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,10 @@ protected override void PersistUpdatedItem(IContent entity)
630630
documentVersionDto.Published = true; // now published
631631
contentVersionDto.Current = false; // no more current
632632
}
633+
634+
// Ensure existing version retains current preventCleanup flag (both saving and publishing).
635+
contentVersionDto.PreventCleanup = version.PreventCleanup;
636+
633637
Database.Update(contentVersionDto);
634638
Database.Update(documentVersionDto);
635639

@@ -641,6 +645,7 @@ protected override void PersistUpdatedItem(IContent entity)
641645
contentVersionDto.Id = 0; // want a new id
642646
contentVersionDto.Current = true; // current version
643647
contentVersionDto.Text = entity.Name;
648+
contentVersionDto.PreventCleanup = false; // new draft version disregards prevent cleanup flag
644649
Database.Insert(contentVersionDto);
645650
entity.VersionId = documentVersionDto.Id = contentVersionDto.Id; // get the new id
646651

src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentVersionRepository.cs

Lines changed: 99 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using NPoco;
45
using Umbraco.Cms.Core;
56
using Umbraco.Cms.Core.Models;
67
using Umbraco.Cms.Core.Persistence.Repositories;
@@ -14,65 +15,112 @@ internal class DocumentVersionRepository : IDocumentVersionRepository
1415
{
1516
private readonly IScopeAccessor _scopeAccessor;
1617

17-
public DocumentVersionRepository(IScopeAccessor scopeAccessor)
18-
{
18+
public DocumentVersionRepository(IScopeAccessor scopeAccessor) =>
1919
_scopeAccessor = scopeAccessor ?? throw new ArgumentNullException(nameof(scopeAccessor));
20-
}
2120

2221
/// <inheritdoc />
2322
/// <remarks>
2423
/// Never includes current draft version. <br/>
2524
/// Never includes current published version.<br/>
2625
/// Never includes versions marked as "preventCleanup".<br/>
2726
/// </remarks>
28-
public IReadOnlyCollection<HistoricContentVersionMeta> GetDocumentVersionsEligibleForCleanup()
27+
public IReadOnlyCollection<ContentVersionMeta> GetDocumentVersionsEligibleForCleanup()
2928
{
30-
var query = _scopeAccessor.AmbientScope.SqlContext.Sql();
29+
Sql<ISqlContext> query = _scopeAccessor.AmbientScope.SqlContext.Sql();
3130

3231
query.Select(@"umbracoDocument.nodeId as contentId,
3332
umbracoContent.contentTypeId as contentTypeId,
3433
umbracoContentVersion.id as versionId,
35-
umbracoContentVersion.versionDate as versionDate")
34+
umbracoContentVersion.userId as userId,
35+
umbracoContentVersion.versionDate as versionDate,
36+
umbracoDocumentVersion.published as currentPublishedVersion,
37+
umbracoContentVersion.[current] as currentDraftVersion,
38+
umbracoContentVersion.preventCleanup as preventCleanup,
39+
umbracoUser.userName as username")
3640
.From<DocumentDto>()
3741
.InnerJoin<ContentDto>()
3842
.On<DocumentDto, ContentDto>(left => left.NodeId, right => right.NodeId)
3943
.InnerJoin<ContentVersionDto>()
4044
.On<ContentDto, ContentVersionDto>(left => left.NodeId, right => right.NodeId)
4145
.InnerJoin<DocumentVersionDto>()
4246
.On<ContentVersionDto, DocumentVersionDto>(left => left.Id, right => right.Id)
47+
.LeftJoin<UserDto>()
48+
.On<UserDto, ContentVersionDto>(left => left.Id, right => right.UserId)
4349
.Where<ContentVersionDto>(x => !x.Current) // Never delete current draft version
4450
.Where<ContentVersionDto>(x => !x.PreventCleanup) // Never delete "pinned" versions
4551
.Where<DocumentVersionDto>(x => !x.Published); // Never delete published version
4652

47-
return _scopeAccessor.AmbientScope.Database.Fetch<HistoricContentVersionMeta>(query);
53+
return _scopeAccessor.AmbientScope.Database.Fetch<ContentVersionMeta>(query);
4854
}
4955

5056
/// <inheritdoc />
5157
public IReadOnlyCollection<ContentVersionCleanupPolicySettings> GetCleanupPolicies()
5258
{
53-
var query = _scopeAccessor.AmbientScope.SqlContext.Sql();
59+
Sql<ISqlContext> query = _scopeAccessor.AmbientScope.SqlContext.Sql();
5460

5561
query.Select<ContentVersionCleanupPolicyDto>()
5662
.From<ContentVersionCleanupPolicyDto>();
5763

5864
return _scopeAccessor.AmbientScope.Database.Fetch<ContentVersionCleanupPolicySettings>(query);
5965
}
6066

67+
/// <inheritdoc />
68+
public IEnumerable<ContentVersionMeta> GetPagedItemsByContentId(int contentId, long pageIndex, int pageSize, out long totalRecords, int? languageId = null)
69+
{
70+
Sql<ISqlContext> query = _scopeAccessor.AmbientScope.SqlContext.Sql();
71+
72+
query.Select(@"umbracoDocument.nodeId as contentId,
73+
umbracoContent.contentTypeId as contentTypeId,
74+
umbracoContentVersion.id as versionId,
75+
umbracoContentVersion.userId as userId,
76+
umbracoContentVersion.versionDate as versionDate,
77+
umbracoDocumentVersion.published as currentPublishedVersion,
78+
umbracoContentVersion.[current] as currentDraftVersion,
79+
umbracoContentVersion.preventCleanup as preventCleanup,
80+
umbracoUser.userName as username")
81+
.From<DocumentDto>()
82+
.InnerJoin<ContentDto>()
83+
.On<DocumentDto, ContentDto>(left => left.NodeId, right => right.NodeId)
84+
.InnerJoin<ContentVersionDto>()
85+
.On<ContentDto, ContentVersionDto>(left => left.NodeId, right => right.NodeId)
86+
.InnerJoin<DocumentVersionDto>()
87+
.On<ContentVersionDto, DocumentVersionDto>(left => left.Id, right => right.Id)
88+
.LeftJoin<UserDto>()
89+
.On<UserDto, ContentVersionDto>(left => left.Id, right => right.UserId)
90+
.LeftJoin<ContentVersionCultureVariationDto>()
91+
.On<ContentVersionCultureVariationDto, ContentVersionDto>(left => left.VersionId, right => right.Id)
92+
.Where<ContentVersionDto>(x => x.NodeId == contentId);
93+
94+
// TODO: If there's not a better way to write this then we need a better way to write this.
95+
query = languageId.HasValue
96+
? query.Where<ContentVersionCultureVariationDto>(x => x.LanguageId == languageId.Value)
97+
: query.Where("umbracoContentVersionCultureVariation.languageId is null");
98+
99+
query = query.OrderByDescending<ContentVersionDto>(x => x.Id);
100+
101+
Page<ContentVersionMeta> page = _scopeAccessor.AmbientScope.Database.Page<ContentVersionMeta>(pageIndex + 1, pageSize, query);
102+
103+
totalRecords = page.TotalItems;
104+
105+
return page.Items;
106+
}
107+
61108
/// <inheritdoc />
62109
/// <remarks>
63110
/// Deletes in batches of <see cref="Constants.Sql.MaxParameterCount"/>
64111
/// </remarks>
65112
public void DeleteVersions(IEnumerable<int> versionIds)
66113
{
67-
foreach (var group in versionIds.InGroupsOf(Constants.Sql.MaxParameterCount))
114+
foreach (IEnumerable<int> group in versionIds.InGroupsOf(Constants.Sql.MaxParameterCount))
68115
{
69116
var groupedVersionIds = group.ToList();
70117

71-
// Note: We had discussed doing this in a single SQL Command.
72-
// If you can work out how to make that work with SQL CE, let me know!
73-
// Can use test PerformContentVersionCleanup_WithNoKeepPeriods_DeletesEverythingExceptActive to try things out.
118+
/* Note: We had discussed doing this in a single SQL Command.
119+
* If you can work out how to make that work with SQL CE, let me know!
120+
* Can use test PerformContentVersionCleanup_WithNoKeepPeriods_DeletesEverythingExceptActive to try things out.
121+
*/
74122

75-
var query = _scopeAccessor.AmbientScope.SqlContext.Sql()
123+
Sql<ISqlContext> query = _scopeAccessor.AmbientScope.SqlContext.Sql()
76124
.Delete<PropertyDataDto>()
77125
.WhereIn<PropertyDataDto>(x => x.VersionId, groupedVersionIds);
78126
_scopeAccessor.AmbientScope.Database.Execute(query);
@@ -93,5 +141,43 @@ public void DeleteVersions(IEnumerable<int> versionIds)
93141
_scopeAccessor.AmbientScope.Database.Execute(query);
94142
}
95143
}
144+
145+
/// <inheritdoc />
146+
public void SetPreventCleanup(int versionId, bool preventCleanup)
147+
{
148+
Sql<ISqlContext> query = _scopeAccessor.AmbientScope.SqlContext.Sql()
149+
.Update<ContentVersionDto>(x => x.Set(y => y.PreventCleanup, preventCleanup))
150+
.Where<ContentVersionDto>(x => x.Id == versionId);
151+
152+
_scopeAccessor.AmbientScope.Database.Execute(query);
153+
}
154+
155+
/// <inheritdoc />
156+
public ContentVersionMeta Get(int versionId)
157+
{
158+
Sql<ISqlContext> query = _scopeAccessor.AmbientScope.SqlContext.Sql();
159+
160+
query.Select(@"umbracoDocument.nodeId as contentId,
161+
umbracoContent.contentTypeId as contentTypeId,
162+
umbracoContentVersion.id as versionId,
163+
umbracoContentVersion.userId as userId,
164+
umbracoContentVersion.versionDate as versionDate,
165+
umbracoDocumentVersion.published as currentPublishedVersion,
166+
umbracoContentVersion.[current] as currentDraftVersion,
167+
umbracoContentVersion.preventCleanup as preventCleanup,
168+
umbracoUser.userName as username")
169+
.From<DocumentDto>()
170+
.InnerJoin<ContentDto>()
171+
.On<DocumentDto, ContentDto>(left => left.NodeId, right => right.NodeId)
172+
.InnerJoin<ContentVersionDto>()
173+
.On<ContentDto, ContentVersionDto>(left => left.NodeId, right => right.NodeId)
174+
.InnerJoin<DocumentVersionDto>()
175+
.On<ContentVersionDto, DocumentVersionDto>(left => left.Id, right => right.Id)
176+
.LeftJoin<UserDto>()
177+
.On<UserDto, ContentVersionDto>(left => left.Id, right => right.UserId)
178+
.Where<ContentVersionDto>(x => x.Id == versionId);
179+
180+
return _scopeAccessor.AmbientScope.Database.Single<ContentVersionMeta>(query);
181+
}
96182
}
97183
}

0 commit comments

Comments
 (0)