Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Archive Later functionality (#10935)
- Loading branch information
Showing
20 changed files
with
436 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[ | ||
{ | ||
"generateSourceMaps": false, | ||
"inputs": [ | ||
"Assets/scss/archive-later.scss" | ||
], | ||
"output": "wwwroot/Styles/archive-later.css" | ||
} | ||
] |
3 changes: 3 additions & 0 deletions
3
src/OrchardCore.Modules/OrchardCore.ArchiveLater/Assets/scss/archive-later.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.btn-archive-later { | ||
white-space: nowrap; | ||
} |
73 changes: 73 additions & 0 deletions
73
src/OrchardCore.Modules/OrchardCore.ArchiveLater/Drivers/ArchiveLaterPartDisplayDriver.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
using System; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.AspNetCore.Http; | ||
using OrchardCore.ArchiveLater.Models; | ||
using OrchardCore.ArchiveLater.ViewModels; | ||
using OrchardCore.ContentManagement.Display.ContentDisplay; | ||
using OrchardCore.ContentManagement.Display.Models; | ||
using OrchardCore.Contents; | ||
using OrchardCore.DisplayManagement.ModelBinding; | ||
using OrchardCore.DisplayManagement.Views; | ||
using OrchardCore.Modules; | ||
|
||
namespace OrchardCore.ArchiveLater.Drivers; | ||
|
||
public class ArchiveLaterPartDisplayDriver : ContentPartDisplayDriver<ArchiveLaterPart> | ||
{ | ||
private readonly IHttpContextAccessor _httpContextAccessor; | ||
private readonly IAuthorizationService _authorizationService; | ||
private readonly ILocalClock _localClock; | ||
|
||
public ArchiveLaterPartDisplayDriver( | ||
IHttpContextAccessor httpContextAccessor, | ||
IAuthorizationService authorizationService, | ||
ILocalClock localClock) | ||
{ | ||
_httpContextAccessor = httpContextAccessor; | ||
_authorizationService = authorizationService; | ||
_localClock = localClock; | ||
} | ||
|
||
public override IDisplayResult Display(ArchiveLaterPart part, BuildPartDisplayContext context) | ||
=> Initialize<ArchiveLaterPartViewModel>( | ||
$"{nameof(ArchiveLaterPart)}_SummaryAdmin", | ||
model => PopulateViewModel(part, model)).Location("SummaryAdmin", "Meta:25"); | ||
|
||
public override IDisplayResult Edit(ArchiveLaterPart part, BuildPartEditorContext context) | ||
=> Initialize<ArchiveLaterPartViewModel>( | ||
GetEditorShapeType(context), | ||
model => PopulateViewModel(part, model)).Location("Actions:10"); | ||
|
||
public override async Task<IDisplayResult> UpdateAsync(ArchiveLaterPart part, IUpdateModel updater, UpdatePartEditorContext context) | ||
{ | ||
var httpContext = _httpContextAccessor.HttpContext; | ||
|
||
if (await _authorizationService.AuthorizeAsync(httpContext?.User, CommonPermissions.PublishContent, part.ContentItem)) | ||
{ | ||
var viewModel = new ArchiveLaterPartViewModel(); | ||
|
||
await updater.TryUpdateModelAsync(viewModel, Prefix); | ||
|
||
if (viewModel.ScheduledArchiveLocalDateTime == null || httpContext.Request.Form["submit.Save"] == "submit.CancelArchiveLater") | ||
{ | ||
part.ScheduledArchiveUtc = null; | ||
} | ||
else | ||
{ | ||
part.ScheduledArchiveUtc = await _localClock.ConvertToUtcAsync(viewModel.ScheduledArchiveLocalDateTime.Value); | ||
} | ||
} | ||
|
||
return Edit(part, context); | ||
} | ||
|
||
private async ValueTask PopulateViewModel(ArchiveLaterPart part, ArchiveLaterPartViewModel viewModel) | ||
{ | ||
viewModel.ContentItem = part.ContentItem; | ||
viewModel.ScheduledArchiveUtc = part.ScheduledArchiveUtc; | ||
viewModel.ScheduledArchiveLocalDateTime = part.ScheduledArchiveUtc.HasValue | ||
? (await _localClock.ConvertToLocalAsync(part.ScheduledArchiveUtc.Value)).DateTime | ||
: (DateTime?)null; | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
src/OrchardCore.Modules/OrchardCore.ArchiveLater/Indexes/ArchiveLaterPartIndex.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using System; | ||
using YesSql.Indexes; | ||
|
||
namespace OrchardCore.ArchiveLater.Indexes; | ||
|
||
public class ArchiveLaterPartIndex : MapIndex | ||
{ | ||
public string ContentItemId { get; set; } | ||
|
||
public DateTime? ScheduledArchiveDateTimeUtc { get; set; } | ||
|
||
public bool Published { get; set; } | ||
|
||
public bool Latest { get; set; } | ||
} |
78 changes: 78 additions & 0 deletions
78
src/OrchardCore.Modules/OrchardCore.ArchiveLater/Indexes/ArchiveLaterPartIndexProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using OrchardCore.ArchiveLater.Models; | ||
using OrchardCore.ContentManagement; | ||
using OrchardCore.ContentManagement.Handlers; | ||
using OrchardCore.ContentManagement.Metadata; | ||
using OrchardCore.Data; | ||
using YesSql.Indexes; | ||
|
||
namespace OrchardCore.ArchiveLater.Indexes; | ||
|
||
public class ArchiveLaterPartIndexProvider : ContentHandlerBase, IIndexProvider, IScopedIndexProvider | ||
{ | ||
private readonly IServiceProvider _serviceProvider; | ||
private readonly HashSet<string> _partRemoved = new(); | ||
private IContentDefinitionManager _contentDefinitionManager; | ||
|
||
public ArchiveLaterPartIndexProvider(IServiceProvider serviceProvider) | ||
{ | ||
_serviceProvider = serviceProvider; | ||
} | ||
|
||
public override Task UpdatedAsync(UpdateContentContext context) | ||
{ | ||
var part = context.ContentItem.As<ArchiveLaterPart>(); | ||
|
||
if (part != null) | ||
{ | ||
_contentDefinitionManager ??= _serviceProvider.GetRequiredService<IContentDefinitionManager>(); | ||
|
||
var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(context.ContentItem.ContentType); | ||
if (!contentTypeDefinition.Parts.Any(pd => pd.Name == nameof(ArchiveLaterPart))) | ||
{ | ||
context.ContentItem.Remove<ArchiveLaterPart>(); | ||
_partRemoved.Add(context.ContentItem.ContentItemId); | ||
} | ||
} | ||
|
||
return Task.CompletedTask; | ||
} | ||
|
||
public string CollectionName { get; set; } | ||
|
||
public Type ForType() => typeof(ContentItem); | ||
|
||
public void Describe(IDescriptor context) => Describe((DescribeContext<ContentItem>)context); | ||
|
||
public void Describe(DescribeContext<ContentItem> context) | ||
{ | ||
context | ||
.For<ArchiveLaterPartIndex>() | ||
.When(contentItem => contentItem.Has<ArchiveLaterPart>() || _partRemoved.Contains(contentItem.ContentItemId)) | ||
.Map(contentItem => | ||
{ | ||
if (!contentItem.Published || !contentItem.Latest) | ||
{ | ||
return null; | ||
} | ||
var part = contentItem.As<ArchiveLaterPart>(); | ||
if (part == null || !part.ScheduledArchiveUtc.HasValue) | ||
{ | ||
return null; | ||
} | ||
return new ArchiveLaterPartIndex | ||
{ | ||
ContentItemId = part.ContentItem.ContentItemId, | ||
ScheduledArchiveDateTimeUtc = part.ScheduledArchiveUtc, | ||
Published = part.ContentItem.Published, | ||
Latest = part.ContentItem.Latest | ||
}; | ||
}); | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
src/OrchardCore.Modules/OrchardCore.ArchiveLater/Manifest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using OrchardCore.Modules.Manifest; | ||
|
||
[assembly: Module( | ||
Name = "Archive Later", | ||
Author = ManifestConstants.OrchardCoreTeam, | ||
Website = ManifestConstants.OrchardCoreWebsite, | ||
Version = ManifestConstants.OrchardCoreVersion, | ||
Description = "The Archive Later module adds the ability to schedule content items to be archived at a given future date and time.", | ||
Dependencies = new[] { "OrchardCore.Contents" }, | ||
Category = "Content Management" | ||
)] |
46 changes: 46 additions & 0 deletions
46
src/OrchardCore.Modules/OrchardCore.ArchiveLater/Migrations.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
using System; | ||
using OrchardCore.ArchiveLater.Indexes; | ||
using OrchardCore.ArchiveLater.Models; | ||
using OrchardCore.ContentManagement.Metadata; | ||
using OrchardCore.ContentManagement.Metadata.Settings; | ||
using OrchardCore.ContentManagement.Records; | ||
using OrchardCore.Data.Migration; | ||
using YesSql.Sql; | ||
|
||
namespace OrchardCore.ArchiveLater; | ||
|
||
public class Migrations : DataMigration | ||
{ | ||
private readonly IContentDefinitionManager _contentDefinitionManager; | ||
|
||
public Migrations(IContentDefinitionManager contentDefinitionManager) | ||
{ | ||
_contentDefinitionManager = contentDefinitionManager; | ||
} | ||
|
||
public int Create() | ||
{ | ||
_contentDefinitionManager.AlterPartDefinition(nameof(ArchiveLaterPart), builder => builder | ||
.Attachable() | ||
.WithDescription("Adds the ability to schedule content items to be archived at a given future date and time.")); | ||
|
||
SchemaBuilder.CreateMapIndexTable<ArchiveLaterPartIndex>(table => table | ||
.Column<string>(nameof(ArchiveLaterPartIndex.ContentItemId)) | ||
.Column<DateTime>(nameof(ArchiveLaterPartIndex.ScheduledArchiveDateTimeUtc)) | ||
.Column<bool>(nameof(ArchiveLaterPartIndex.Published)) | ||
.Column<bool>(nameof(ArchiveLaterPartIndex.Latest)) | ||
); | ||
|
||
SchemaBuilder.AlterIndexTable<ArchiveLaterPartIndex>(table => table | ||
.CreateIndex($"IDX_{nameof(ArchiveLaterPartIndex)}_{nameof(ContentItemIndex.DocumentId)}", | ||
"Id", | ||
nameof(ContentItemIndex.DocumentId), | ||
nameof(ArchiveLaterPartIndex.ContentItemId), | ||
nameof(ArchiveLaterPartIndex.ScheduledArchiveDateTimeUtc), | ||
nameof(ArchiveLaterPartIndex.Published), | ||
nameof(ArchiveLaterPartIndex.Latest)) | ||
); | ||
|
||
return 1; | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
src/OrchardCore.Modules/OrchardCore.ArchiveLater/Models/ArchiveLaterPart.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using System; | ||
using OrchardCore.ContentManagement; | ||
|
||
namespace OrchardCore.ArchiveLater.Models; | ||
|
||
public class ArchiveLaterPart : ContentPart | ||
{ | ||
public DateTime? ScheduledArchiveUtc { get; set; } | ||
} |
28 changes: 28 additions & 0 deletions
28
src/OrchardCore.Modules/OrchardCore.ArchiveLater/OrchardCore.ArchiveLater.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.Razor"> | ||
|
||
<PropertyGroup> | ||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc> | ||
<!-- NuGet properties--> | ||
<Title>OrchardCore AchiveLater</Title> | ||
<Description>$(OCCMSDescription) | ||
|
||
The Archive Later module adds the ability to schedule content items to be archived at a given future date and time.</Description> | ||
<PackageTags>$(PackageTags) OrchardCoreCMS</PackageTags> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<FrameworkReference Include="Microsoft.AspNetCore.App" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\OrchardCore\OrchardCore.ContentManagement\OrchardCore.ContentManagement.csproj" /> | ||
<ProjectReference Include="..\..\OrchardCore\OrchardCore.ContentManagement.Display\OrchardCore.ContentManagement.Display.csproj" /> | ||
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Contents.Core\OrchardCore.Contents.Core.csproj" /> | ||
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Data.Abstractions\OrchardCore.Data.Abstractions.csproj" /> | ||
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Data.YesSql\OrchardCore.Data.YesSql.csproj" /> | ||
<ProjectReference Include="..\..\OrchardCore\OrchardCore.DisplayManagement\OrchardCore.DisplayManagement.csproj" /> | ||
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Module.Targets\OrchardCore.Module.Targets.csproj" /> | ||
<ProjectReference Include="..\..\OrchardCore\OrchardCore.ResourceManagement\OrchardCore.ResourceManagement.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
58 changes: 58 additions & 0 deletions
58
...OrchardCore.Modules/OrchardCore.ArchiveLater/Services/ScheduledArchivingBackgroundTask.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Logging; | ||
using OrchardCore.ArchiveLater.Indexes; | ||
using OrchardCore.ArchiveLater.Models; | ||
using OrchardCore.BackgroundTasks; | ||
using OrchardCore.ContentManagement; | ||
using OrchardCore.Entities; | ||
using OrchardCore.Modules; | ||
using YesSql; | ||
|
||
namespace OrchardCore.ArchiveLater.Services; | ||
|
||
[BackgroundTask(Schedule = "* * * * *", Description = "Archives content items when their scheduled archive date time arrives.")] | ||
public class ScheduledArchivingBackgroundTask : IBackgroundTask | ||
{ | ||
private readonly ILogger<ScheduledArchivingBackgroundTask> _logger; | ||
private readonly IClock _clock; | ||
|
||
public ScheduledArchivingBackgroundTask(ILogger<ScheduledArchivingBackgroundTask> logger, IClock clock) | ||
{ | ||
_logger = logger; | ||
_clock = clock; | ||
} | ||
|
||
public async Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) | ||
{ | ||
var itemsToArchive = await serviceProvider | ||
.GetRequiredService<ISession>() | ||
.QueryIndex<ArchiveLaterPartIndex>(index => index.Latest && index.Published && index.ScheduledArchiveDateTimeUtc < _clock.UtcNow) | ||
.ListAsync(); | ||
|
||
if (!itemsToArchive.Any()) | ||
{ | ||
return; | ||
} | ||
|
||
var contentManager = serviceProvider.GetRequiredService<IContentManager>(); | ||
|
||
foreach (var item in itemsToArchive) | ||
{ | ||
var contentItem = await contentManager.GetAsync(item.ContentItemId); | ||
|
||
var part = contentItem.As<ArchiveLaterPart>(); | ||
if (part != null) | ||
{ | ||
part.ScheduledArchiveUtc = null; | ||
} | ||
|
||
_logger.LogDebug("Archiving scheduled content item {ContentItemId}.", contentItem.ContentItemId); | ||
|
||
await contentManager.UnpublishAsync(contentItem); | ||
} | ||
} | ||
} |
Oops, something went wrong.