mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-26 20:16:15 +08:00
Adding Rollback command to content published events and added logging of the rolled back event.
This commit is contained in:
@@ -55,7 +55,8 @@ namespace Orchard.AuditTrail.Controllers {
|
||||
Record = record,
|
||||
EventDescriptor = descriptor,
|
||||
CategoryDescriptor = descriptor.CategoryDescriptor,
|
||||
SummaryShape = _displayBuilder.BuildDisplay(record, "SummaryAdmin")
|
||||
SummaryShape = _displayBuilder.BuildDisplay(record, "SummaryAdmin"),
|
||||
ActionsShape = _displayBuilder.BuildActions(record, "SummaryAdmin"),
|
||||
};
|
||||
|
||||
var viewModel = new AuditTrailViewModel {
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
using System.Web.Mvc;
|
||||
using Orchard.AuditTrail.Helpers;
|
||||
using Orchard.AuditTrail.Models;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Security;
|
||||
using Orchard.UI.Admin;
|
||||
using Orchard.UI.Notify;
|
||||
|
||||
namespace Orchard.AuditTrail.Controllers {
|
||||
[Admin]
|
||||
public class ContentController : Controller {
|
||||
private readonly IAuthorizer _authorizer;
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly INotifier _notifier;
|
||||
|
||||
public ContentController(IAuthorizer authorizer, IContentManager contentManager) {
|
||||
public ContentController(IAuthorizer authorizer, IContentManager contentManager, INotifier notifier) {
|
||||
_authorizer = authorizer;
|
||||
_contentManager = contentManager;
|
||||
_notifier = notifier;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ActionResult Detail(int id, int version) {
|
||||
var contentItem = _contentManager.Get(id, VersionOptions.Number(version));
|
||||
if (!_authorizer.Authorize(Core.Contents.Permissions.ViewContent, contentItem))
|
||||
@@ -29,5 +37,26 @@ namespace Orchard.AuditTrail.Controllers {
|
||||
var editor = _contentManager.BuildEditor(contentItem);
|
||||
return View(editor);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Rollback(int id, int version, string returnUrl) {
|
||||
var contentItem = _contentManager.Get(id);
|
||||
if (!_authorizer.Authorize(Core.Contents.Permissions.PublishContent, contentItem))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var title = _contentManager.GetItemMetadata(contentItem).DisplayText;
|
||||
var currentVersion = contentItem.Version;
|
||||
var newContentItem = _contentManager.Rollback(contentItem, VersionOptions.Rollback(version, publish: true));
|
||||
|
||||
_notifier.Information(T("{0} has been rolled back from version {1} to version {2} as version {3}.", title, currentVersion, version, newContentItem.Version));
|
||||
|
||||
returnUrl = Url.IsLocalUrl(returnUrl)
|
||||
? returnUrl
|
||||
: Request.UrlReferrer != null
|
||||
? Request.UrlReferrer.ToString()
|
||||
: Url.Action("Index", "Admin");
|
||||
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,6 +302,12 @@
|
||||
<ItemGroup>
|
||||
<Content Include="Views\EditorTemplates\Parts.ClientIpAddressSettings.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\AuditTrailEventActions.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\AuditTrailEventActions-Content-Published.SummaryAdmin.cshtml" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace Orchard.AuditTrail.Providers.Content {
|
||||
public const string Removed = "Removed";
|
||||
public const string Imported = "Imported";
|
||||
public const string Exported = "Exported";
|
||||
public const string RolledBack = "RolledBack";
|
||||
|
||||
public static Filters CreateFilters(int contentId, IUpdateModel updateModel) {
|
||||
return new Filters(updateModel) {
|
||||
@@ -34,7 +35,8 @@ namespace Orchard.AuditTrail.Providers.Content {
|
||||
.Event(this, Unpublished, T("Unpublished"), T("A content item was unpublished."), enableByDefault: true)
|
||||
.Event(this, Removed, T("Removed"), T("A content item was deleted."), enableByDefault: true)
|
||||
.Event(this, Imported, T("Imported"), T("A content item was imported."), enableByDefault: true)
|
||||
.Event(this, Exported, T("Exported"), T("A content item was exported."), enableByDefault: false);
|
||||
.Event(this, Exported, T("Exported"), T("A content item was exported."), enableByDefault: false)
|
||||
.Event(this, RolledBack, T("Rolled Back"), T("A content item was rolled back to a previous version."), enableByDefault: true);
|
||||
|
||||
context.QueryFilter(QueryFilter);
|
||||
context.DisplayFilter(DisplayFilter);
|
||||
@@ -51,7 +53,7 @@ namespace Orchard.AuditTrail.Providers.Content {
|
||||
private void DisplayFilter(DisplayFilterContext context) {
|
||||
var contentItemId = context.Filters.Get("content").ToInt32();
|
||||
if (contentItemId != null) {
|
||||
var contentItem = contentItemId != null ? _contentManager.Get(contentItemId.Value, VersionOptions.Latest) : default(ContentItem);
|
||||
var contentItem = _contentManager.Get(contentItemId.Value, VersionOptions.Latest);
|
||||
var filterDisplay = context.ShapeFactory.AuditTrailFilter__ContentItem(ContentItem: contentItem);
|
||||
|
||||
context.FilterDisplay.Add(filterDisplay);
|
||||
|
||||
@@ -41,6 +41,23 @@ namespace Orchard.AuditTrail.Providers.Content {
|
||||
context.Shape.ContentItem = contentItem;
|
||||
context.Shape.PreviousVersion = previousVersion;
|
||||
});
|
||||
|
||||
builder.Describe("AuditTrailEventActions").OnDisplaying(context => {
|
||||
var record = (AuditTrailEventRecord)context.Shape.Record;
|
||||
|
||||
if (record.Category != "Content")
|
||||
return;
|
||||
|
||||
var eventData = (IDictionary<string, object>)context.Shape.EventData;
|
||||
var contentItemId = eventData.Get<int>("ContentId");
|
||||
var previousContentItemVersionId = eventData.Get<int>("PreviousVersionId");
|
||||
var contentItem = _contentManager.Value.Get(contentItemId, VersionOptions.AllVersions);
|
||||
var previousVersion = previousContentItemVersionId > 0 ? _contentManager.Value.Get(contentItemId, VersionOptions.VersionRecord(previousContentItemVersionId)) : default(ContentItem);
|
||||
|
||||
context.Shape.ContentItemId = contentItemId;
|
||||
context.Shape.ContentItem = contentItem;
|
||||
context.Shape.PreviousVersion = previousVersion;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,9 +33,12 @@ namespace Orchard.AuditTrail.Providers.Content {
|
||||
|
||||
protected override void Updating(UpdateContentContext context) {
|
||||
var contentItem = context.ContentItem;
|
||||
|
||||
_ignoreExportHandlerFor = contentItem;
|
||||
_previousVersionXml = _contentItemCreated
|
||||
? default(XElement) // No need to do a diff on a newly created content item.
|
||||
: _contentManager.Export(contentItem);
|
||||
_ignoreExportHandlerFor = null;
|
||||
}
|
||||
|
||||
protected override void Updated(UpdateContentContext context) {
|
||||
@@ -54,6 +57,23 @@ namespace Orchard.AuditTrail.Providers.Content {
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RollingBack(RollbackContentContext context) {
|
||||
_ignoreExportHandlerFor = context.ContentItem;
|
||||
_previousVersionXml = _contentManager.Export(context.ContentItem);
|
||||
_ignoreExportHandlerFor = null;
|
||||
}
|
||||
|
||||
protected override void RolledBack(RollbackContentContext context) {
|
||||
var contentItem = context.ContentItem;
|
||||
|
||||
_ignoreExportHandlerFor = contentItem;
|
||||
var newVersionXml = _contentManager.Export(contentItem);
|
||||
_ignoreExportHandlerFor = null;
|
||||
|
||||
var diffGram = _analyzer.GenerateDiffGram(_previousVersionXml, newVersionXml);
|
||||
RecordAuditTrailEvent(ContentAuditTrailEventProvider.RolledBack, context.ContentItem, diffGram: diffGram, previousVersionXml: _previousVersionXml);
|
||||
}
|
||||
|
||||
protected override void Published(PublishContentContext context) {
|
||||
var previousVersion = context.PreviousItemVersionRecord;
|
||||
RecordAuditTrailEvent(ContentAuditTrailEventProvider.Published, context.ContentItem, previousVersion);
|
||||
|
||||
@@ -1,33 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.AuditTrail.Models;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.DisplayManagement.Shapes;
|
||||
|
||||
namespace Orchard.AuditTrail.Services {
|
||||
public class AuditTrailEventDisplayBuilder : IAuditTrailEventDisplayBuilder {
|
||||
private readonly IShapeFactory _shapeFactory;
|
||||
private readonly IEventDataSerializer _serializer;
|
||||
private readonly IAuditTrailManager _auditTrailManager;
|
||||
|
||||
public AuditTrailEventDisplayBuilder(IShapeFactory shapeFactory, IEventDataSerializer serializer, IAuditTrailManager auditTrailManager) {
|
||||
_shapeFactory = shapeFactory;
|
||||
_serializer = serializer;
|
||||
_auditTrailManager = auditTrailManager;
|
||||
New = shapeFactory;
|
||||
}
|
||||
|
||||
public dynamic New { get; set; }
|
||||
|
||||
public dynamic BuildDisplay(AuditTrailEventRecord record, string displayType) {
|
||||
return BuildEventShape("AuditTrailEvent", record, displayType);
|
||||
}
|
||||
|
||||
public dynamic BuildActions(AuditTrailEventRecord record, string displayType) {
|
||||
return BuildEventShape("AuditTrailEventActions", record, displayType);
|
||||
}
|
||||
|
||||
private dynamic BuildEventShape(string shapeType, AuditTrailEventRecord record, string displayType) {
|
||||
var eventData = _serializer.Deserialize(record.EventData);
|
||||
var descriptor = _auditTrailManager.DescribeEvent(record);
|
||||
var auditTrailEventShape = New.AuditTrailEvent(Record: record, EventData: eventData, Descriptor: descriptor);
|
||||
var metaData = (ShapeMetadata)auditTrailEventShape.Metadata;
|
||||
var auditTrailEventActionsShape = _shapeFactory.Create(shapeType, Arguments.From(new Dictionary<string, object> {
|
||||
{"Record", record},
|
||||
{"EventData", eventData},
|
||||
{"Descriptor", descriptor}
|
||||
}));
|
||||
var metaData = auditTrailEventActionsShape.Metadata;
|
||||
metaData.DisplayType = displayType;
|
||||
metaData.Alternates.Add(String.Format("AuditTrailEvent_{0}", displayType));
|
||||
metaData.Alternates.Add(String.Format("AuditTrailEvent__{0}", record.Category));
|
||||
metaData.Alternates.Add(String.Format("AuditTrailEvent_{0}__{1}", displayType, record.Category));
|
||||
metaData.Alternates.Add(String.Format("AuditTrailEvent__{0}__{1}", record.Category, record.EventName));
|
||||
metaData.Alternates.Add(String.Format("AuditTrailEvent_{0}__{1}__{2}", displayType, record.Category, record.EventName));
|
||||
return auditTrailEventShape;
|
||||
metaData.Alternates.Add(String.Format("{0}_{1}", shapeType, displayType));
|
||||
metaData.Alternates.Add(String.Format("{0}__{1}", shapeType, record.Category));
|
||||
metaData.Alternates.Add(String.Format("{0}_{1}__{2}", shapeType, displayType, record.Category));
|
||||
metaData.Alternates.Add(String.Format("{0}__{1}__{2}", shapeType, record.Category, record.EventName));
|
||||
metaData.Alternates.Add(String.Format("{0}_{1}__{2}__{3}", shapeType, displayType, record.Category, record.EventName));
|
||||
return auditTrailEventActionsShape;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,5 +3,6 @@ using Orchard.AuditTrail.Models;
|
||||
namespace Orchard.AuditTrail.Services {
|
||||
public interface IAuditTrailEventDisplayBuilder : IDependency {
|
||||
dynamic BuildDisplay(AuditTrailEventRecord record, string displayType);
|
||||
dynamic BuildActions(AuditTrailEventRecord record, string displayType);
|
||||
}
|
||||
}
|
||||
@@ -7,5 +7,6 @@ namespace Orchard.AuditTrail.ViewModels {
|
||||
public AuditTrailEventDescriptor EventDescriptor { get; set; }
|
||||
public AuditTrailCategoryDescriptor CategoryDescriptor { get; set; }
|
||||
public dynamic SummaryShape { get; set; }
|
||||
public dynamic ActionsShape { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
@using Orchard.AuditTrail.Helpers
|
||||
@using Orchard.AuditTrail.Models
|
||||
@using Orchard.AuditTrail.Services.Models
|
||||
@using Orchard.ContentManagement
|
||||
@model Orchard.AuditTrail.ViewModels.AuditTrailViewModel
|
||||
@{
|
||||
Style.Include("audittrail-display.css");
|
||||
@@ -16,7 +14,7 @@
|
||||
Layout.Title = T("Audit Trail");
|
||||
}
|
||||
@Html.ValidationSummary()
|
||||
@using (Html.BeginForm("Index", "Admin", new { area = "Orchard.AuditTrail" }, FormMethod.Get)) {
|
||||
@using (Html.BeginFormAntiForgeryPost(Url.Action("Index", "Admin", new { area = "Orchard.AuditTrail" }), FormMethod.Get)) {
|
||||
<section class="audittrail-filter-section">
|
||||
@Display(Model.FilterDisplay)
|
||||
<div class="filter-control-group">
|
||||
@@ -57,7 +55,7 @@
|
||||
<td class="timestamp-column">@Display.DateTime(DateTimeUtc: record.Record.CreatedUtc)</td>
|
||||
<td class="summary-column">@Display(record.SummaryShape)</td>
|
||||
<td class="comment-column">@Html.Raw(record.Record.Comment.NewlinesToHtml())</td>
|
||||
<td class="actions-column">@Html.ActionLink(T("Details").Text, "Detail", "Admin", new { id = record.Record.Id, area = "Orchard.AuditTrail" }, null)</td>
|
||||
<td class="actions-column">@Display(record.ActionsShape)</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
@using Orchard.AuditTrail.Helpers
|
||||
@using Orchard.AuditTrail.Models
|
||||
@using Orchard.AuditTrail.Providers.Content
|
||||
@using Orchard.AuditTrail.Services.Models
|
||||
@using Orchard.ContentManagement
|
||||
@{
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
@using Orchard.AuditTrail.Helpers
|
||||
@using Orchard.AuditTrail.Models
|
||||
@using Orchard.ContentManagement
|
||||
@{
|
||||
var record = (AuditTrailEventRecord)Model.Record;
|
||||
var eventData = (IDictionary<string, object>) Model.EventData;
|
||||
var versionNumber = eventData.Get<int>("VersionNumber");
|
||||
var contentItem = (ContentItem) Model.ContentItem;
|
||||
}
|
||||
@Html.ActionLink(T("Details").Text, "Detail", "Admin", new { id = record.Id, area = "Orchard.AuditTrail" }, null)
|
||||
@if (contentItem != null) {
|
||||
var isLatest = contentItem.VersionRecord.Number == versionNumber;
|
||||
if (!isLatest) {
|
||||
@T(" | ")
|
||||
@Html.ActionLink(T("Rollback").Text, "Rollback", "Content", new {id = contentItem.Id, version = versionNumber, area = "Orchard.AuditTrail"}, new { data_unsafe_url = T("Are you sure you want to rollback to version {0}?", versionNumber) })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
@using Orchard.AuditTrail.Models
|
||||
@{
|
||||
var record = (AuditTrailEventRecord)Model.Record;
|
||||
}
|
||||
@Html.ActionLink(T("Details").Text, "Detail", "Admin", new { id = record.Id, area = "Orchard.AuditTrail" }, null)
|
||||
Reference in New Issue
Block a user