mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-09-19 10:07:55 +08:00
Fix scheduled publishing of pages
Data model is now correctly updated when publishing, creating drafts, scheduling publishing, etc. There is no code left in the pages module which directly sets the CommonAspect dates. All is properly done through the CommonAspectHandler, the ContentManager and the PublishingTaskManager. There is a remaining issue with the background scheduler which is unable to run scheduled tasks (exception thrown by autofac). This will be investigated later. --HG-- extra : convert_revision : svn%3A5ff7c347-ad56-4c35-b696-ccb81de16e03/trunk%4045944
This commit is contained in:
@@ -105,6 +105,7 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Scheduling\Records\ScheduledTaskRecord.cs" />
|
||||
<Compile Include="Scheduling\Services\PublishingTaskHandler.cs" />
|
||||
<Compile Include="Scheduling\Services\PublishingTaskManager.cs" />
|
||||
<Compile Include="Scheduling\Services\ScheduledTaskManager.cs" />
|
||||
<Compile Include="Scheduling\Services\ScheduledTaskExecutor.cs" />
|
||||
<Compile Include="Scheduling\Models\Task.cs" />
|
||||
|
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Core.Scheduling.Records;
|
||||
using Orchard.Tasks.Scheduling;
|
||||
|
||||
namespace Orchard.Core.Scheduling.Models {
|
||||
public class Task : IScheduledTask {
|
||||
|
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Tasks.Scheduling;
|
||||
|
||||
namespace Orchard.Core.Scheduling.Services {
|
||||
public class PublishingTaskManager : IPublishingTaskManager {
|
||||
private const string PublishTaskType = "Publish";
|
||||
private const string UnpublishTaskType = "Unpublish";
|
||||
|
||||
private readonly IScheduledTaskManager _scheduledTaskManager;
|
||||
|
||||
public PublishingTaskManager(IScheduledTaskManager scheduledTaskManager) {
|
||||
_scheduledTaskManager = scheduledTaskManager;
|
||||
}
|
||||
|
||||
public IScheduledTask GetPublishTask(ContentItem item) {
|
||||
return _scheduledTaskManager
|
||||
.GetTasks(item)
|
||||
.Where(task => task.TaskType == PublishTaskType)
|
||||
.SingleOrDefault();
|
||||
}
|
||||
|
||||
public void Publish(ContentItem item, DateTime scheduledUtc) {
|
||||
DeleteTasks(item);
|
||||
_scheduledTaskManager.CreateTask(PublishTaskType, scheduledUtc, item);
|
||||
}
|
||||
|
||||
public void DeleteTasks(ContentItem item) {
|
||||
_scheduledTaskManager.DeleteTasks(item, task => task.TaskType == PublishTaskType || task.TaskType == UnpublishTaskType);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,10 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Core.Scheduling.Models;
|
||||
using Orchard.Core.Scheduling.Records;
|
||||
using Orchard.Data;
|
||||
@@ -13,7 +10,6 @@ using Orchard.Tasks.Scheduling;
|
||||
using Orchard.Utility;
|
||||
|
||||
namespace Orchard.Core.Scheduling.Services {
|
||||
[UsedImplicitly]
|
||||
public class ScheduledTaskManager : IScheduledTaskManager {
|
||||
private readonly IRepository<ScheduledTaskRecord> _repository;
|
||||
|
||||
@@ -46,5 +42,17 @@ namespace Orchard.Core.Scheduling.Services {
|
||||
.Cast<IScheduledTask>()
|
||||
.ToReadOnlyCollection();
|
||||
}
|
||||
|
||||
public void DeleteTasks(ContentItem contentItem, Func<IScheduledTask, bool> predicate) {
|
||||
//TEMP: Is this thread safe? Does it matter?
|
||||
var tasks = _repository
|
||||
.Fetch(x => x.ContentItemVersionRecord.ContentItemRecord == contentItem.Record);
|
||||
|
||||
foreach (var task in tasks) {
|
||||
if (predicate(new Task(Services.ContentManager, task))) {
|
||||
_repository.Delete(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -119,25 +119,10 @@ namespace Orchard.Pages.Controllers {
|
||||
if (!Services.Authorizer.Authorize(Permissions.EditPages, T("Couldn't create page")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
//TODO: (erikpo) Move this duplicate code somewhere else
|
||||
DateTime? publishDate = null;
|
||||
bool publishNow = false;
|
||||
if (string.Equals(Request.Form["Command"], "PublishNow")) {
|
||||
publishNow = true;
|
||||
}
|
||||
else if (string.Equals(Request.Form["Command"], "PublishLater")) {
|
||||
DateTime publishDateValue;
|
||||
if (DateTime.TryParse(Request.Form["Published"], out publishDateValue)) {
|
||||
publishDate = publishDateValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate form input
|
||||
var page = Services.ContentManager.New<Page>("page");
|
||||
model.Page = Services.ContentManager.UpdateEditorModel(page, this);
|
||||
|
||||
if (!publishNow && publishDate != null)
|
||||
model.Page.Item.Published = publishDate.Value;
|
||||
|
||||
if (!ModelState.IsValid) {
|
||||
Services.TransactionManager.Cancel();
|
||||
return View(model);
|
||||
@@ -145,15 +130,20 @@ namespace Orchard.Pages.Controllers {
|
||||
|
||||
Services.ContentManager.Create(model.Page.Item.ContentItem, VersionOptions.Draft);
|
||||
|
||||
if (publishNow)
|
||||
Services.ContentManager.Publish(model.Page.Item.ContentItem);
|
||||
|
||||
if (publishNow)
|
||||
Services.Notifier.Information(T("Page has been published"));
|
||||
else if (publishDate != null)
|
||||
Services.Notifier.Information(T("Page has been scheduled for publishing"));
|
||||
else
|
||||
Services.Notifier.Information(T("Page draft has been saved"));
|
||||
// Execute publish command
|
||||
switch (Request.Form["Command"]) {
|
||||
case "PublishNow":
|
||||
_pageService.Publish(model.Page.Item);
|
||||
Services.Notifier.Information(T("Page has been published"));
|
||||
break;
|
||||
case "PublishLater":
|
||||
_pageService.Publish(model.Page.Item, model.Page.Item.ScheduledPublishUtc.Value);
|
||||
Services.Notifier.Information(T("Page has been scheduled for publishing"));
|
||||
break;
|
||||
default:
|
||||
Services.Notifier.Information(T("Page draft has been saved"));
|
||||
break;
|
||||
}
|
||||
|
||||
return RedirectToAction("Edit", "Admin", new { id = model.Page.Item.ContentItem.Id });
|
||||
}
|
||||
@@ -191,37 +181,24 @@ namespace Orchard.Pages.Controllers {
|
||||
|
||||
if (!ModelState.IsValid) {
|
||||
Services.TransactionManager.Cancel();
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//TODO: (erikpo) Move this duplicate code somewhere else
|
||||
DateTime? publishDate = null;
|
||||
bool publishNow = false;
|
||||
if (string.Equals(Request.Form["Command"], "PublishNow")) {
|
||||
publishNow = true;
|
||||
// Execute publish command
|
||||
switch (Request.Form["Command"]) {
|
||||
case "PublishNow":
|
||||
_pageService.Publish(model.Page.Item);
|
||||
Services.Notifier.Information(T("Page has been published"));
|
||||
break;
|
||||
case "PublishLater":
|
||||
_pageService.Publish(model.Page.Item, model.Page.Item.ScheduledPublishUtc.Value);
|
||||
Services.Notifier.Information(T("Page has been scheduled for publishing"));
|
||||
break;
|
||||
default:
|
||||
Services.Notifier.Information(T("Page draft has been saved"));
|
||||
_pageService.Unpublish(page);
|
||||
break;
|
||||
}
|
||||
else if (string.Equals(Request.Form["Command"], "PublishLater")) {
|
||||
DateTime publishDateValue;
|
||||
if (DateTime.TryParse(Request.Form["Published"], out publishDateValue)) {
|
||||
publishDate = publishDateValue;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: (erikpo) Move this duplicate code somewhere else
|
||||
if (publishNow)
|
||||
_pageService.Publish(page);
|
||||
else if (publishDate != null)
|
||||
_pageService.Publish(page, publishDate.Value);
|
||||
else
|
||||
_pageService.Unpublish(page);
|
||||
|
||||
if (publishNow)
|
||||
Services.Notifier.Information(T("Page has been published"));
|
||||
else if (publishDate != null)
|
||||
Services.Notifier.Information(T("Page has been scheduled for publishing"));
|
||||
else
|
||||
Services.Notifier.Information(T("Page draft has been saved"));
|
||||
|
||||
return RedirectToAction("Edit", "Admin", new { id = model.Page.Item.ContentItem.Id });
|
||||
}
|
||||
|
@@ -2,7 +2,6 @@
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web.Routing;
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Common.Services;
|
||||
using Orchard.Localization;
|
||||
@@ -13,7 +12,6 @@ using Orchard.Pages.Services;
|
||||
using Orchard.UI.Notify;
|
||||
|
||||
namespace Orchard.Pages.Controllers {
|
||||
[UsedImplicitly]
|
||||
public class PageDriver : ContentItemDriver<Page> {
|
||||
private readonly IPageService _pageService;
|
||||
private readonly IRoutableService _routableService;
|
||||
|
@@ -57,12 +57,6 @@ namespace Orchard.Pages.Models {
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime? Published {
|
||||
get { return this.As<CommonAspect>().PublishedUtc; }
|
||||
set { this.As<CommonAspect>().PublishedUtc = value; }
|
||||
}
|
||||
|
||||
//[CascadeAllDeleteOrphan]
|
||||
//public virtual IList<Scheduled> Scheduled { get; protected set; }
|
||||
public DateTime? ScheduledPublishUtc { get; set;}
|
||||
}
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@ namespace Orchard.Pages.Models {
|
||||
Filters.Add(new ActivatingFilter<RoutableAspect>(PageDriver.ContentType.Name));
|
||||
Filters.Add(new ActivatingFilter<BodyAspect>(PageDriver.ContentType.Name));
|
||||
|
||||
OnLoaded<Page>((context, p) => p.ScheduledPublishUtc = _pageService.GetScheduledPublishUtc(p));
|
||||
OnPublished<Page>((context, p) => ProcessSlug(p));
|
||||
}
|
||||
|
||||
|
@@ -14,8 +14,9 @@ namespace Orchard.Pages.Services {
|
||||
Page GetLatest(int id);
|
||||
void Delete(Page page);
|
||||
void Publish(Page page);
|
||||
void Publish(Page page, DateTime publishDate);
|
||||
void Publish(Page page, DateTime scheduledPublishUtc);
|
||||
void Unpublish(Page page);
|
||||
DateTime? GetScheduledPublishUtc(Page page);
|
||||
}
|
||||
|
||||
public enum PageStatus {
|
||||
|
@@ -5,15 +5,16 @@ using Orchard.Pages.Models;
|
||||
using Orchard.Core.Common.Records;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Services;
|
||||
using Orchard.Tasks.Scheduling;
|
||||
|
||||
namespace Orchard.Pages.Services {
|
||||
public class PageService : IPageService {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IClock _clock;
|
||||
private readonly IPublishingTaskManager _publishingTaskManager;
|
||||
|
||||
public PageService(IContentManager contentManager, IClock clock) {
|
||||
public PageService(IContentManager contentManager, IPublishingTaskManager publishingTaskManager) {
|
||||
_contentManager = contentManager;
|
||||
_clock = clock;
|
||||
_publishingTaskManager = publishingTaskManager;
|
||||
}
|
||||
|
||||
public IEnumerable<Page> Get() {
|
||||
@@ -71,21 +72,26 @@ namespace Orchard.Pages.Services {
|
||||
}
|
||||
|
||||
public void Delete(Page page) {
|
||||
_publishingTaskManager.DeleteTasks(page.ContentItem);
|
||||
_contentManager.Remove(page.ContentItem);
|
||||
}
|
||||
|
||||
public void Publish(Page page) {
|
||||
_publishingTaskManager.DeleteTasks(page.ContentItem);
|
||||
_contentManager.Publish(page.ContentItem);
|
||||
}
|
||||
|
||||
public void Publish(Page page, DateTime publishDate) {
|
||||
//TODO: Implement task scheduling
|
||||
//if (page.Published != null && page.Published.Value >= _clock.UtcNow)
|
||||
// _contentManager.Unpublish(page.ContentItem);
|
||||
public void Publish(Page page, DateTime scheduledPublishUtc) {
|
||||
_publishingTaskManager.Publish(page.ContentItem, scheduledPublishUtc);
|
||||
}
|
||||
|
||||
public void Unpublish(Page page) {
|
||||
_contentManager.Unpublish(page.ContentItem);
|
||||
}
|
||||
|
||||
public DateTime? GetScheduledPublishUtc(Page page) {
|
||||
var task = _publishingTaskManager.GetPublishTask(page.ContentItem);
|
||||
return (task == null ? null : task.ScheduledUtc);
|
||||
}
|
||||
}
|
||||
}
|
@@ -90,8 +90,8 @@ foreach (var pageEntry in Model.PageEntries)
|
||||
<td>
|
||||
<% if (!pageEntry.Page.IsPublished)
|
||||
{ %>
|
||||
<%=pageEntry.Page.Published != null
|
||||
? string.Format("{0:d}<br />{0:t}", pageEntry.Page.Published.Value)
|
||||
<%=pageEntry.Page.ScheduledPublishUtc != null
|
||||
? string.Format("{0:d}<br />{0:t}", pageEntry.Page.ScheduledPublishUtc.Value)
|
||||
: ""%>
|
||||
<% } %>
|
||||
</td>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<fieldset>
|
||||
<legend><%=_Encoded("Publish Settings")%></legend>
|
||||
<div>
|
||||
<%=Html.RadioButton("Command", "SaveDraft", Model.ContentItem.VersionRecord == null || !Model.ContentItem.VersionRecord.Published, new { id = "Command_SaveDraft" }) %>
|
||||
<%=Html.RadioButton("Command", "SaveDraft", Model.ContentItem.VersionRecord == null || !Model.ContentItem.VersionRecord.Published, new { id = "Command_SaveDraft" })%>
|
||||
<label class="forcheckbox" for="Command_SaveDraft"><%=_Encoded("Save Draft")%></label>
|
||||
</div>
|
||||
<div>
|
||||
@@ -10,8 +10,8 @@
|
||||
<label class="forcheckbox" for="Command_PublishNow"><%=_Encoded("Publish Now")%></label>
|
||||
</div>
|
||||
<div>
|
||||
<%=Html.RadioButton("Command", "PublishLater", Model.Published != null && Model.Published.Value > DateTime.UtcNow, new { id = "Command_PublishLater" }) %>
|
||||
<%=Html.RadioButton("Command", "PublishLater", Model.ScheduledPublishUtc != null, new { id = "Command_PublishLater" }) %>
|
||||
<label class="forcheckbox" for="Command_PublishLater"><%=_Encoded("Publish Later")%></label>
|
||||
<%=Html.EditorFor(m => m.Published) %>
|
||||
<%=Html.EditorFor(m => m.ScheduledPublishUtc)%>
|
||||
</div>
|
||||
</fieldset>
|
@@ -140,6 +140,7 @@
|
||||
<Compile Include="Security\IAuthorizationServiceEvents.cs" />
|
||||
<Compile Include="Security\StandardPermissions.cs" />
|
||||
<Compile Include="Security\OrchardSecurityException.cs" />
|
||||
<Compile Include="Tasks\Scheduling\IPublishingTaskManager.cs" />
|
||||
<Compile Include="Tasks\Scheduling\IScheduledTask.cs" />
|
||||
<Compile Include="ContentManagement\ContentExtensions.cs" />
|
||||
<Compile Include="ContentManagement\ContentItem.cs" />
|
||||
|
10
src/Orchard/Tasks/Scheduling/IPublishingTaskManager.cs
Normal file
10
src/Orchard/Tasks/Scheduling/IPublishingTaskManager.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using Orchard.ContentManagement;
|
||||
|
||||
namespace Orchard.Tasks.Scheduling {
|
||||
public interface IPublishingTaskManager : IDependency {
|
||||
IScheduledTask GetPublishTask(ContentItem item);
|
||||
void Publish(ContentItem item, DateTime scheduledUtc);
|
||||
void DeleteTasks(ContentItem item);
|
||||
}
|
||||
}
|
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using Orchard.ContentManagement;
|
||||
|
||||
namespace Orchard.ContentManagement.Aspects {
|
||||
namespace Orchard.Tasks.Scheduling {
|
||||
public interface IScheduledTask {
|
||||
string TaskType { get; }
|
||||
DateTime? ScheduledUtc { get; }
|
||||
ContentItem ContentItem { get; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
|
||||
namespace Orchard.Tasks.Scheduling {
|
||||
public interface IScheduledTaskManager : IDependency {
|
||||
void CreateTask(string taskType, DateTime scheduledUtc, ContentItem contentItem);
|
||||
IEnumerable<IScheduledTask> GetTasks(ContentItem contentItem);
|
||||
void DeleteTasks(ContentItem contentItem, Func<IScheduledTask, bool> predicate);
|
||||
}
|
||||
}
|
@@ -1,5 +1,3 @@
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
|
||||
namespace Orchard.Tasks.Scheduling {
|
||||
public class ScheduledTaskContext {
|
||||
public IScheduledTask Task { get; set; }
|
||||
|
@@ -14,7 +14,7 @@ namespace Orchard.Tasks {
|
||||
_timer = new Timer();
|
||||
_timer.Elapsed += Elapsed;
|
||||
Logger = NullLogger.Instance;
|
||||
Interval = TimeSpan.FromMinutes(5);
|
||||
Interval = TimeSpan.FromSeconds(30);
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
Reference in New Issue
Block a user