using NPoco; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Cms.Infrastructure.Scoping; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; internal class DocumentVersionRepository : IDocumentVersionRepository { private readonly IScopeAccessor _scopeAccessor; public DocumentVersionRepository(IScopeAccessor scopeAccessor) => _scopeAccessor = scopeAccessor ?? throw new ArgumentNullException(nameof(scopeAccessor)); /// /// /// Never includes current draft version.
/// Never includes current published version.
/// Never includes versions marked as "preventCleanup".
///
public IReadOnlyCollection? GetDocumentVersionsEligibleForCleanup() { Sql? query = _scopeAccessor.AmbientScope?.SqlContext.Sql(); query?.Select(@"umbracoDocument.nodeId as contentId, umbracoContent.contentTypeId as contentTypeId, umbracoContentVersion.id as versionId, umbracoContentVersion.userId as userId, umbracoContentVersion.versionDate as versionDate, umbracoDocumentVersion.published as currentPublishedVersion, umbracoContentVersion.[current] as currentDraftVersion, umbracoContentVersion.preventCleanup as preventCleanup, umbracoUser.userName as username") .From() .InnerJoin() .On(left => left.NodeId, right => right.NodeId) .InnerJoin() .On(left => left.NodeId, right => right.NodeId) .InnerJoin() .On(left => left.Id, right => right.Id) .LeftJoin() .On(left => left.Id, right => right.UserId) .Where(x => !x.Current) // Never delete current draft version .Where(x => !x.PreventCleanup) // Never delete "pinned" versions .Where(x => !x.Published); // Never delete published version return _scopeAccessor.AmbientScope?.Database.Fetch(query); } /// public IReadOnlyCollection? GetCleanupPolicies() { Sql? query = _scopeAccessor.AmbientScope?.SqlContext.Sql(); query?.Select() .From(); return _scopeAccessor.AmbientScope?.Database.Fetch(query); } /// public IEnumerable? GetPagedItemsByContentId(int contentId, long pageIndex, int pageSize, out long totalRecords, int? languageId = null) { Sql? query = _scopeAccessor.AmbientScope?.SqlContext.Sql(); query?.Select(@"umbracoDocument.nodeId as contentId, umbracoContent.contentTypeId as contentTypeId, umbracoContentVersion.id as versionId, umbracoContentVersion.userId as userId, umbracoContentVersion.versionDate as versionDate, umbracoDocumentVersion.published as currentPublishedVersion, umbracoContentVersion.[current] as currentDraftVersion, umbracoContentVersion.preventCleanup as preventCleanup, umbracoUser.userName as username") .From() .InnerJoin() .On(left => left.NodeId, right => right.NodeId) .InnerJoin() .On(left => left.NodeId, right => right.NodeId) .InnerJoin() .On(left => left.Id, right => right.Id) .LeftJoin() .On(left => left.Id, right => right.UserId) .LeftJoin() .On(left => left.VersionId, right => right.Id) .Where(x => x.NodeId == contentId); // TODO: If there's not a better way to write this then we need a better way to write this. query = languageId.HasValue ? query?.Where(x => x.LanguageId == languageId.Value) : query?.Where("umbracoContentVersionCultureVariation.languageId is null"); query = query?.OrderByDescending(x => x.Id); Page? page = _scopeAccessor.AmbientScope?.Database.Page(pageIndex + 1, pageSize, query); totalRecords = page?.TotalItems ?? 0; return page?.Items; } /// /// /// Deletes in batches of /// public void DeleteVersions(IEnumerable versionIds) { foreach (IEnumerable group in versionIds.InGroupsOf(Constants.Sql.MaxParameterCount)) { var groupedVersionIds = group.ToList(); /* Note: We had discussed doing this in a single SQL Command. * If you can work out how to make that work with SQL CE, let me know! * Can use test PerformContentVersionCleanup_WithNoKeepPeriods_DeletesEverythingExceptActive to try things out. */ Sql? query = _scopeAccessor.AmbientScope?.SqlContext.Sql() .Delete() .WhereIn(x => x.VersionId, groupedVersionIds); _scopeAccessor.AmbientScope?.Database.Execute(query); query = _scopeAccessor.AmbientScope?.SqlContext.Sql() .Delete() .WhereIn(x => x.VersionId, groupedVersionIds); _scopeAccessor.AmbientScope?.Database.Execute(query); query = _scopeAccessor.AmbientScope?.SqlContext.Sql() .Delete() .WhereIn(x => x.Id, groupedVersionIds); _scopeAccessor.AmbientScope?.Database.Execute(query); query = _scopeAccessor.AmbientScope?.SqlContext.Sql() .Delete() .WhereIn(x => x.Id, groupedVersionIds); _scopeAccessor.AmbientScope?.Database.Execute(query); } } /// public void SetPreventCleanup(int versionId, bool preventCleanup) { Sql? query = _scopeAccessor.AmbientScope?.SqlContext.Sql() .Update(x => x.Set(y => y.PreventCleanup, preventCleanup)) .Where(x => x.Id == versionId); _scopeAccessor.AmbientScope?.Database.Execute(query); } /// public ContentVersionMeta? Get(int versionId) { Sql? query = _scopeAccessor.AmbientScope?.SqlContext.Sql(); query?.Select(@"umbracoDocument.nodeId as contentId, umbracoContent.contentTypeId as contentTypeId, umbracoContentVersion.id as versionId, umbracoContentVersion.userId as userId, umbracoContentVersion.versionDate as versionDate, umbracoDocumentVersion.published as currentPublishedVersion, umbracoContentVersion.[current] as currentDraftVersion, umbracoContentVersion.preventCleanup as preventCleanup, umbracoUser.userName as username") .From() .InnerJoin() .On(left => left.NodeId, right => right.NodeId) .InnerJoin() .On(left => left.NodeId, right => right.NodeId) .InnerJoin() .On(left => left.Id, right => right.Id) .LeftJoin() .On(left => left.Id, right => right.UserId) .Where(x => x.Id == versionId); return _scopeAccessor.AmbientScope?.Database.Single(query); } }