#21091: Fixing audit trail bug.

This fixes an issue causing a YSOD on the AuditTrail screen by recording all relevant data for the AuditTrailSettings event provider, such that the event providers themselves can be disabled without breaking the recorded events (which relied on the Event Descriptor to be available when rendering the event, which is unavailable if its providing feature is disabled).

Work Item: 21091
This commit is contained in:
Sipke Schoorstra
2014-11-27 16:55:18 -08:00
parent ae9c8a9457
commit da72fc9809
7 changed files with 115 additions and 12 deletions

View File

@@ -10,9 +10,9 @@ using Orchard.Localization;
namespace Orchard.AuditTrail.Handlers {
public class AuditTrailSettingsPartHandler : ContentHandler {
private readonly ISignals _signals;
private string _oldEventSettings;
private readonly IAuditTrailManager _auditTrailManager;
private readonly IWorkContextAccessor _wca;
private string _oldEventSettings;
public AuditTrailSettingsPartHandler(ISignals signals, IAuditTrailManager auditTrailManager, IWorkContextAccessor wca) {
_signals = signals;
@@ -54,8 +54,8 @@ namespace Orchard.AuditTrail.Handlers {
_auditTrailManager.CreateRecord<AuditTrailSettingsEventProvider>(
eventName: AuditTrailSettingsEventProvider.EventsChanged,
eventData: new Dictionary<string, object> {
{"OldSettings", _oldEventSettings},
{"NewSettings", newEventSettings}
{"OldSettings", _auditTrailManager.ToEventData(_oldEventSettings)},
{"NewSettings", _auditTrailManager.ToEventData(newEventSettings)}
},
user: _wca.GetContext().CurrentUser);
}

View File

@@ -189,6 +189,8 @@
<ItemGroup>
<Compile Include="Menus\AuditTrailAdminMenu.cs" />
<Compile Include="Menus\RecycleBinAdminMenu.cs" />
<Compile Include="Providers\AuditTrail\AuditTrailEventSettingEventData.cs" />
<Compile Include="Providers\AuditTrail\AuditTrailManagerExtensions.cs" />
<Compile Include="ViewModels\RecycleBinCommand.cs" />
<Compile Include="Controllers\RecycleBinController.cs" />
<Compile Include="Controllers\ContentController.cs" />

View File

@@ -0,0 +1,9 @@
namespace Orchard.AuditTrail.Providers.AuditTrail {
public class AuditTrailEventSettingEventData
{
public string EventName { get; set; }
public string EventDisplayName { get; set; }
public string EventCategory { get; set; }
public bool IsEnabled { get; set; }
}
}

View File

@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Orchard.AuditTrail.Services;
using Orchard.ContentManagement;
using Orchard.Logging;
namespace Orchard.AuditTrail.Providers.AuditTrail {
public static class AuditTrailManagerExtensions {
public static string ToEventData(this IAuditTrailManager auditTrailManager, string settingsData) {
var settings = auditTrailManager.DeserializeProviderConfiguration(settingsData);
var query =
from setting in settings
let descriptor = auditTrailManager.DescribeEvent(setting.EventName)
select new AuditTrailEventSettingEventData {
EventName = setting.EventName,
IsEnabled = setting.IsEnabled,
EventCategory = descriptor.CategoryDescriptor.Name.TextHint,
EventDisplayName = descriptor.Name.TextHint
};
return SerializeEventData(query);
}
public static string SerializeEventData(IEnumerable<AuditTrailEventSettingEventData> settings) {
var doc = new XDocument(
new XElement("Events",
settings.Select(x =>
new XElement("Event",
new XAttribute("Name", x.EventName),
new XAttribute("IsEnabled", x.IsEnabled),
new XAttribute("DisplayName", x.EventDisplayName),
new XAttribute("Category", x.EventCategory)))));
return doc.ToString(SaveOptions.DisableFormatting);
}
public static IEnumerable<AuditTrailEventSettingEventData> DeserializeEventData(string data, ILogger logger) {
if (String.IsNullOrWhiteSpace(data))
return Enumerable.Empty<AuditTrailEventSettingEventData>();
try {
var doc = XDocument.Parse(data);
return doc.Element("Events").Elements("Event").Select(x => new AuditTrailEventSettingEventData {
EventName = x.Attr<string>("Name"),
IsEnabled = x.Attr<bool>("IsEnabled"),
EventDisplayName = x.Attr<string>("DisplayName"),
EventCategory = x.Attr<string>("Category")
}).ToArray();
}
catch (Exception ex) {
logger.Error(ex, "Error occurred during deserialization of audit trail settings.");
}
return Enumerable.Empty<AuditTrailEventSettingEventData>();
}
}
}

View File

@@ -1,11 +1,11 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.AuditTrail.Models;
using Orchard.AuditTrail.Services;
using Orchard.AuditTrail.Shapes;
using Orchard.AuditTrail.ViewModels;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment;
using Orchard.Logging;
namespace Orchard.AuditTrail.Providers.AuditTrail {
public class AuditTrailSettingsEventShape : AuditTrailEventShapeAlteration<AuditTrailSettingsEventProvider> {
@@ -13,16 +13,19 @@ namespace Orchard.AuditTrail.Providers.AuditTrail {
public AuditTrailSettingsEventShape(Work<IAuditTrailManager> auditTrailManager) {
_auditTrailManager = auditTrailManager;
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
protected override string EventName {
get { return AuditTrailSettingsEventProvider.EventsChanged; }
}
protected override void OnAlterShape(ShapeDisplayingContext context) {
var eventData = (IDictionary<string, object>)context.Shape.EventData;
var oldSettings = _auditTrailManager.Value.DeserializeProviderConfiguration((string)eventData["OldSettings"]);
var newSettings = _auditTrailManager.Value.DeserializeProviderConfiguration((string)eventData["NewSettings"]);
var oldSettings = AuditTrailManagerExtensions.DeserializeEventData((string)eventData["OldSettings"], Logger);
var newSettings = AuditTrailManagerExtensions.DeserializeEventData((string)eventData["NewSettings"], Logger);
var diff = GetDiffQuery(oldSettings, newSettings).ToArray();
context.Shape.OldSettings = oldSettings;
@@ -30,16 +33,17 @@ namespace Orchard.AuditTrail.Providers.AuditTrail {
context.Shape.Diff = diff;
}
private IEnumerable<AuditTrailEventDescriptorSettingViewModel> GetDiffQuery(IEnumerable<AuditTrailEventSetting> oldSettings, IEnumerable<AuditTrailEventSetting> newSettings) {
private IEnumerable<AuditTrailEventDescriptorSettingViewModel> GetDiffQuery(IEnumerable<AuditTrailEventSettingEventData> oldSettings, IEnumerable<AuditTrailEventSettingEventData> newSettings) {
var oldDictionary = oldSettings.ToDictionary(x => x.EventName);
return
from newSetting in newSettings
let oldSetting = oldDictionary.ContainsKey(newSetting.EventName) ? oldDictionary[newSetting.EventName] : default(AuditTrailEventSetting)
let oldSetting = oldDictionary.ContainsKey(newSetting.EventName) ? oldDictionary[newSetting.EventName] : default(AuditTrailEventSettingEventData)
where oldSetting == null || oldSetting.IsEnabled != newSetting.IsEnabled
let descriptor = _auditTrailManager.Value.DescribeEvent(newSetting.EventName)
select new AuditTrailEventDescriptorSettingViewModel {
Setting = newSetting,
Descriptor = _auditTrailManager.Value.DescribeEvent(newSetting.EventName)
Descriptor = descriptor
};
}
}

View File

@@ -1,9 +1,32 @@
using Orchard.AuditTrail.Models;
using System;
using Orchard.AuditTrail.Helpers;
using Orchard.AuditTrail.Providers.AuditTrail;
using Orchard.AuditTrail.Services.Models;
namespace Orchard.AuditTrail.ViewModels {
public class AuditTrailEventDescriptorSettingViewModel {
public AuditTrailEventDescriptor Descriptor { get; set; }
public AuditTrailEventSetting Setting { get; set; }
public AuditTrailEventSettingEventData Setting { get; set; }
public string EventDisplayName {
get { return
!String.IsNullOrWhiteSpace(Setting.EventDisplayName)
? Setting.EventDisplayName
: Descriptor != null
? Descriptor.Name.Text
: EventNameExtensions.GetShortEventName(Setting.EventName);
}
}
public string EventCategory {
get {
return
!String.IsNullOrWhiteSpace(Setting.EventCategory)
? Setting.EventCategory
: Descriptor != null
? Descriptor.CategoryDescriptor.Name.Text
: Setting.EventCategory;
}
}
}
}

View File

@@ -1,7 +1,13 @@
@using Orchard.AuditTrail.ViewModels
@{
var diff = (IList<AuditTrailEventDescriptorSettingViewModel>)Model.Diff;
var descriptions = String.Join("<br/>", diff.Select(x => T("The <strong>{0}</strong> event in category <strong>{1}</strong> was <strong>{2}</strong>.", x.Descriptor.Name, x.Descriptor.CategoryDescriptor.Name, x.Setting.IsEnabled ? "enabled" : "disabled").Text));
var descriptions = String.Join("<br/>", diff.Select(x => {
var template = String.IsNullOrWhiteSpace(x.EventCategory)
? "The <strong>{0}</strong> event was <strong>{2}</strong>."
: "The <strong>{0}</strong> event in category <strong>{1}</strong> was <strong>{2}</strong>.";
return T(template, x.EventDisplayName, T(x.EventCategory), x.Setting.IsEnabled ? "enabled" : "disabled").Text;
}));
}
<section class="audittrail-settings-eventsummary">
@Html.Raw(descriptions)