Merged from 1.x.

This commit is contained in:
Daniel Stolt
2014-07-30 18:39:37 +02:00
227 changed files with 5704 additions and 678 deletions

View File

@@ -245,6 +245,10 @@
<Name>Orchard.ArchiveLater</Name>
<Private>True</Private>
</ProjectReference>
<ProjectReference Include="..\..\Orchard.Web\Modules\Orchard.AuditTrail\Orchard.AuditTrail.csproj">
<Project>{3dd574cd-9c5d-4a45-85e1-ebba64c22b5f}</Project>
<Name>Orchard.AuditTrail</Name>
</ProjectReference>
<ProjectReference Include="..\..\Orchard.Web\Modules\Orchard.Autoroute\Orchard.Autoroute.csproj">
<Project>{66FCCD76-2761-47E3-8D11-B45D0001DDAA}</Project>
<Name>Orchard.Autoroute</Name>

View File

@@ -1,9 +1,12 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.30110.0
VisualStudioVersion = 12.0.30501.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}"
ProjectSection(SolutionItems) = preProject
..\Orchard.Web\Modules\Orchard.AuditTrail\Orchard.AuditTrail.csproj = ..\Orchard.Web\Modules\Orchard.AuditTrail\Orchard.AuditTrail.csproj
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{75E7476C-C05B-4C41-8E38-081D3EB55659}"
EndProject
@@ -145,6 +148,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Templates", "..\Orc
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Azure.MediaServices", "..\Orchard.Web\Modules\Orchard.Azure.MediaServices\Orchard.Azure.MediaServices.csproj", "{14A96B1A-9DC9-44C8-A675-206329E15263}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.AuditTrail", "..\Orchard.Web\Modules\Orchard.AuditTrail\Orchard.AuditTrail.csproj", "{3DD574CD-9C5D-4A45-85E1-EBBA64C22B5F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -415,15 +420,22 @@ Global
{14A96B1A-9DC9-44C8-A675-206329E15263}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14A96B1A-9DC9-44C8-A675-206329E15263}.Release|Any CPU.ActiveCfg = Release|Any CPU
{14A96B1A-9DC9-44C8-A675-206329E15263}.Release|Any CPU.Build.0 = Release|Any CPU
{3DD574CD-9C5D-4A45-85E1-EBBA64C22B5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DD574CD-9C5D-4A45-85E1-EBBA64C22B5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DD574CD-9C5D-4A45-85E1-EBBA64C22B5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DD574CD-9C5D-4A45-85E1-EBBA64C22B5F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} = {F2AB7512-139A-420F-AE3A-9ED22CA52CE1}
{9916839C-39FC-4CEB-A5AF-89CA7E87119F} = {F2AB7512-139A-420F-AE3A-9ED22CA52CE1}
{63FBD4D9-E1DA-4A7B-AA6A-D6074FE50867} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{14C049FD-B35B-415A-A824-87F26B26E7FD} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{0E7646E8-FE8F-43C1-8799-D97860925EC4} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{EA2B9121-EF54-40A6-A53E-6593C86EE696} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{D9A7B330-CD22-4DA1-A95A-8DE1982AD8EB} = {B6092A92-1071-4C30-AD55-8E8D46BC2F14}
{17F86780-9A1F-4AA1-86F1-875EEC2730C7} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{72457126-E118-4171-A08F-9A709EE4B7FC} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{D10AD48F-407D-4DB5-A328-173EC7CB010F} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
@@ -433,6 +445,7 @@ Global
{CDE24A24-01D3-403C-84B9-37722E18DFB7} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{79AED36E-ABD0-4747-93D3-8722B042454B} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{954CA994-D204-468B-9D69-51F6AD3E1C29} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{33B1BC8D-E292-4972-A363-22056B207156} = {75E7476C-C05B-4C41-8E38-081D3EB55659}
{D5D447D7-EF8E-43A6-B9A4-3B025DD9F45D} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{DFD137A2-DDB5-4D22-BE0D-FA9AD4C8B059} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{C0C45321-B51D-4D8D-9B7B-AA4C2E0B2962} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
@@ -441,16 +454,20 @@ Global
{8F116B06-1C0E-4E4C-9A0A-D2FAB851E768} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{EA4F1DA7-F2AB-4384-9AA4-9B756E2026B1} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{194D3CCC-1153-474D-8176-FDE8D7D0D0BD} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{CB70A642-8CEC-4DDE-8C9F-AD08900EC98D} = {84650275-884D-4CBB-9CC0-67553996E211}
{FBC8B571-ED50-49D8-8D9D-64AB7454A0D6} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{137906EA-15FE-4AD8-A6A0-27528F0477D6} = {B6092A92-1071-4C30-AD55-8E8D46BC2F14}
{3420C92A-747F-4990-BA08-F2C9531E44AD} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{C889167C-E52C-4A65-A419-224B3D1B957D} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{99002B65-86F7-415E-BF4A-381AA8AB9CCC} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{2AD6973D-C7BB-416E-89FE-EEE34664E05F} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{4A4595EF-6C37-4F99-96ED-4AE0B9E438D3} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{43D0EC0B-1955-4566-8D31-7B9102DA1703} = {B6092A92-1071-4C30-AD55-8E8D46BC2F14}
{FC1D74E8-7A4D-48F4-83DE-95C6173780C4} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{3158C928-888C-4A84-8BC1-4A8257489538} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{642A49D7-8752-4177-80D6-BFBBCFAD3DE0} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{6F759635-13D7-4E94-BCC9-80445D63F117} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{966EC390-3C7F-4D98-92A6-F0F30D02E9B1} = {B6092A92-1071-4C30-AD55-8E8D46BC2F14}
{3F72A4E9-7B72-4260-B010-C16EC54F9BAF} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{475B6C45-B27C-438B-8966-908B9D6D1077} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{66FCCD76-2761-47E3-8D11-B45D0001DDAA} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
@@ -475,13 +492,6 @@ Global
{36B82383-D69E-4897-A24A-648BABDF80EC} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{10AB3CE2-A720-467F-9EC8-EBB4BAC9A1C9} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{14A96B1A-9DC9-44C8-A675-206329E15263} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
{33B1BC8D-E292-4972-A363-22056B207156} = {75E7476C-C05B-4C41-8E38-081D3EB55659}
{CB70A642-8CEC-4DDE-8C9F-AD08900EC98D} = {84650275-884D-4CBB-9CC0-67553996E211}
{9916839C-39FC-4CEB-A5AF-89CA7E87119F} = {F2AB7512-139A-420F-AE3A-9ED22CA52CE1}
{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} = {F2AB7512-139A-420F-AE3A-9ED22CA52CE1}
{137906EA-15FE-4AD8-A6A0-27528F0477D6} = {B6092A92-1071-4C30-AD55-8E8D46BC2F14}
{43D0EC0B-1955-4566-8D31-7B9102DA1703} = {B6092A92-1071-4C30-AD55-8E8D46BC2F14}
{966EC390-3C7F-4D98-92A6-F0F30D02E9B1} = {B6092A92-1071-4C30-AD55-8E8D46BC2F14}
{D9A7B330-CD22-4DA1-A95A-8DE1982AD8EB} = {B6092A92-1071-4C30-AD55-8E8D46BC2F14}
{3DD574CD-9C5D-4A45-85E1-EBBA64C22B5F} = {8E3DE014-9B28-4B32-8AC1-B2BE404E9B2D}
EndGlobalSection
EndGlobal

View File

@@ -6,12 +6,12 @@
@if (Model.ShowDate) {
<label class="forpicker" for="@Html.FieldIdFor(m => Model.Date)">@T("Date")</label>
<span class="date">@Html.EditorFor(m => m.Date)</span>
<span class="date">@Html.TextBoxFor(m => m.Date, new { placeholder = T("Date").Text })</span>
}
@if (Model.ShowTime) {
<label class="forpicker" for="@Html.FieldIdFor(m => Model.Time)">@T("Time")</label>
<span class="time">@Html.EditorFor(m => m.Time)</span>
<span class="time">@Html.TextBoxFor(m => m.Time, new { placeholder = T("Time").Text })</span>
}
@if (Model.ShowDate) { <text>@Html.ValidationMessageFor(m => m.Date)</text> }

View File

@@ -0,0 +1,15 @@
using Orchard.UI.Navigation;
namespace Orchard.AuditTrail {
public class AdminMenu : Component, INavigationProvider {
public string MenuName { get { return "admin"; } }
public void GetNavigation(NavigationBuilder builder) {
builder.AddImageSet("audittrail")
.Add(T("Audit Trail"), "12", menuItem => menuItem
.Action("Index", "Admin", new { area = "Orchard.AuditTrail" })
.Permission(Permissions.ManageAuditTrailSettings));
}
}
}

View File

@@ -0,0 +1,97 @@
using System.Linq;
using System.Web.Mvc;
using Orchard.AuditTrail.Models;
using Orchard.AuditTrail.Services;
using Orchard.AuditTrail.Services.Models;
using Orchard.AuditTrail.ViewModels;
using Orchard.Collections;
using Orchard.ContentManagement;
using Orchard.Localization;
using Orchard.Localization.Services;
using Orchard.Security;
using Orchard.UI.Navigation;
namespace Orchard.AuditTrail.Controllers {
public class AdminController : Controller, IUpdateModel {
private readonly IAuthorizer _authorizer;
private readonly IAuditTrailManager _auditTrailManager;
private readonly IOrchardServices _services;
private readonly IAuditTrailEventDisplayBuilder _displayBuilder;
private readonly IDateServices _dateServices;
public AdminController(IAuditTrailManager auditTrailManager, IOrchardServices services, IAuditTrailEventDisplayBuilder displayBuilder, IDateServices dateServices) {
_auditTrailManager = auditTrailManager;
_services = services;
_displayBuilder = displayBuilder;
_dateServices = dateServices;
_authorizer = services.Authorizer;
New = _services.New;
}
public dynamic New { get; private set; }
public ActionResult Index(PagerParameters pagerParameters, AuditTrailOrderBy? orderBy = null) {
if(!_authorizer.Authorize(Permissions.ViewAuditTrail))
return new HttpUnauthorizedResult();
var pager = new Pager(_services.WorkContext.CurrentSite, pagerParameters);
var filters = Filters.From(Request.QueryString, this);
var pageOfData = _auditTrailManager.GetRecords(pager.Page, pager.PageSize, filters, orderBy ?? AuditTrailOrderBy.DateDescending);
// If there's a filter validation error, clear the results.
if (!ModelState.IsValid) {
pageOfData = new PageOfItems<AuditTrailEventRecord>(Enumerable.Empty<AuditTrailEventRecord>());
}
var pagerShape = New.Pager(pager).TotalItemCount(pageOfData.TotalItemCount);
var filterDisplay = _auditTrailManager.BuildFilterDisplay(filters);
var eventDescriptorsQuery =
from c in _auditTrailManager.DescribeCategories()
from e in c.Events
select e;
var eventDescriptors = eventDescriptorsQuery.ToDictionary(x => x.Event);
var recordViewModelsQuery =
from record in pageOfData
let descriptor = eventDescriptors.ContainsKey(record.FullEventName) ? eventDescriptors[record.FullEventName] : default(AuditTrailEventDescriptor)
where descriptor != null
select new AuditTrailEventSummaryViewModel {
Record = record,
EventDescriptor = descriptor,
CategoryDescriptor = descriptor.CategoryDescriptor,
SummaryShape = _displayBuilder.BuildDisplay(record, "SummaryAdmin")
};
var viewModel = new AuditTrailViewModel {
Records = recordViewModelsQuery.ToArray(),
Pager = pagerShape,
OrderBy = orderBy ?? AuditTrailOrderBy.DateDescending,
FilterDisplay = filterDisplay
};
return View(viewModel);
}
public ActionResult Detail(int id) {
if (!_authorizer.Authorize(Permissions.ViewAuditTrail))
return new HttpUnauthorizedResult();
var record = _auditTrailManager.GetRecord(id);
var descriptor = _auditTrailManager.DescribeEvent(record.FullEventName);
var detailsShape = _displayBuilder.BuildDisplay(record, "Detail");
var viewModel = new AuditTrailDetailsViewModel {
Record = record,
Descriptor = descriptor,
DetailsShape = detailsShape
};
return View(viewModel);
}
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
}
void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
ModelState.AddModelError(key, errorMessage.Text);
}
}
}

View File

@@ -0,0 +1,33 @@
using System.Web.Mvc;
using Orchard.AuditTrail.Models;
using Orchard.ContentManagement;
using Orchard.Security;
using Orchard.UI.Admin;
namespace Orchard.AuditTrail.Controllers {
[Admin]
public class ContentController : Controller {
private readonly IAuthorizer _authorizer;
private readonly IContentManager _contentManager;
public ContentController(IAuthorizer authorizer, IContentManager contentManager) {
_authorizer = authorizer;
_contentManager = contentManager;
}
public ActionResult Detail(int id, int version) {
var contentItem = _contentManager.Get(id, VersionOptions.Number(version));
if (!_authorizer.Authorize(Core.Contents.Permissions.ViewContent, contentItem))
return new HttpUnauthorizedResult();
var auditTrailPart = contentItem.As<AuditTrailPart>();
if (auditTrailPart != null) {
auditTrailPart.ShowComment = true;
}
var editor = _contentManager.BuildEditor(contentItem);
return View(editor);
}
}
}

View File

@@ -0,0 +1,82 @@
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Orchard.AuditTrail.Models;
using Orchard.AuditTrail.Providers.Content;
using Orchard.AuditTrail.Services;
using Orchard.AuditTrail.Settings;
using Orchard.AuditTrail.ViewModels;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.UI.Navigation;
namespace Orchard.AuditTrail.Drivers {
public class AuditTrailPartDriver : ContentPartDriver<AuditTrailPart> {
private readonly IOrchardServices _services;
private readonly IAuditTrailManager _auditTrailManager;
private readonly IAuditTrailEventDisplayBuilder _displayBuilder;
public AuditTrailPartDriver(IOrchardServices services, IAuditTrailManager auditTrailManager, IAuditTrailEventDisplayBuilder displayBuilder) {
_services = services;
_auditTrailManager = auditTrailManager;
_displayBuilder = displayBuilder;
}
protected override DriverResult Editor(AuditTrailPart part, dynamic shapeHelper) {
return Editor(part, null, shapeHelper);
}
protected override DriverResult Editor(AuditTrailPart part, IUpdateModel updater, dynamic shapeHelper) {
var settings = part.Settings.GetModel<AuditTrailPartSettings>();
var results = new List<DriverResult>();
if (settings.ShowAuditTrailCommentInput) {
results.Add(ContentShape("Parts_AuditTrail_Comment", () => {
var viewModel = new AuditTrailCommentViewModel();
if (part.ShowComment) {
viewModel.Comment = part.Comment;
}
if (updater != null) {
if (updater.TryUpdateModel(viewModel, Prefix, null, null)) {
part.Comment = viewModel.Comment;
}
}
return shapeHelper.EditorTemplate(Model: viewModel, TemplateName: "Parts.AuditTrail.Comment", Prefix: Prefix);
}));
}
if (_services.Authorizer.Authorize(Permissions.ViewAuditTrail)) {
if (settings.ShowAuditTrailLink) {
results.Add(ContentShape("Parts_AuditTrail_Link", () => shapeHelper.Parts_AuditTrail_Link()));
}
if (settings.ShowAuditTrail) {
results.Add(ContentShape("Parts_AuditTrail", () => {
var pager = new Pager(_services.WorkContext.CurrentSite, null, null);
var pageOfData = _auditTrailManager.GetRecords(pager.Page, pager.PageSize, ContentAuditTrailEventProvider.CreateFilters(part.Id, updater));
var pagerShape = shapeHelper.Pager(pager).TotalItemCount(pageOfData.TotalItemCount);
var eventDescriptors =
from c in _auditTrailManager.DescribeCategories()
from e in c.Events
select e;
var recordViewModels =
from record in pageOfData
let descriptor = eventDescriptors.FirstOrDefault(x => x.Event == record.FullEventName)
where descriptor != null
select new AuditTrailEventSummaryViewModel {
Record = record,
EventDescriptor = descriptor,
CategoryDescriptor = descriptor.CategoryDescriptor,
SummaryShape = _displayBuilder.BuildDisplay(record, "SummaryAdmin")
};
return shapeHelper.Parts_AuditTrail(Records: recordViewModels, Pager: pagerShape);
}));
}
}
return Combined(results.ToArray());
}
}
}

View File

@@ -0,0 +1,92 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.AuditTrail.Models;
using Orchard.AuditTrail.Services;
using Orchard.AuditTrail.Services.Models;
using Orchard.AuditTrail.ViewModels;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.Security;
namespace Orchard.AuditTrail.Drivers {
public class AuditTrailSettingsPartDriver : ContentPartDriver<AuditTrailSettingsPart> {
private readonly IAuditTrailManager _auditTrailManager;
private readonly IAuthorizer _authorizer;
public AuditTrailSettingsPartDriver(IAuditTrailManager auditTrailManager, IAuthorizer authorizer) {
_auditTrailManager = auditTrailManager;
_authorizer = authorizer;
}
protected override DriverResult Editor(AuditTrailSettingsPart part, dynamic shapeHelper) {
return Editor(part, null, shapeHelper);
}
protected override DriverResult Editor(AuditTrailSettingsPart part, IUpdateModel updater, dynamic shapeHelper) {
if (!_authorizer.Authorize(Permissions.ManageAuditTrailSettings))
return null;
return ContentShape("Parts_AuditTrailSettings_Edit", () => {
var descriptors = _auditTrailManager.DescribeCategories();
var eventSettings = part.EventSettings.ToList();
var categoriesQuery =
from categoryDescriptor in descriptors
let eventsQuery =
from eventDescriptor in categoryDescriptor.Events
let eventSetting = GetOrCreate(eventSettings, eventDescriptor)
select new AuditTrailEventSettingsViewModel {
Event = eventDescriptor.Event,
Name = eventDescriptor.Name,
Description = eventDescriptor.Description,
IsEnabled = eventDescriptor.IsMandatory || eventSetting.IsEnabled,
IsMandatory = eventDescriptor.IsMandatory
}
select new AuditTrailCategorySettingsViewModel {
Category = categoryDescriptor.Category,
Name = categoryDescriptor.Name,
Events = eventsQuery.ToList()
};
var viewModel = new AuditTrailSettingsViewModel {
Categories = categoriesQuery.ToList()
};
// Update the settings as we may have added new settings.
part.EventSettings = eventSettings;
if (updater != null) {
var eventsDictionary = _auditTrailManager.DescribeProviders().Describe().SelectMany(x => x.Events).ToDictionary(x => x.Event);
if (updater.TryUpdateModel(viewModel, Prefix, null, null)) {
foreach (var eventSettingViewModel in viewModel.Categories.SelectMany(x => x.Events)) {
var eventSetting = eventSettings.FirstOrDefault(x => x.EventName == eventSettingViewModel.Event);
var descriptor = eventsDictionary[eventSetting.EventName];
eventSetting.IsEnabled = eventSettingViewModel.IsEnabled || descriptor.IsMandatory;
}
part.EventSettings = eventSettings;
}
}
return shapeHelper.EditorTemplate(TemplateName: "Parts.AuditTrailSettings", Model: viewModel, Prefix: Prefix);
}).OnGroup("Audit Trail");
}
/// <summary>
/// We're creating settings on the fly so that when the user updates the settings the first time, we won't log a massive amount of event settings that have changed.
/// </summary>
private AuditTrailEventSetting GetOrCreate(ICollection<AuditTrailEventSetting> settings, AuditTrailEventDescriptor descriptor) {
var setting = settings.FirstOrDefault(x => x.EventName == descriptor.Event);
if (setting == null) {
setting = new AuditTrailEventSetting {
EventName = descriptor.Event,
IsEnabled = descriptor.IsMandatory || descriptor.IsEnabledByDefault
};
settings.Add(setting);
}
return setting;
}
}
}

View File

@@ -0,0 +1,52 @@
using Orchard.AuditTrail.Models;
using Orchard.AuditTrail.ViewModels;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.Environment.Extensions;
using Orchard.Localization;
using Orchard.Localization.Services;
using Orchard.Security;
namespace Orchard.AuditTrail.Drivers {
[OrchardFeature("Orchard.AuditTrail.Trimming")]
public class AuditTrailTrimmingSettingsPartDriver : ContentPartDriver<AuditTrailTrimmingSettingsPart> {
private readonly IAuthorizer _authorizer;
private readonly IDateServices _dateServices;
private readonly IDateTimeFormatProvider _dateTimeLocalization;
public AuditTrailTrimmingSettingsPartDriver(IAuthorizer authorizer, IDateServices dateServices, IDateTimeFormatProvider dateTimeLocalization) {
_authorizer = authorizer;
_dateServices = dateServices;
_dateTimeLocalization = dateTimeLocalization;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
protected override DriverResult Editor(AuditTrailTrimmingSettingsPart part, dynamic shapeHelper) {
return Editor(part, null, shapeHelper);
}
protected override DriverResult Editor(AuditTrailTrimmingSettingsPart part, IUpdateModel updater, dynamic shapeHelper) {
if (!_authorizer.Authorize(Permissions.ManageAuditTrailSettings))
return null;
return ContentShape("Parts_AuditTrailTrimmingSettings_Edit", () => {
var viewModel = new AuditTrailTrimmingSettingsViewModel {
RetentionPeriod = part.RetentionPeriod,
MinimumRunInterval = part.MinimumRunInterval,
LastRunDateString = _dateServices.ConvertToLocalString(part.LastRunUtc, _dateTimeLocalization.ShortDateTimeFormat, T("Never").Text)
};
if (updater != null) {
if (updater.TryUpdateModel(viewModel, Prefix, null, null)) {
part.RetentionPeriod = viewModel.RetentionPeriod;
part.MinimumRunInterval = viewModel.MinimumRunInterval;
}
}
return shapeHelper.EditorTemplate(TemplateName: "Parts.AuditTrailTrimmingSettings", Model: viewModel, Prefix: Prefix);
}).OnGroup("Audit Trail");
}
}
}

View File

@@ -0,0 +1,10 @@
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
namespace Orchard.AuditTrail.Drivers {
public class ContentsDriver : ContentPartDriver<ContentPart> {
protected override DriverResult Display(ContentPart part, string displayType, dynamic shapeHelper) {
return ContentShape("Parts_Contents_AuditTrail_SummaryAdmin", () => shapeHelper.Parts_Contents_AuditTrail_SummaryAdmin());
}
}
}

View File

@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Orchard.AuditTrail.Models;
using Orchard.AuditTrail.Providers.AuditTrail;
using Orchard.AuditTrail.Services;
using Orchard.Caching;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.Localization;
using Orchard.Logging;
namespace Orchard.AuditTrail.Handlers {
public class AuditTrailSettingsPartHandler : ContentHandler {
private readonly ISignals _signals;
private string _oldEventSettings;
private readonly IAuditTrailManager _auditTrailManager;
private readonly IWorkContextAccessor _wca;
public AuditTrailSettingsPartHandler(ISignals signals, IAuditTrailManager auditTrailManager, IWorkContextAccessor wca) {
_signals = signals;
_auditTrailManager = auditTrailManager;
_wca = wca;
Filters.Add(new ActivatingFilter<AuditTrailSettingsPart>("Site"));
OnActivated<AuditTrailSettingsPart>(SetupLazyFields);
OnUpdating<AuditTrailSettingsPart>(BeginUpdateEvent);
OnUpdated<AuditTrailSettingsPart>(EndUpdateEvent);
OnGetContentItemMetadata<AuditTrailSettingsPart>(GetMetadata);
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
private void GetMetadata(GetContentItemMetadataContext context, AuditTrailSettingsPart part) {
context.Metadata.EditorGroupInfo.Add(new GroupInfo(T("Audit Trail")));
}
private void SetupLazyFields(ActivatedContentContext context, AuditTrailSettingsPart part) {
part._eventProviderSettingsField.Loader(() => _auditTrailManager.DeserializeProviderConfiguration(part.Retrieve<string>("Events")));
part._eventProviderSettingsField.Setter(value => {
part.Store("Events", _auditTrailManager.SerializeProviderConfiguration(value));
_signals.Trigger("AuditTrail.EventSettings");
return value;
});
}
private void BeginUpdateEvent(UpdateContentContext context, AuditTrailSettingsPart part) {
_oldEventSettings = part.Retrieve<string>("Events");
}
private void EndUpdateEvent(UpdateContentContext context, AuditTrailSettingsPart part) {
var newEventSettings = part.Retrieve<string>("Events");
if (newEventSettings == _oldEventSettings)
return;
_auditTrailManager.CreateRecord<AuditTrailSettingsEventProvider>(
eventName: AuditTrailSettingsEventProvider.EventsChanged,
eventData: new Dictionary<string, object> {
{"OldSettings", _oldEventSettings},
{"NewSettings", newEventSettings}
},
user: _wca.GetContext().CurrentUser);
}
}
}

View File

@@ -0,0 +1,57 @@
using System.Collections.Generic;
using Orchard.AuditTrail.Models;
using Orchard.AuditTrail.Providers.AuditTrail;
using Orchard.AuditTrail.Services;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.Environment.Extensions;
using Orchard.Localization;
namespace Orchard.AuditTrail.Handlers {
[OrchardFeature("Orchard.AuditTrail.Trimming")]
public class AuditTrailTrimmingSettingsPartHandler : ContentHandler {
private int _oldRetentionPeriod;
private readonly IAuditTrailManager _auditTrailManager;
private readonly IWorkContextAccessor _wca;
private int _oldMinimumRunInterval;
public AuditTrailTrimmingSettingsPartHandler(IAuditTrailManager auditTrailManager, IWorkContextAccessor wca) {
_auditTrailManager = auditTrailManager;
_wca = wca;
Filters.Add(new ActivatingFilter<AuditTrailTrimmingSettingsPart>("Site"));
OnGetContentItemMetadata<AuditTrailTrimmingSettingsPart>(GetMetadata);
OnUpdating<AuditTrailTrimmingSettingsPart>(BeginUpdateEvent);
OnUpdated<AuditTrailTrimmingSettingsPart>(EndUpdateEvent);
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
private void GetMetadata(GetContentItemMetadataContext context, AuditTrailTrimmingSettingsPart part) {
context.Metadata.EditorGroupInfo.Add(new GroupInfo(T("Audit Trail")));
}
private void BeginUpdateEvent(UpdateContentContext context, AuditTrailTrimmingSettingsPart part) {
_oldRetentionPeriod = part.RetentionPeriod;
_oldMinimumRunInterval = part.MinimumRunInterval;
}
private void EndUpdateEvent(UpdateContentContext context, AuditTrailTrimmingSettingsPart part) {
var newRetentionPeriod = part.RetentionPeriod;
var newMinimumRunInterval = part.MinimumRunInterval;
if (newRetentionPeriod == _oldRetentionPeriod && newMinimumRunInterval == _oldMinimumRunInterval)
return;
_auditTrailManager.CreateRecord<AuditTrailTrimmingSettingsEventProvider>(
eventName: AuditTrailTrimmingSettingsEventProvider.TrimmingSettingsChanged,
user: _wca.GetContext().CurrentUser,
eventData: new Dictionary<string, object> {
{"OldRetentionPeriod", _oldRetentionPeriod},
{"NewRetentionPeriod", newRetentionPeriod},
{"OldMinimumRunInterval", _oldMinimumRunInterval},
{"NewMinimumRunInterval", newMinimumRunInterval}
});
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
namespace Orchard.AuditTrail.Helpers {
public static class DateTimeExtensions {
public static DateTime? Earliest(this DateTime? value) {
if (value == null)
return null;
return Earliest(value.Value);
}
public static DateTime Earliest(this DateTime value) {
return new DateTime(value.Year, value.Month, value.Day, 0, 0, 0, 0, value.Kind);
}
public static DateTime? Latest(this DateTime? value) {
if (value == null)
return null;
var v = value.Value;
return new DateTime(v.Year, v.Month, v.Day, 23, 59, 59, 999, v.Kind);
}
public static DateTime Latest(this DateTime value) {
return new DateTime(value.Year, value.Month, value.Day, 23, 59, 59, 999, value.Kind);
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
namespace Orchard.AuditTrail.Helpers {
public static class EventDataExtensions {
public static T Get<T>(this IDictionary<string, object> eventData, string key) {
if (eventData == null || !eventData.ContainsKey(key))
return default(T);
var value = eventData[key];
return (T) Convert.ChangeType(value, typeof (T));
}
}
}

View File

@@ -0,0 +1,19 @@
using System;
using Orchard.AuditTrail.Services;
namespace Orchard.AuditTrail.Helpers {
public static class EventNameExtensions {
public static string GetFullyQualifiedEventName<T>(string eventName) where T : IAuditTrailEventProvider {
return GetFullyQualifiedEventName(typeof(T), eventName);
}
public static string GetFullyQualifiedEventName(Type providerType, string eventName) {
return String.Format("{0}.{1}", providerType.FullName, eventName);
}
public static string GetShortEventName(string fullyQualifiedEventName) {
var index = fullyQualifiedEventName.LastIndexOf('.') + 1;
return fullyQualifiedEventName.Substring(index);
}
}
}

View File

@@ -0,0 +1,12 @@
using Orchard.AuditTrail.Services.Models;
namespace Orchard.AuditTrail.Helpers {
public static class FiltersExtensions {
public static string Get(this Filters filters, string key) {
if (!filters.ContainsKey(key))
return null;
return filters[key];
}
}
}

View File

@@ -0,0 +1,15 @@
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
namespace Orchard.AuditTrail.Helpers {
public static class HtmlExtensions {
public static IHtmlString ContentPartEditLink(this HtmlHelper html, string contentPartName) {
return html.ActionLink(contentPartName, "EditPart", "Admin", new {id = contentPartName, area = "Orchard.ContentTypes"}, null);
}
public static IHtmlString ContentTypeEditLink(this HtmlHelper html, string contentTypeName) {
return html.ActionLink(contentTypeName, "Edit", "Admin", new { id = contentTypeName, area = "Orchard.ContentTypes" }, null);
}
}
}

View File

@@ -0,0 +1,44 @@
using Orchard.AuditTrail.Services.Models;
using Orchard.ContentManagement.MetaData.Models;
namespace Orchard.AuditTrail.Helpers {
public static class SettingsDictionaryExtensions {
public static string Get(this SettingsDictionary settings, string key) {
return settings.ContainsKey(key) ? settings[key] : null;
}
public static DiffDictionary<string, string> GetDiff(this SettingsDictionary oldSettings, SettingsDictionary newSettings) {
var dictionary = new DiffDictionary<string, string>();
BuildDiff(dictionary, newSettings, oldSettings);
BuildDiff(dictionary, oldSettings, newSettings);
return dictionary;
}
private static void BuildDiff(DiffDictionary<string, string> dictionary, SettingsDictionary settingsA, SettingsDictionary settingsB) {
foreach (var settingA in settingsA) {
string oldValue, newValue;
if (settingsB.ContainsKey(settingA.Key)) {
oldValue = settingA.Value;
newValue = settingsB[settingA.Key];
}
else {
oldValue = settingA.Value;
newValue = settingsB[settingA.Key] = default(string);
}
if (oldValue != newValue) {
dictionary[settingA.Key] = new Diff<string> {
NewValue = newValue,
OldValue = oldValue
};
}
}
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
using Orchard.Localization;
namespace Orchard.AuditTrail.Helpers {
public static class StringExtensions {
public static int? ToInt32(this string value) {
if (String.IsNullOrWhiteSpace(value))
return null;
int i;
if(!Int32.TryParse(value, out i))
return null;
return i;
}
public static string OrIfEmpty(this string value, LocalizedString emptyString) {
return String.IsNullOrWhiteSpace(value) ? emptyString.Text : value;
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Xml.Linq;
namespace Orchard.AuditTrail.Helpers {
public static class XmlHelper {
public static XElement Parse(string xml) {
if (String.IsNullOrEmpty(xml))
return null;
try {
return XElement.Parse(xml);
}
catch (Exception) {
return null;
}
}
}
}

View File

@@ -0,0 +1,70 @@
using System;
using System.Linq;
using System.Xml.Linq;
using Orchard.AuditTrail.Models;
using Orchard.Data;
using Orchard.Environment.Extensions;
using Orchard.ImportExport.Services;
namespace Orchard.AuditTrail.ImportExport {
[OrchardFeature("Orchard.AuditTrail.ImportExport")]
public class AuditTrailExportEventHandler : IExportEventHandler {
private readonly IRepository<AuditTrailEventRecord> _auditTrailEventRepository;
public AuditTrailExportEventHandler(IRepository<AuditTrailEventRecord> auditTrailEventRepository) {
_auditTrailEventRepository = auditTrailEventRepository;
}
public void Exporting(ExportContext context) {
}
public void Exported(ExportContext context) {
if (!context.ExportOptions.CustomSteps.Contains("AuditTrail")) {
return;
}
var records = _auditTrailEventRepository.Table.ToList();
if (!records.Any()) {
return;
}
var root = new XElement("AuditTrail");
context.Document.Element("Orchard").Add(root);
foreach (var record in records) {
root.Add(new XElement("Event",
CreateAttribute("Name", record.EventName),
CreateAttribute("FullName", record.FullEventName),
CreateAttribute("Category", record.Category),
CreateAttribute("User", record.UserName),
CreateAttribute("CreatedUtc", record.CreatedUtc),
CreateAttribute("EventFilterKey", record.EventFilterKey),
CreateAttribute("EventFilterData", record.EventFilterData),
CreateElement("Comment", record.Comment),
ParseEventData(record.EventData)));
}
}
private static XElement CreateElement(string name, string value) {
return !String.IsNullOrWhiteSpace(value) ? new XElement(name, value) : null;
}
private static XAttribute CreateAttribute(string name, string value) {
return !String.IsNullOrWhiteSpace(value) ? new XAttribute(name, value) : null;
}
private static XAttribute CreateAttribute(string name, object value) {
return new XAttribute(name, value);
}
private static XElement ParseEventData(string eventData) {
if(String.IsNullOrWhiteSpace(eventData))
return new XElement("EventData");
return XElement.Parse(eventData);
}
}
}

View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
using Orchard.Environment.Extensions;
using Orchard.ImportExport.Services;
namespace Orchard.AuditTrail.ImportExport {
[OrchardFeature("Orchard.AuditTrail.ImportExport")]
public class AuditTrailExportStep : ICustomExportStep {
public void Register(IList<string> steps) {
steps.Add("AuditTrail");
}
}
}

View File

@@ -0,0 +1,54 @@
using System;
using Orchard.AuditTrail.Models;
using Orchard.ContentManagement;
using Orchard.Data;
using Orchard.Environment.Extensions;
using Orchard.Logging;
using Orchard.Recipes.Models;
using Orchard.Recipes.Services;
using Orchard.Security;
namespace Orchard.AuditTrail.ImportExport {
[OrchardFeature("Orchard.AuditTrail.ImportExport")]
public class AuditTrailImportHandler : Component, IRecipeHandler {
private readonly IRepository<AuditTrailEventRecord> _auditTrailEventRepository;
private readonly IAuthorizer _authorizer;
private readonly IWorkContextAccessor _wca;
public AuditTrailImportHandler(IRepository<AuditTrailEventRecord> auditTrailEventRepository, IAuthorizer authorizer, IWorkContextAccessor wca) {
_auditTrailEventRepository = auditTrailEventRepository;
_authorizer = authorizer;
_wca = wca;
}
public void ExecuteRecipeStep(RecipeContext recipeContext) {
if (!String.Equals(recipeContext.RecipeStep.Name, "AuditTrail", StringComparison.OrdinalIgnoreCase)) {
return;
}
if (!_authorizer.Authorize(Permissions.ImportAuditTrail)) {
Logger.Warning("Blocked {0} from importing an audit trail because this user does not have the ImportauditTrail permission.", _wca.GetContext().CurrentUser.UserName);
recipeContext.Executed = false;
return;
}
foreach (var eventElement in recipeContext.RecipeStep.Step.Elements()) {
var record = new AuditTrailEventRecord {
EventName = eventElement.Attr<string>("Name"),
FullEventName = eventElement.Attr<string>("FullName"),
Category = eventElement.Attr<string>("Category"),
UserName = eventElement.Attr<string>("User"),
CreatedUtc = eventElement.Attr<DateTime>("CreatedUtc"),
EventFilterKey = eventElement.Attr<string>("EventFilterKey"),
EventFilterData = eventElement.Attr<string>("EventFilterData"),
Comment = eventElement.El("Comment"),
EventData = eventElement.Element("EventData").ToString(),
};
_auditTrailEventRepository.Create(record);
}
recipeContext.Executed = true;
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
namespace Orchard.AuditTrail {
public class Migrations : DataMigrationImpl {
public int Create() {
SchemaBuilder.CreateTable("AuditTrailEventRecord", table => table
.Column<int>("Id", c => c.PrimaryKey().Identity())
.Column<DateTime>("CreatedUtc")
.Column<string>("UserName", c => c.WithLength(64))
.Column<string>("EventName", c => c.WithLength(128))
.Column<string>("FullEventName", c => c.WithLength(512))
.Column<string>("Category", c => c.WithLength(64))
.Column<int>("ContentItemVersion_Id")
.Column<string>("EventData", c => c.Unlimited())
.Column<string>("EventFilterKey", c => c.WithLength(16))
.Column<string>("EventFilterData", c => c.WithLength(256))
.Column<string>("Comment", c => c.Unlimited()));
ContentDefinitionManager.AlterPartDefinition("AuditTrailPart", part => part
.Attachable()
.WithDescription("Enables the user to enter a comment about the change when saving a content item."));
return 1;
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
using Orchard.Data.Conventions;
namespace Orchard.AuditTrail.Models {
public class AuditTrailEventRecord {
public virtual int Id { get; set; }
public virtual DateTime CreatedUtc { get; set; }
public virtual string UserName { get; set; }
public virtual string EventName { get; set; }
public virtual string FullEventName { get; set; }
public virtual string Category { get; set; }
[StringLengthMax]
public virtual string EventData { get; set; }
public virtual string EventFilterKey { get; set; }
public virtual string EventFilterData { get; set; }
[StringLengthMax]
public virtual string Comment { get; set; }
}
}

View File

@@ -0,0 +1,6 @@
namespace Orchard.AuditTrail.Models {
public class AuditTrailEventRecordResult {
public AuditTrailEventRecord Record { get; set; }
public bool IsDisabled { get; set; }
}
}

View File

@@ -0,0 +1,6 @@
namespace Orchard.AuditTrail.Models {
public class AuditTrailEventSetting {
public string EventName { get; set; }
public bool IsEnabled { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
using Orchard.ContentManagement;
namespace Orchard.AuditTrail.Models {
public class AuditTrailPart : ContentPart {
public string Comment {
get { return RetrieveVersioned<string>("Comment"); }
set { StoreVersioned("Comment", value); }
}
public bool ShowComment { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
using System.Collections.Generic;
using Orchard.ContentManagement;
using Orchard.Core.Common.Utilities;
namespace Orchard.AuditTrail.Models {
public class AuditTrailSettingsPart : ContentPart {
internal LazyField<IEnumerable<AuditTrailEventSetting>> _eventProviderSettingsField = new LazyField<IEnumerable<AuditTrailEventSetting>>();
public IEnumerable<AuditTrailEventSetting> EventSettings {
get { return _eventProviderSettingsField.Value; }
set { _eventProviderSettingsField.Value = value; }
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using Orchard.ContentManagement;
namespace Orchard.AuditTrail.Models {
public class AuditTrailTrimmingSettingsPart : ContentPart {
/// <summary>
/// Gets or sets the retention period in days of audit trail records before they are deleted.
/// </summary>
public int RetentionPeriod {
get { return this.Retrieve(x => x.RetentionPeriod, defaultValue: 10); }
set { this.Store(x => x.RetentionPeriod, value); }
}
/// <summary>
/// Gets or sets the miminum wait time in hours between audit trail trimming runs.
/// </summary>
public int MinimumRunInterval {
get { return this.Retrieve(x => x.MinimumRunInterval, defaultValue: 12); }
set { this.Store(x => x.MinimumRunInterval, value); }
}
/// <summary>
/// Gets or sets the time in UTC at which the audit trail was last trimmed.
/// </summary>
public DateTime? LastRunUtc {
get { return this.Retrieve(x => x.LastRunUtc); }
set { this.Store(x => x.LastRunUtc, value); }
}
}
}

View File

@@ -0,0 +1,37 @@
Name: Audit Trail
AntiForgery: enabled
Author: The Orchard Team
Website: http://orchardproject.net
Version: 1.8.1
OrchardVersion: 1.8
Description: Provides a log for recording and viewing back-end changes.
Features:
Orchard.AuditTrail:
Name: Audit Trail
Description: Provides a log for recording and viewing back-end changes.
Category: Security
Orchard.AuditTrail.ImportExport:
Name: Audit Trail Import Export
Description: Provides import/export functionality for the Audit Trail feature.
Category: Security
Dependencies: Orchard.AuditTrail, Orchard.ImportExport
Orchard.AuditTrail.Trimming:
Name: Audit Trail Trimming
Description: Provides a background task that regularly deletes old audit trail records.
Category: Security
Dependencies: Orchard.AuditTrail, Orchard.TaskLease
Orchard.AuditTrail.Users:
Name: Audit Trail User Events
Description: Provides audit trail support for user related events.
Category: Security
Dependencies: Orchard.AuditTrail, Orchard.Users
Orchard.AuditTrail.Roles:
Name: Audit Trail Role Events
Description: Provides audit trail support for role related events.
Category: Security
Dependencies: Orchard.AuditTrail, Orchard.Roles
Orchard.AuditTrail.ContentDefinition:
Name: Audit Trail Content Definition Events
Description: Provides audit trail support for content definition related events.
Category: Security
Dependencies: Orchard.AuditTrail, Orchard.ContentTypes

View File

@@ -0,0 +1,333 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{3DD574CD-9C5D-4A45-85E1-EBBA64C22B5F}</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Orchard.AuditTrail</RootNamespace>
<AssemblyName>Orchard.AuditTrail</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<MvcBuildViews>false</MvcBuildViews>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>4.0</OldToolsVersion>
<UpgradeBackupLocation />
<TargetFrameworkProfile />
<UseIISExpress>false</UseIISExpress>
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\lib\newtonsoft.json\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.ComponentModel.DataAnnotations">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Mvc, Version=5.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Web.Abstractions" />
<Reference Include="System.Web.Routing" />
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Xml.Linq" />
<Reference Include="xmldiffpatch, Version=1.0.8.28, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<SpecificVersion>False</SpecificVersion>
<HintPath>Lib\XmlDiffPatch\xmldiffpatch.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<None Include="Placement.info" />
<Content Include="Lib\XmlDiffPatch\xmldiffpatch.dll" />
<Content Include="Recipes\audit-trail.recipe.xml" />
<Content Include="Scripts\audittrail-expando.js" />
<Content Include="Scripts\audittrail-checkall.js" />
<Content Include="Scripts\audittrail-disabledcontent.js" />
<Content Include="Styles\audittrail-content-event.css" />
<Content Include="Styles\audittrail-display.css" />
<Content Include="Styles\audittrail-disabledcontent.css" />
<Content Include="Styles\audittrail-part.css" />
<Content Include="Styles\audittrail-contentdefinition-event.css" />
<Content Include="Styles\audittrail-settings-event.css" />
<Content Include="Styles\audittrail-settings.css" />
<Content Include="Styles\menu.audit-trail-admin.css" />
<Content Include="Web.config" />
<Content Include="Scripts\Web.config" />
<Content Include="Styles\Web.config" />
<Content Include="Properties\AssemblyInfo.cs" />
<Content Include="Module.txt" />
<Content Include="Views\Parts.Contents.AuditTrail.SummaryAdmin.cshtml" />
<Content Include="Views\DefinitionTemplates\AuditTrailPartSettings.cshtml" />
<Content Include="Views\Parts.AuditTrail.Link.cshtml" />
<Content Include="Views\Parts.AuditTrail.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentType-PartSettingsUpdated.cshtml" />
<Content Include="Views\EditorTemplates\Parts.AuditTrailTrimmingSettings.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentPart-Created.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentPart-FieldAdded.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentPart-FieldRemoved.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentPart-FieldSettingsUpdated.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentPart-PartSettingsUpdated.SummaryAdmin.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentPart-Removed.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentType-Created.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentType-PartAdded.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentType-PartRemoved.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentType-Removed.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentType-TypeSettingsUpdated.SummaryAdmin.cshtml" />
<Content Include="Views\AuditTrailEvent-Role-PermissionAdded.cshtml" />
<Content Include="Views\AuditTrailEvent-Role-PermissionRemoved.cshtml" />
<Content Include="Views\AuditTrailEvent-Role-Created.cshtml" />
<Content Include="Views\AuditTrailEvent-Role-Renamed.cshtml" />
<Content Include="Views\AuditTrailEvent-User.cshtml" />
<Content Include="Views\EditorTemplates\Parts.AuditTrailSettings.cshtml" />
<Content Include="Views\AuditTrailEvent-User-LoginFailed..cshtml" />
<Content Include="Views\AuditTrailFilter-Common-User.cshtml" />
<Content Include="Views\AuditTrailFilter-ContentType.cshtml" />
<Content Include="Views\AuditTrailFilter-Common-Date-From.cshtml" />
<Content Include="Views\AuditTrailFilter-ContentItem.cshtml" />
<Content Include="Views\AuditTrailFilter-Common-Category.cshtml" />
<Content Include="Views\Content\Detail.cshtml" />
<Content Include="Views\AuditTrailEvent-Content.cshtml">
<SubType>Code</SubType>
</Content>
<Content Include="Views\AuditTrailFilter.cshtml" />
<Content Include="Views\AuditTrailFilter-Common-Date-To.cshtml" />
<Content Include="Views\AuditTrailEvent-AuditTrailSettings-EventsChanged.SummaryAdmin.cshtml" />
<Content Include="Views\AuditTrailEvent-AuditTrailSettings-EventsChanged.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentType-TypeDisplayNameUpdated.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentType-TypeSettingsUpdated.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentType-PartSettingsUpdated.SummaryAdmin.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentType-FieldSettingsUpdated.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentType-FieldSettingsUpdated.SummaryAdmin.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentPart-PartSettingsUpdated.cshtml" />
<Content Include="Views\AuditTrailEvent-Role-Removed.cshtml" />
<Content Include="Views\AuditTrailEvent-Role-UserAdded.cshtml" />
<Content Include="Views\AuditTrailEvent-Role-UserRemoved.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentPart-DescriptionChanged.cshtml" />
<Content Include="Views\AuditTrailEvent-ContentPart-FieldSettingsUpdated.SummaryAdmin.cshtml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
<Name>Orchard.Framework</Name>
</ProjectReference>
<ProjectReference Include="..\..\Core\Orchard.Core.csproj">
<Project>{9916839C-39FC-4CEB-A5AF-89CA7E87119F}</Project>
<Name>Orchard.Core</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.ContentTypes\Orchard.ContentTypes.csproj">
<Project>{0e7646e8-fe8f-43c1-8799-d97860925ec4}</Project>
<Name>Orchard.ContentTypes</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.ImportExport\Orchard.ImportExport.csproj">
<Project>{fe5c5947-d2d5-42c5-992a-13d672946135}</Project>
<Name>Orchard.ImportExport</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Roles\Orchard.Roles.csproj">
<Project>{d10ad48f-407d-4db5-a328-173ec7cb010f}</Project>
<Name>Orchard.Roles</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.TaskLease\Orchard.TaskLease.csproj">
<Project>{3f72a4e9-7b72-4260-b010-c16ec54f9baf}</Project>
<Name>Orchard.TaskLease</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Users\Orchard.Users.csproj">
<Project>{79aed36e-abd0-4747-93d3-8722b042454b}</Project>
<Name>Orchard.Users</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="AdminMenu.cs" />
<Compile Include="Controllers\ContentController.cs" />
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="Drivers\AuditTrailTrimmingSettingsPartDriver.cs" />
<Compile Include="Drivers\AuditTrailSettingsPartDriver.cs" />
<Compile Include="Handlers\AuditTrailTrimmingSettingsPartHandler.cs" />
<Compile Include="Helpers\FiltersExtensions.cs" />
<Compile Include="Helpers\DateTimeExtensions.cs" />
<Compile Include="Helpers\SettingsDictionaryExtensions.cs" />
<Compile Include="Helpers\HtmlExtensions.cs" />
<Compile Include="Helpers\XmlHelper.cs" />
<Compile Include="Helpers\StringExtensions.cs" />
<Compile Include="ImportExport\AuditTrailExportStep.cs" />
<Compile Include="ImportExport\AuditTrailExportHandler.cs" />
<Compile Include="ImportExport\AuditTrailImportHandler.cs" />
<Compile Include="Models\AuditTrailEventRecordResult.cs" />
<Compile Include="Models\AuditTrailTrimmingSettingsPart.cs" />
<Compile Include="Providers\AuditTrail\AuditTrailTrimmingSettingsEventProvider.cs" />
<Compile Include="Providers\AuditTrail\AuditTrailSettingsEventProvider.cs" />
<Compile Include="Services\Models\AuditTrailSettingsContext.cs" />
<Compile Include="Services\CommonAuditTrailEventHandler.cs" />
<Compile Include="Services\AuditTrailEventHandlerBase.cs" />
<Compile Include="Services\IAuditTrailSettingsEventHandler.cs" />
<Compile Include="Services\Models\DisplayFilterContext.cs" />
<Compile Include="Services\Models\QueryFilterContext.cs" />
<Compile Include="Providers\Content\ContentAuditTrailEventShapes.cs" />
<Compile Include="Providers\Content\DiffGramAnalyzer.cs" />
<Compile Include="Providers\Content\DiffNode.cs" />
<Compile Include="Providers\Content\DiffType.cs" />
<Compile Include="Services\Models\Filters.cs" />
<Compile Include="Providers\Content\IDiffGramAnalyzer.cs" />
<Compile Include="Services\AuditTrailTrimmingBackgroundTask.cs" />
<Compile Include="Providers\AuditTrail\AuditTrailFilterShapes.cs" />
<Compile Include="Shapes\AuditTrailEventShapeAlteration.cs" />
<Compile Include="Providers\ContentDefinition\Shapes\ContentTypeFieldSettingsUpdatedEventShape.cs" />
<Compile Include="Providers\ContentDefinition\Shapes\ContentPartSettingsUpdatedEventShape.cs" />
<Compile Include="Providers\ContentDefinition\Shapes\ContentTypePartSettingsUpdatedEventShape.cs" />
<Compile Include="Providers\ContentDefinition\Shapes\ContentTypeSettingsUpdatedEventShape.cs" />
<Compile Include="Providers\AuditTrail\AuditTrailSettingsEventShape.cs" />
<Compile Include="Services\Models\Diff.cs" />
<Compile Include="Services\Models\DiffDictionary.cs" />
<Compile Include="ViewModels\AuditTrailCategorySettingsViewModel.cs" />
<Compile Include="ViewModels\AuditTrailEventDescriptorSettingViewModel.cs" />
<Compile Include="ViewModels\AuditTrailEventSettingsViewModel.cs" />
<Compile Include="ViewModels\AuditTrailTrimmingSettingsViewModel.cs" />
<Compile Include="ViewModels\AuditTrailSettingsViewModel.cs" />
<Compile Include="Drivers\ContentsDriver.cs" />
<Compile Include="Handlers\AuditTrailSettingsPartHandler.cs" />
<Compile Include="Models\AuditTrailEventSetting.cs" />
<Compile Include="Models\AuditTrailSettingsPart.cs" />
<Compile Include="Providers\ContentDefinition\ContentDefinitionEventHandler.cs" />
<Compile Include="Providers\ContentDefinition\GlobalContentDefinitionEditorEvents.cs" />
<Compile Include="Providers\ContentDefinition\ContentPartAuditTrailEventProvider.cs" />
<Compile Include="Providers\ContentDefinition\ContentTypeAuditTrailEventProvider.cs" />
<Compile Include="Providers\Content\AuditTrailEventHandler.cs" />
<Compile Include="Helpers\EventDataExtensions.cs" />
<Compile Include="Services\Models\AuditTrailFilterParameters.cs" />
<Compile Include="Providers\Roles\RoleEventHandler.cs" />
<Compile Include="Providers\Roles\RoleAuditTrailEventProvider.cs" />
<Compile Include="Providers\Users\UserAuditTrailEventProvider.cs" />
<Compile Include="Providers\Users\UserEventHandler.cs" />
<Compile Include="Settings\AuditTrailPartSettings.cs" />
<Compile Include="Settings\AuditTrailPartSettingsEvents.cs" />
<Compile Include="ViewModels\AuditTrailEventSummaryViewModel.cs" />
<Compile Include="Drivers\AuditTrailPartDriver.cs" />
<Compile Include="Helpers\EventNameExtensions.cs" />
<Compile Include="Models\AuditTrailPart.cs" />
<Compile Include="Services\AuditTrailEventDisplayBuilder.cs" />
<Compile Include="Services\EventDataSerializer.cs" />
<Compile Include="Services\IAuditTrailEventDisplayBuilder.cs" />
<Compile Include="Services\IEventDataSerializer.cs" />
<Compile Include="ViewModels\AuditTrailDetailsViewModel.cs" />
<Compile Include="Providers\Content\GlobalContentHandler.cs" />
<Compile Include="Services\Models\AuditTrailEventDescriptor.cs" />
<Compile Include="Services\Models\AuditTrailCategoryDescriptor.cs" />
<Compile Include="Services\Models\DescribeContext.cs" />
<Compile Include="Services\Models\DescribeFor.cs" />
<Compile Include="Services\Models\AuditTrailContext.cs" />
<Compile Include="Services\Models\AuditTrailCreateContext.cs" />
<Compile Include="Services\AuditTrailEventProviderBase.cs" />
<Compile Include="Providers\Content\ContentAuditTrailEventProvider.cs" />
<Compile Include="Services\IAuditTrailEventHandler.cs" />
<Compile Include="Services\IAuditTrailEventProvider.cs" />
<Compile Include="ViewModels\AuditTrailCommentViewModel.cs" />
<Compile Include="ViewModels\CommonAuditTrailFilterViewModel.cs" />
<Compile Include="Services\Models\AuditTrailOrderBy.cs" />
<Compile Include="ViewModels\AuditTrailViewModel.cs" />
<Compile Include="Migrations.cs" />
<Compile Include="Models\AuditTrailEventRecord.cs" />
<Compile Include="Permissions.cs" />
<Compile Include="Services\AuditTrailManager.cs" />
<Compile Include="Services\IAuditTrailManager.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Admin\Detail.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Admin\Index.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\AuditTrailEvent.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\AuditTrailEvent-Content.SummaryAdmin.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\AuditTrailEvent-AuditTrailSettings-TrimmingSettingsChanged.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\EditorTemplates\Parts.AuditTrail.Comment.cshtml" />
</ItemGroup>
<ItemGroup />
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target> -->
<Target Name="AfterBuild" DependsOnTargets="AfterBuildCompiler">
<PropertyGroup>
<AreasManifestDir>$(ProjectDir)\..\Manifests</AreasManifestDir>
</PropertyGroup>
<!-- If this is an area child project, uncomment the following line:
<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Child" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
-->
<!-- If this is an area parent project, uncomment the following lines:
<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Parent" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
<CopyAreaManifests ManifestPath="$(AreasManifestDir)" CrossCopy="false" RenameViews="true" />
-->
</Target>
<Target Name="AfterBuildCompiler" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(ProjectDir)\..\$(ProjectName)" />
</Target>
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>False</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>45979</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>
</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>True</UseCustomServer>
<CustomServerUrl>http://orchard.codeplex.com</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>

View File

@@ -0,0 +1,30 @@
using System.Collections.Generic;
using Orchard.Environment.Extensions.Models;
using Orchard.Security.Permissions;
namespace Orchard.AuditTrail {
public class Permissions : IPermissionProvider {
public static readonly Permission ViewAuditTrail = new Permission { Description = "View audit trail", Name = "ViewAuditTrail" };
public static readonly Permission ManageAuditTrailSettings = new Permission { Description = "Manage audit trail settings", Name = "ManageAuditTrailSettings" };
public static readonly Permission ImportAuditTrail = new Permission { Description = "Import audit trail", Name = "ImportAuditTrail" };
public virtual Feature Feature { get; set; }
public IEnumerable<Permission> GetPermissions() {
yield return ViewAuditTrail;
yield return ManageAuditTrailSettings;
yield return ImportAuditTrail;
}
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {
yield return new PermissionStereotype {
Name = "Administrator",
Permissions = new[] {
ViewAuditTrail,
ManageAuditTrailSettings,
/* Not even an administrator will get the ImportAuditTrail permission. */
}
};
}
}
}

View File

@@ -0,0 +1,11 @@
<Placement>
<Place Parts_AuditTrail_Comment="Content:after.1"
Parts_AuditTrail_Link="Content:after.2"
Parts_AuditTrail="Content:after.3"
Parts_AuditTrailSettings_Edit="Content:0"
Parts_AuditTrailTrimmingSettings_Edit="Content:1"/>
<Match DisplayType="SummaryAdmin">
<Place Parts_Contents_AuditTrail_SummaryAdmin="Actions:7"/>
</Match>
</Placement>

View File

@@ -0,0 +1,37 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Orchard.AuditTrail")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("Orchard")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c5863d1d-f836-4ce8-8791-027ba14c6815")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,17 @@
using System.IO;
using Orchard.DisplayManagement;
namespace Orchard.AuditTrail.Providers.AuditTrail {
public class AuditTrailFilterShapes : IDependency {
[Shape]
public void AuditTrailFilterDisplay(dynamic Shape, dynamic Display, TextWriter Output) {
DispayChildren(Shape, Display, Output);
}
private void DispayChildren(dynamic shape, dynamic display, TextWriter output) {
foreach (var child in shape) {
output.Write(display(child));
}
}
}
}

View File

@@ -0,0 +1,14 @@
using Orchard.AuditTrail.Services;
using Orchard.AuditTrail.Services.Models;
namespace Orchard.AuditTrail.Providers.AuditTrail {
public class AuditTrailSettingsEventProvider : AuditTrailEventProviderBase {
public const string EventsChanged = "EventsChanged";
public override void Describe(DescribeContext context) {
context.For("AuditTrailSettings", T("Audit Trail Settings"))
.Event(this, EventsChanged, T("Events changed"), T("The audit trail event settings were changed."), enableByDefault: true, isMandatory: true);
}
}
}

View File

@@ -0,0 +1,46 @@
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;
namespace Orchard.AuditTrail.Providers.AuditTrail {
public class AuditTrailSettingsEventShape : AuditTrailEventShapeAlteration<AuditTrailSettingsEventProvider> {
private readonly Work<IAuditTrailManager> _auditTrailManager;
public AuditTrailSettingsEventShape(Work<IAuditTrailManager> auditTrailManager) {
_auditTrailManager = auditTrailManager;
}
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 diff = GetDiffQuery(oldSettings, newSettings).ToArray();
context.Shape.OldSettings = oldSettings;
context.Shape.NewSettings = newSettings;
context.Shape.Diff = diff;
}
private IEnumerable<AuditTrailEventDescriptorSettingViewModel> GetDiffQuery(IEnumerable<AuditTrailEventSetting> oldSettings, IEnumerable<AuditTrailEventSetting> newSettings) {
var oldDictionary = oldSettings.ToDictionary(x => x.EventName);
return
from newSetting in newSettings
let oldSetting = oldDictionary.ContainsKey(newSetting.EventName) ? oldDictionary[newSetting.EventName] : default(AuditTrailEventSetting)
where oldSetting == null || oldSetting.IsEnabled != newSetting.IsEnabled
select new AuditTrailEventDescriptorSettingViewModel {
Setting = newSetting,
Descriptor = _auditTrailManager.Value.DescribeEvent(newSetting.EventName)
};
}
}
}

View File

@@ -0,0 +1,16 @@
using Orchard.AuditTrail.Services;
using Orchard.AuditTrail.Services.Models;
using Orchard.Environment.Extensions;
namespace Orchard.AuditTrail.Providers.AuditTrail {
[OrchardFeature("Orchard.AuditTrail.Trimming")]
public class AuditTrailTrimmingSettingsEventProvider : AuditTrailEventProviderBase {
public const string TrimmingSettingsChanged = "TrimmingSettingsChanged";
public override void Describe(DescribeContext context) {
context.For("AuditTrailSettings", T("Audit Trail Settings"))
.Event(this, TrimmingSettingsChanged, T("Trimming settings changed"), T("The audit trail trimming settings were changed."), enableByDefault: true);
}
}
}

View File

@@ -0,0 +1,18 @@
using Orchard.AuditTrail.Models;
using Orchard.AuditTrail.Services;
using Orchard.AuditTrail.Services.Models;
using Orchard.ContentManagement;
namespace Orchard.AuditTrail.Providers.Content {
public class ContentAuditTrailEventHandler : AuditTrailEventHandlerBase {
public override void Create(AuditTrailCreateContext context) {
var content = context.Properties.ContainsKey("Content") ? (IContent)context.Properties["Content"] : default(IContent);
var auditTrailPart = content != null ? content.As<AuditTrailPart>() : default(AuditTrailPart);
if (auditTrailPart == null)
return;
context.Comment = auditTrailPart.Comment;
}
}
}

View File

@@ -0,0 +1,57 @@
using System.Globalization;
using System.Linq;
using Orchard.AuditTrail.Helpers;
using Orchard.AuditTrail.Services;
using Orchard.AuditTrail.Services.Models;
using Orchard.ContentManagement;
namespace Orchard.AuditTrail.Providers.Content {
public class ContentAuditTrailEventProvider : AuditTrailEventProviderBase {
private readonly IContentManager _contentManager;
public ContentAuditTrailEventProvider(IContentManager contentManager) {
_contentManager = contentManager;
}
public const string Created = "Created";
public const string Saved = "Saved";
public const string Published = "Published";
public const string Unpublished = "Unpublished";
public const string Removed = "Removed";
public static Filters CreateFilters(int contentId, IUpdateModel updateModel) {
return new Filters(updateModel) {
{"content", contentId.ToString(CultureInfo.InvariantCulture)}
};
}
public override void Describe(DescribeContext context) {
context.For("Content", T("Content Items"))
.Event(this, Created, T("Created"), T("A content item was created."), enableByDefault: true)
.Event(this, Saved, T("Saved"), T("A content item was saved."), enableByDefault: true)
.Event(this, Published, T("Published"), T("A content item was published."), enableByDefault: true)
.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);
context.QueryFilter(QueryFilter);
context.DisplayFilter(DisplayFilter);
}
private void QueryFilter(QueryFilterContext context) {
if (!context.Filters.ContainsKey("content"))
return;
var contentId = context.Filters["content"].ToInt32();
context.Query = context.Query.Where(x => x.EventFilterKey == "content" && x.EventFilterData == contentId.ToString());
}
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 filterDisplay = context.ShapeFactory.AuditTrailFilter__ContentItem(ContentItem: contentItem);
context.FilterDisplay.Add(filterDisplay);
}
}
}
}

View File

@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Orchard.AuditTrail.Helpers;
using Orchard.AuditTrail.Models;
using Orchard.ContentManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.Environment;
namespace Orchard.AuditTrail.Providers.Content {
public class ContentAuditTrailEventShapes : IShapeTableProvider {
private readonly Work<IContentManager> _contentManager;
private readonly IDiffGramAnalyzer _analyzer;
public ContentAuditTrailEventShapes(Work<IContentManager> contentManager, IDiffGramAnalyzer analyzer) {
_contentManager = contentManager;
_analyzer = analyzer;
}
public void Discover(ShapeTableBuilder builder) {
builder.Describe("AuditTrailEvent").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 previousVersionXml = GetXml(eventData, "PreviousVersionXml");
var diffGram = GetXml(eventData, "DiffGram");
var contentItem = _contentManager.Value.Get(contentItemId, VersionOptions.Latest);
var previousVersion = previousContentItemVersionId > 0 ? _contentManager.Value.Get(contentItemId, VersionOptions.VersionRecord(previousContentItemVersionId)) : default(ContentItem);
if (diffGram != null) {
var diffNodes = _analyzer.Analyze(previousVersionXml, diffGram).ToArray();
context.Shape.DiffNodes = diffNodes;
}
context.Shape.ContentItem = contentItem;
context.Shape.PreviousVersion = previousVersion;
});
}
private static XElement GetXml(IDictionary<string, object> eventData, string key) {
var data = eventData.Get<string>(key);
if (String.IsNullOrWhiteSpace(data))
return null;
try {
return XElement.Parse(data);
}
catch (Exception) {
return null;
}
}
}
}

View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Microsoft.XmlDiffPatch;
namespace Orchard.AuditTrail.Providers.Content {
public class DiffGramAnalyzer : IDiffGramAnalyzer {
public XElement GenerateDiffGram(XElement element1, XElement element2) {
using (var node1Reader = element1.CreateReader())
using (var node2Reader = element2.CreateReader()) {
var result = new XDocument();
using (var writer = result.CreateWriter()) {
var diff = new XmlDiff(XmlDiffOptions.IgnoreChildOrder | XmlDiffOptions.IgnoreWhitespace | XmlDiffOptions.IgnoreComments | XmlDiffOptions.IgnoreXmlDecl);
diff.Compare(node1Reader, node2Reader, writer);
writer.Flush();
writer.Close();
}
return result.Root;
}
}
public IEnumerable<DiffNode> Analyze(XElement original, XElement diffGram) {
var stack = new Stack<XElement>();
stack.Push(new XElement("original", original));
using (var reader = diffGram.CreateReader()) {
while (!reader.EOF) {
var readNext = true;
switch (reader.NodeType) {
case XmlNodeType.Element:
var match = reader.GetAttribute("match");
var isAttributeChange = match != null && match.StartsWith("@");
var index = match == null || isAttributeChange ? default(int?) : Int32.Parse(match) - 1;
var diffType = reader.LocalName;
var currentElement = stack.Peek();
if (currentElement.HasElements && index != null) {
var sourceElement = currentElement.Elements().ElementAt(index.Value);
stack.Push(sourceElement);
}
if (diffType != "node") {
switch (diffType) {
case "change":
if (isAttributeChange) {
var attributeName = match.Substring(1);
var originalValue = currentElement.Attribute(attributeName).Value;
var currentValue = reader.ReadElementContentAsString();
readNext = false;
yield return
new DiffNode {
Type = DiffType.Change,
Context = BuildContextName(stack, attributeName),
Previous = originalValue,
Current = currentValue
};
}
else {
var elementName = currentElement.Name.ToString();
var originalContent = currentElement.Value;
var currentContent = reader.ReadElementContentAsString();
stack.Pop();
readNext = false;
yield return
new DiffNode {
Type = DiffType.Change,
Context = BuildContextName(stack, elementName),
Previous = originalContent,
Current = currentContent
};
}
break;
case "add":
var nodeName = reader.GetAttribute("name");
var addedContent = default(string);
reader.Read();
if (reader.NodeType != XmlNodeType.EndElement) {
nodeName = reader.Name;
addedContent = reader.ReadOuterXml();
}
yield return
new DiffNode {
Type = DiffType.Addition,
Context = BuildContextName(stack, nodeName),
Current = addedContent
};
break;
}
}
break;
case XmlNodeType.EndElement:
stack.Pop();
break;
}
if (readNext)
reader.Read();
}
}
}
private string BuildContextName(IEnumerable<XElement> stack, string nodeName) {
return String.Format("{0}/{1}", String.Join("/", stack.Reverse().Skip(1).Select(x => x.Name)), nodeName);
}
}
}

View File

@@ -0,0 +1,8 @@
namespace Orchard.AuditTrail.Providers.Content {
public class DiffNode {
public DiffType Type { get; set; }
public string Context { get; set; }
public string Previous { get; set; }
public string Current { get; set; }
}
}

View File

@@ -0,0 +1,6 @@
namespace Orchard.AuditTrail.Providers.Content {
public enum DiffType {
Change,
Addition
}
}

View File

@@ -0,0 +1,93 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using Orchard.AuditTrail.Services;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.ContentManagement.Records;
namespace Orchard.AuditTrail.Providers.Content {
public class GlobalContentHandler : ContentHandler {
private readonly IAuditTrailManager _auditTrailManager;
private readonly IWorkContextAccessor _wca;
private readonly IContentManager _contentManager;
private XElement _previousVersionXml;
private readonly IDiffGramAnalyzer _analyzer;
public GlobalContentHandler(IAuditTrailManager auditTrailManager, IWorkContextAccessor wca, IContentManager contentManager, IDiffGramAnalyzer analyzer) {
_auditTrailManager = auditTrailManager;
_wca = wca;
_contentManager = contentManager;
_analyzer = analyzer;
}
protected override void Created(CreateContentContext context) {
RecordAuditTrailEvent(ContentAuditTrailEventProvider.Created, context.ContentItem);
}
protected override void Updating(UpdateContentContext context) {
var contentItem = context.ContentItem;
_previousVersionXml = _contentManager.Export(contentItem);
}
protected override void Updated(UpdateContentContext context) {
var contentItem = context.ContentItem;
var newVersionXml = _contentManager.Export(contentItem);
var diffGram = _analyzer.GenerateDiffGram(_previousVersionXml, newVersionXml);
RecordAuditTrailEvent(ContentAuditTrailEventProvider.Saved, context.ContentItem, diffGram: diffGram, previousVersionXml: _previousVersionXml);
}
protected override void Published(PublishContentContext context) {
var previousVersion = context.PreviousItemVersionRecord;
RecordAuditTrailEvent(ContentAuditTrailEventProvider.Published, context.ContentItem, previousVersion);
}
protected override void Unpublished(PublishContentContext context) {
RecordAuditTrailEvent(ContentAuditTrailEventProvider.Unpublished, context.ContentItem);
}
protected override void Removed(RemoveContentContext context) {
RecordAuditTrailEvent(ContentAuditTrailEventProvider.Removed, context.ContentItem);
}
private void RecordAuditTrailEvent(string eventName, IContent content, ContentItemVersionRecord previousContentItemVersion = null, XElement diffGram = null, XElement previousVersionXml = null) {
var blackList = new[] {"Site"};
if (blackList.Contains(content.ContentItem.ContentType))
return;
var properties = new Dictionary<string, object> {
{"Content", content}
};
var eventData = new Dictionary<string, object> {
{"ContentId", content.Id},
{"ContentIdentity", _contentManager.GetItemMetadata(content).Identity.ToString()},
{"ContentType", content.ContentItem.ContentType},
{"VersionId", content.ContentItem.VersionRecord.Id},
{"VersionNumber", content.ContentItem.VersionRecord.Number},
{"Published", content.ContentItem.VersionRecord.Published},
};
if (previousContentItemVersion != null) {
eventData["PreviousVersionId"] = previousContentItemVersion.Id;
eventData["PreviousVersionNumber"] = previousContentItemVersion.Number;
}
if (diffGram != null && previousVersionXml != null) {
eventData["PreviousVersionXml"] = previousVersionXml.ToString(SaveOptions.DisableFormatting);
eventData["DiffGram"] = diffGram.ToString(SaveOptions.DisableFormatting);
}
_auditTrailManager.CreateRecord<ContentAuditTrailEventProvider>(
eventName,
_wca.GetContext().CurrentUser,
properties,
eventData,
eventFilterKey: "content",
eventFilterData: content.Id.ToString(CultureInfo.InvariantCulture));
}
}
}

View File

@@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Xml.Linq;
namespace Orchard.AuditTrail.Providers.Content {
public interface IDiffGramAnalyzer : IDependency {
/// <summary>
/// Compares the specified XML elements and returns a DiffGram XML element.
/// </summary>
XElement GenerateDiffGram(XElement element1, XElement element2);
/// <summary>
/// Analyzes the specified DiffGram element against the specified original XML element and returns a list of diff nodes,
/// where each node describes a difference between the original and updated document.
/// </summary>
IEnumerable<DiffNode> Analyze(XElement original, XElement diffGram);
}
}

View File

@@ -0,0 +1,83 @@
using System.Collections.Generic;
using Orchard.AuditTrail.Services;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentTypes.Events;
using Orchard.Environment.Extensions;
namespace Orchard.AuditTrail.Providers.ContentDefinition {
[OrchardFeature("Orchard.AuditTrail.ContentDefinition")]
public class ContentDefinitionEventHandler : IContentDefinitionEventHandler {
private readonly IAuditTrailManager _auditTrailManager;
private readonly IWorkContextAccessor _wca;
public ContentDefinitionEventHandler(IAuditTrailManager auditTrailManager, IWorkContextAccessor wca) {
_auditTrailManager = auditTrailManager;
_wca = wca;
}
public void ContentTypeCreated(ContentTypeCreatedContext context) {
RecordContentTypeAuditTrailEvent(ContentTypeAuditTrailEventProvider.Created, context.ContentTypeDefinition);
}
public void ContentTypeRemoved(ContentTypeRemovedContext context) {
RecordContentTypeAuditTrailEvent(ContentTypeAuditTrailEventProvider.Removed, context.ContentTypeDefinition);
}
public void ContentPartCreated(ContentPartCreatedContext context) {
RecordContentPartAuditTrailEvent(ContentPartAuditTrailEventProvider.Created, context.ContentPartDefinition);
}
public void ContentPartRemoved(ContentPartRemovedContext context) {
RecordContentPartAuditTrailEvent(ContentPartAuditTrailEventProvider.Removed, context.ContentPartDefinition);
}
public void ContentPartAttached(ContentPartAttachedContext context) {
RecordContentTypePartAuditTrailEvent(ContentTypeAuditTrailEventProvider.PartAdded, context.ContentTypeName, context.ContentPartName);
}
public void ContentPartDetached(ContentPartDetachedContext context) {
RecordContentTypePartAuditTrailEvent(ContentTypeAuditTrailEventProvider.PartRemoved, context.ContentTypeName, context.ContentPartName);
}
public void ContentFieldAttached(ContentFieldAttachedContext context) {
var eventData = new Dictionary<string, object> {
{"ContentPartName", context.ContentPartName},
{"ContentFieldName", context.ContentFieldName},
{"ContentFieldTypeName", context.ContentFieldTypeName},
{"ContentFieldDisplayName", context.ContentFieldDisplayName}
};
_auditTrailManager.CreateRecord<ContentPartAuditTrailEventProvider>(ContentPartAuditTrailEventProvider.FieldAdded, _wca.GetContext().CurrentUser, properties: null, eventData: eventData, eventFilterKey: "contentpart", eventFilterData: context.ContentPartName);
}
public void ContentFieldDetached(ContentFieldDetachedContext context) {
var eventData = new Dictionary<string, object> {
{"ContentPartName", context.ContentPartName},
{"ContentFieldName", context.ContentFieldName}
};
_auditTrailManager.CreateRecord<ContentPartAuditTrailEventProvider>(ContentPartAuditTrailEventProvider.FieldRemoved, _wca.GetContext().CurrentUser, properties: null, eventData: eventData, eventFilterKey: "contentpart", eventFilterData: context.ContentPartName);
}
private void RecordContentTypeAuditTrailEvent(string eventName, ContentTypeDefinition contentTypeDefinition) {
var eventData = new Dictionary<string, object> {
{"ContentTypeName", contentTypeDefinition.Name},
{"ContentTypeDisplayName", contentTypeDefinition.DisplayName},
};
_auditTrailManager.CreateRecord<ContentTypeAuditTrailEventProvider>(eventName, _wca.GetContext().CurrentUser, properties: null, eventData: eventData, eventFilterKey: "contenttype", eventFilterData: contentTypeDefinition.Name);
}
private void RecordContentPartAuditTrailEvent(string eventName, ContentPartDefinition contentPartDefinition) {
var eventData = new Dictionary<string, object> {
{"ContentPartName", contentPartDefinition.Name}
};
_auditTrailManager.CreateRecord<ContentPartAuditTrailEventProvider>(eventName, _wca.GetContext().CurrentUser, properties: null, eventData: eventData, eventFilterKey: "contentpart", eventFilterData: contentPartDefinition.Name);
}
private void RecordContentTypePartAuditTrailEvent(string eventName, string contentTypeName, string contentPartName) {
var eventData = new Dictionary<string, object> {
{"ContentTypeName", contentTypeName},
{"ContentPartName", contentPartName}
};
_auditTrailManager.CreateRecord<ContentTypeAuditTrailEventProvider>(eventName, _wca.GetContext().CurrentUser, properties: null, eventData: eventData, eventFilterKey: "contenttype", eventFilterData: contentTypeName);
}
}
}

View File

@@ -0,0 +1,27 @@
using Orchard.AuditTrail.Services;
using Orchard.AuditTrail.Services.Models;
using Orchard.Environment.Extensions;
namespace Orchard.AuditTrail.Providers.ContentDefinition {
[OrchardFeature("Orchard.AuditTrail.ContentDefinition")]
public class ContentPartAuditTrailEventProvider : AuditTrailEventProviderBase {
public const string Created = "Created";
public const string Removed = "Removed";
public const string DescriptionChanged = "DescriptionChanged";
public const string FieldAdded = "FieldAdded";
public const string FieldRemoved = "FieldRemoved";
public const string PartSettingsUpdated = "PartSettingsUpdated";
public const string FieldSettingsUpdated = "FieldSettingsUpdated";
public override void Describe(DescribeContext context) {
context.For("ContentPart", T("Content Parts"))
.Event(this, Created, T("Created"), T("A content part was created."), enableByDefault: true)
.Event(this, Removed, T("Removed"), T("A content part was removed."), enableByDefault: true)
.Event(this, DescriptionChanged, T("Description changed"), T("A content part description was changed."), enableByDefault: true)
.Event(this, FieldAdded, T("Field added"), T("A field was added to a content part."), enableByDefault: true)
.Event(this, FieldRemoved, T("Field removed"), T("A field was removed from a content part."), enableByDefault: true)
.Event(this, PartSettingsUpdated, T("Part settings updated"), T("The settings of a content part were updated."), enableByDefault: true)
.Event(this, FieldSettingsUpdated, T("Field settings updated"), T("The settings of a field on a content part were updated."), enableByDefault: true);
}
}
}

View File

@@ -0,0 +1,59 @@
using System;
using System.Linq;
using Orchard.AuditTrail.Helpers;
using Orchard.AuditTrail.Services;
using Orchard.AuditTrail.Services.Models;
using Orchard.ContentManagement.MetaData;
using Orchard.Environment.Extensions;
namespace Orchard.AuditTrail.Providers.ContentDefinition {
[OrchardFeature("Orchard.AuditTrail.ContentDefinition")]
public class ContentTypeAuditTrailEventProvider : AuditTrailEventProviderBase {
private readonly IContentDefinitionManager _contentDefinitionManager;
public ContentTypeAuditTrailEventProvider(IContentDefinitionManager contentDefinitionManager) {
_contentDefinitionManager = contentDefinitionManager;
}
public const string Created = "Created";
public const string Removed = "Removed";
public const string PartAdded = "PartAdded";
public const string PartRemoved = "PartRemoved";
public const string TypeDisplayNameUpdated = "TypeDisplayNameUpdated";
public const string TypeSettingsUpdated = "TypeSettingsUpdated";
public const string PartSettingsUpdated = "PartSettingsUpdated";
public const string FieldSettingsUpdated = "FieldSettingsUpdated";
public override void Describe(DescribeContext context) {
context.For("ContentType", T("Content Type"))
.Event(this, Created, T("Created"), T("A content type was created."), enableByDefault: true)
.Event(this, Removed, T("Removed"), T("A content type was removed."), enableByDefault: true)
.Event(this, PartAdded, T("Part added"), T("A content part was added to a content type."), enableByDefault: true)
.Event(this, PartRemoved, T("Part removed"), T("A content part was removed from a content type."), enableByDefault: true)
.Event(this, TypeDisplayNameUpdated, T("Type display name updated"), T("The display name of a content type was updated."), enableByDefault: true)
.Event(this, TypeSettingsUpdated, T("Type settings updated"), T("The settings of a content type were updated."), enableByDefault: true)
.Event(this, PartSettingsUpdated, T("Part settings updated"), T("The settings of a content part on a content type were updated."), enableByDefault: true)
.Event(this, FieldSettingsUpdated, T("Field settings updated"), T("The settings of a content field on a content part on a content type were updated."), enableByDefault: true);
context.QueryFilter(QueryFilter);
context.DisplayFilter(DisplayFilter);
}
private void QueryFilter(QueryFilterContext context) {
var contentType = context.Filters.Get("contenttype");
if(String.IsNullOrWhiteSpace(contentType))
return;
context.Query = context.Query.Where(x => x.EventFilterKey == "contenttype" && x.EventFilterData == contentType);
}
private void DisplayFilter(DisplayFilterContext context) {
var filterDisplay = context.ShapeFactory.AuditTrailFilter__ContentType(
ContentType: context.Filters.Get("contenttype"),
ContentTypes: _contentDefinitionManager.ListTypeDefinitions().OrderBy(x => x.DisplayName).ToArray());
context.FilterDisplay.Add(filterDisplay);
}
}
}

View File

@@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Orchard.AuditTrail.Helpers;
using Orchard.AuditTrail.Services;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentManagement.MetaData.Services;
using Orchard.ContentTypes.Services;
using Orchard.ContentTypes.ViewModels;
using Orchard.Environment.Extensions;
namespace Orchard.AuditTrail.Providers.ContentDefinition {
[OrchardFeature("Orchard.AuditTrail.ContentDefinition")]
public class GlobalContentDefinitionEditorEvents : ContentDefinitionEditorEventsBase {
private const string _contentPartSettingsDescriptionName = "ContentPartSettings.Description";
private readonly IAuditTrailManager _auditTrailManager;
private readonly IWorkContextAccessor _wca;
private readonly IContentDefinitionService _contentDefinitionService;
private readonly ISettingsFormatter _settingsFormatter;
private string _oldContentTypeDisplayName;
private EditTypeViewModel _currentContentType;
private SettingsDictionary _oldContentTypeSettings;
private SettingsDictionary _oldContentTypePartSettings;
private SettingsDictionary _oldContentPartFieldSettings;
private SettingsDictionary _oldPartSettings;
public GlobalContentDefinitionEditorEvents(
IAuditTrailManager auditTrailManager,
IWorkContextAccessor wca,
IContentDefinitionService contentDefinitionService,
ISettingsFormatter settingsFormatter) {
_auditTrailManager = auditTrailManager;
_wca = wca;
_contentDefinitionService = contentDefinitionService;
_settingsFormatter = settingsFormatter;
}
public override void TypeEditorUpdating(ContentTypeDefinitionBuilder definition) {
var contentType = _contentDefinitionService.GetType(definition.Name);
_currentContentType = contentType;
_oldContentTypeDisplayName = contentType.DisplayName;
_oldContentTypeSettings = new SettingsDictionary(contentType.Settings);
}
public override void TypeEditorUpdated(ContentTypeDefinitionBuilder builder) {
var contentTypeDefinition = builder.Build();
var newDisplayName = contentTypeDefinition.DisplayName;
var newSettings = contentTypeDefinition.Settings;
if (newDisplayName != _oldContentTypeDisplayName) {
var eventData = new Dictionary<string, object> {
{"ContentTypeName", builder.Name},
{"OldDisplayName", _oldContentTypeDisplayName},
{"NewDisplayName", newDisplayName}
};
RecordContentTypeAuditTrail(ContentTypeAuditTrailEventProvider.TypeDisplayNameUpdated, eventData, contentTypeDefinition.Name);
}
if (!AreEqual(newSettings, _oldContentTypeSettings)) {
var eventData = new Dictionary<string, object> {
{"ContentTypeName", builder.Name},
{"OldSettings", ToXml(_oldContentTypeSettings)},
{"NewSettings", ToXml(newSettings)}
};
RecordContentTypeAuditTrail(ContentTypeAuditTrailEventProvider.TypeSettingsUpdated, eventData, contentTypeDefinition.Name);
}
}
public override void TypePartEditorUpdating(ContentTypePartDefinitionBuilder builder) {
var contentTypeDefinition = _contentDefinitionService.GetType(builder.TypeName);
var contentPart = contentTypeDefinition.Parts.Single(x => x.PartDefinition.Name == builder.Name);
_oldContentTypePartSettings = contentPart.Settings;
}
public override void TypePartEditorUpdated(ContentTypePartDefinitionBuilder builder) {
var contentTypePartDefinition = builder.Build();
var newSettings = contentTypePartDefinition.Settings;
if (!AreEqual(newSettings, _oldContentTypePartSettings)) {
var eventData = new Dictionary<string, object> {
{"ContentPartName", builder.Name},
{"ContentTypeName", builder.TypeName},
{"OldSettings", ToXml(_oldContentTypePartSettings)},
{"NewSettings", ToXml(newSettings)}
};
RecordContentTypeAuditTrail(ContentTypeAuditTrailEventProvider.PartSettingsUpdated, eventData, builder.TypeName);
}
}
public override void PartFieldEditorUpdating(ContentPartFieldDefinitionBuilder builder) {
var contentPart = _contentDefinitionService.GetPart(builder.PartName);
var contentField = contentPart.Fields.Single(x => x.Name == builder.Name);
_oldContentPartFieldSettings = contentField.Settings;
}
public override void PartFieldEditorUpdated(ContentPartFieldDefinitionBuilder builder) {
var contentPartFieldDefinition = builder.Build();
var newSettings = contentPartFieldDefinition.Settings;
if (!AreEqual(newSettings, _oldContentPartFieldSettings)) {
var eventData = new Dictionary<string, object> {
{"ContentFieldName", builder.Name},
{"ContentPartName", builder.PartName},
{"OldSettings", ToXml(_oldContentPartFieldSettings)},
{"NewSettings", ToXml(newSettings)}
};
RecordContentPartAuditTrail(ContentPartAuditTrailEventProvider.FieldSettingsUpdated, eventData, builder.PartName);
}
}
public override void PartEditorUpdating(ContentPartDefinitionBuilder builder) {
var contentPart = _contentDefinitionService.GetPart(builder.Name);
_oldPartSettings = contentPart.Settings;
}
public override void PartEditorUpdated(ContentPartDefinitionBuilder builder) {
var contentPartDefinition = builder.Build();
var newPartSettings = contentPartDefinition.Settings;
if (newPartSettings.ContainsKey(_contentPartSettingsDescriptionName)) {
var oldDescription = _oldPartSettings.Get(_contentPartSettingsDescriptionName);
var newDescription = newPartSettings.Get(_contentPartSettingsDescriptionName);
if (oldDescription != newDescription) {
var eventData = new Dictionary<string, object> {
{"ContentPartName", builder.Name},
{"OldDescription", oldDescription},
{"NewDescription", newDescription}
};
RecordContentPartAuditTrail(ContentPartAuditTrailEventProvider.DescriptionChanged, eventData, builder.Name);
}
}
// Description change should not be re-recorded as general settings change.
var remainingOldPartSettings = new SettingsDictionary(_oldPartSettings.Where(item => item.Key != _contentPartSettingsDescriptionName).ToDictionary(item => item.Key, item => item.Value));
var remainingNewPartSettings = new SettingsDictionary(newPartSettings.Where(item => item.Key != _contentPartSettingsDescriptionName).ToDictionary(item => item.Key, item => item.Value));
if (!AreEqual(remainingNewPartSettings, remainingOldPartSettings)) {
var eventData = new Dictionary<string, object> {
{"ContentPartName", builder.Name},
{"OldSettings", ToXml(remainingOldPartSettings)},
{"NewSettings", ToXml(remainingNewPartSettings)}
};
RecordContentPartAuditTrail(ContentPartAuditTrailEventProvider.PartSettingsUpdated, eventData, builder.Name);
}
}
private void RecordContentTypeAuditTrail(string eventName, IDictionary<string, object> eventData, string contentTypeName) {
_auditTrailManager.CreateRecord<ContentTypeAuditTrailEventProvider>(eventName, _wca.GetContext().CurrentUser, properties: null, eventData: eventData, eventFilterKey: "contenttype", eventFilterData: contentTypeName);
}
private void RecordContentPartAuditTrail(string eventName, IDictionary<string, object> eventData, string contentPartName) {
_auditTrailManager.CreateRecord<ContentPartAuditTrailEventProvider>(eventName, _wca.GetContext().CurrentUser, properties: null, eventData: eventData, eventFilterKey: "contentpart", eventFilterData: contentPartName);
}
private string ToXml(SettingsDictionary settings) {
return _settingsFormatter.Map(settings).ToString(SaveOptions.DisableFormatting);
}
private bool AreEqual(SettingsDictionary a, SettingsDictionary b) {
var xml1 = ToXml(a);
var xml2 = ToXml(b);
return String.Equals(xml1, xml2, StringComparison.OrdinalIgnoreCase);
}
}
}

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using Orchard.AuditTrail.Helpers;
using Orchard.AuditTrail.Shapes;
using Orchard.ContentManagement.MetaData.Services;
using Orchard.DisplayManagement.Implementation;
namespace Orchard.AuditTrail.Providers.ContentDefinition.Shapes {
public class ContentPartSettingsUpdatedEventShape : AuditTrailEventShapeAlteration<ContentPartAuditTrailEventProvider> {
private readonly ISettingsFormatter _settingsFormatter;
public ContentPartSettingsUpdatedEventShape(ISettingsFormatter settingsFormatter) {
_settingsFormatter = settingsFormatter;
}
protected override string EventName {
get { return ContentPartAuditTrailEventProvider.PartSettingsUpdated; }
}
protected override void OnAlterShape(ShapeDisplayingContext context) {
var eventData = (IDictionary<string, object>)context.Shape.EventData;
var oldSettings = _settingsFormatter.Map(XmlHelper.Parse((string)eventData["OldSettings"]));
var newSettings = _settingsFormatter.Map(XmlHelper.Parse((string)eventData["NewSettings"]));
var diff = oldSettings.GetDiff(newSettings);
context.Shape.OldSettings = oldSettings;
context.Shape.NewSettings = newSettings;
context.Shape.Diff = diff;
}
}
}

View File

@@ -0,0 +1,32 @@
using System.Collections.Generic;
using Orchard.AuditTrail.Helpers;
using Orchard.AuditTrail.Shapes;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentManagement.MetaData.Services;
using Orchard.DisplayManagement.Implementation;
namespace Orchard.AuditTrail.Providers.ContentDefinition.Shapes {
public class ContentTypeFieldSettingsUpdatedEventShape : AuditTrailEventShapeAlteration<ContentPartAuditTrailEventProvider> {
private readonly ISettingsFormatter _settingsFormatter;
public ContentTypeFieldSettingsUpdatedEventShape(ISettingsFormatter settingsFormatter) {
_settingsFormatter = settingsFormatter;
}
protected override string EventName {
get { return ContentPartAuditTrailEventProvider.FieldSettingsUpdated; }
}
protected override void OnAlterShape(ShapeDisplayingContext context) {
var eventData = (IDictionary<string, object>)context.Shape.EventData;
var oldSettings = _settingsFormatter.Map(XmlHelper.Parse((string)eventData["OldSettings"]));
var newSettings = _settingsFormatter.Map(XmlHelper.Parse((string)eventData["NewSettings"]));
var diff = oldSettings.GetDiff(newSettings);
context.Shape.OldSettings = oldSettings;
context.Shape.OldDisplayName = oldSettings.Get(ContentPartFieldDefinition.DisplayNameKey);
context.Shape.NewSettings = newSettings;
context.Shape.NewDisplayName = newSettings.Get(ContentPartFieldDefinition.DisplayNameKey);
context.Shape.Diff = diff;
}
}
}

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using Orchard.AuditTrail.Helpers;
using Orchard.AuditTrail.Shapes;
using Orchard.ContentManagement.MetaData.Services;
using Orchard.DisplayManagement.Implementation;
namespace Orchard.AuditTrail.Providers.ContentDefinition.Shapes {
public class ContentTypePartSettingsUpdatedEventShape : AuditTrailEventShapeAlteration<ContentTypeAuditTrailEventProvider> {
private readonly ISettingsFormatter _settingsFormatter;
public ContentTypePartSettingsUpdatedEventShape(ISettingsFormatter settingsFormatter) {
_settingsFormatter = settingsFormatter;
}
protected override string EventName {
get { return ContentTypeAuditTrailEventProvider.PartSettingsUpdated; }
}
protected override void OnAlterShape(ShapeDisplayingContext context) {
var eventData = (IDictionary<string, object>)context.Shape.EventData;
var oldSettings = _settingsFormatter.Map(XmlHelper.Parse((string)eventData["OldSettings"]));
var newSettings = _settingsFormatter.Map(XmlHelper.Parse((string)eventData["NewSettings"]));
var diff = oldSettings.GetDiff(newSettings);
context.Shape.OldSettings = oldSettings;
context.Shape.NewSettings = newSettings;
context.Shape.Diff = diff;
}
}
}

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using Orchard.AuditTrail.Helpers;
using Orchard.AuditTrail.Shapes;
using Orchard.ContentManagement.MetaData.Services;
using Orchard.DisplayManagement.Implementation;
namespace Orchard.AuditTrail.Providers.ContentDefinition.Shapes {
public class ContentTypeSettingsUpdatedEventShape : AuditTrailEventShapeAlteration<ContentTypeAuditTrailEventProvider> {
private readonly ISettingsFormatter _settingsFormatter;
public ContentTypeSettingsUpdatedEventShape(ISettingsFormatter settingsFormatter) {
_settingsFormatter = settingsFormatter;
}
protected override string EventName {
get { return ContentTypeAuditTrailEventProvider.TypeSettingsUpdated; }
}
protected override void OnAlterShape(ShapeDisplayingContext context) {
var eventData = (IDictionary<string, object>)context.Shape.EventData;
var oldSettings = _settingsFormatter.Map(XmlHelper.Parse((string)eventData["OldSettings"]));
var newSettings = _settingsFormatter.Map(XmlHelper.Parse((string)eventData["NewSettings"]));
var diff = oldSettings.GetDiff(newSettings);
context.Shape.OldSettings = oldSettings;
context.Shape.NewSettings = newSettings;
context.Shape.Diff = diff;
}
}
}

View File

@@ -0,0 +1,27 @@
using Orchard.AuditTrail.Services;
using Orchard.AuditTrail.Services.Models;
using Orchard.Environment.Extensions;
namespace Orchard.AuditTrail.Providers.Roles {
[OrchardFeature("Orchard.AuditTrail.Roles")]
public class RoleAuditTrailEventProvider : AuditTrailEventProviderBase {
public const string Created = "Created";
public const string Removed = "Removed";
public const string Renamed = "Renamed";
public const string PermissionAdded = "PermissionAdded";
public const string PermissionRemoved = "PermissionRemoved";
public const string UserAdded = "UserAdded";
public const string UserRemoved = "UserRemoved";
public override void Describe(DescribeContext context) {
context.For("Role", T("Roles"))
.Event(this, Created, T("Created"), T("A role was created."), enableByDefault: true)
.Event(this, Removed, T("Removed"), T("A role was removed."), enableByDefault: true)
.Event(this, Renamed, T("Renamed"), T("A role was renamed."), enableByDefault: true)
.Event(this, PermissionAdded, T("Permission added"), T("A permission was added to a role."), enableByDefault: true)
.Event(this, PermissionRemoved, T("Permission removed"), T("A permission was removed from a role."), enableByDefault: true)
.Event(this, UserAdded, T("User added"), T("A user was added to a role."), enableByDefault: true)
.Event(this, UserRemoved, T("User removed"), T("A user was removed from a role."), enableByDefault: true);
}
}
}

View File

@@ -0,0 +1,87 @@
using System.Collections.Generic;
using Orchard.AuditTrail.Services;
using Orchard.Environment.Extensions;
using Orchard.Roles.Events;
using Orchard.Security;
namespace Orchard.AuditTrail.Providers.Roles {
[OrchardFeature("Orchard.AuditTrail.Roles")]
public class RoleEventHandler : IRoleEventHandler {
private readonly IAuditTrailManager _auditTrailManager;
private readonly IWorkContextAccessor _wca;
public RoleEventHandler(IAuditTrailManager auditTrailManager, IWorkContextAccessor wca) {
_auditTrailManager = auditTrailManager;
_wca = wca;
}
public void Created(RoleCreatedContext context) {
RecordAuditTrailEvent(RoleAuditTrailEventProvider.Created, context.Role.Name);
}
public void Removed(RoleRemovedContext context) {
RecordAuditTrailEvent(RoleAuditTrailEventProvider.Removed, context.Role.Name);
}
public void Renamed(RoleRenamedContext context) {
var eventData = new Dictionary<string, object> {
{"RoleName", context.Role.Name},
{"PreviousRoleName", context.PreviousRoleName},
{"NewRoleName", context.NewRoleName},
};
RecordAuditTrailEvent(RoleAuditTrailEventProvider.Renamed, context.Role.Name, properties: null, eventData:eventData);
}
public void PermissionAdded(PermissionAddedContext context) {
RecordAuditTrailEvent(RoleAuditTrailEventProvider.PermissionAdded, context.Role.Name, context.Permission.Name);
}
public void PermissionRemoved(PermissionRemovedContext context) {
RecordAuditTrailEvent(RoleAuditTrailEventProvider.PermissionRemoved, context.Role.Name, context.Permission.Name);
}
public void UserAdded(UserAddedContext context) {
RecordAuditTrailEvent(RoleAuditTrailEventProvider.UserAdded, context.Role.Name, context.User);
}
public void UserRemoved(UserRemovedContext context) {
RecordAuditTrailEvent(RoleAuditTrailEventProvider.UserRemoved, context.Role.Name, context.User);
}
private void RecordAuditTrailEvent(string eventName, string roleName) {
var eventData = new Dictionary<string, object> {
{"RoleName", roleName}
};
RecordAuditTrailEvent(eventName, roleName, properties: null, eventData: eventData);
}
private void RecordAuditTrailEvent(string eventName, string roleName, string permissionName) {
var eventData = new Dictionary<string, object> {
{"RoleName", roleName},
{"PermissionName", permissionName}
};
RecordAuditTrailEvent(eventName, roleName, properties: null, eventData: eventData);
}
private void RecordAuditTrailEvent(string eventName, string roleName, IUser user) {
var properties = new Dictionary<string, object> {
{"User", user}
};
var eventData = new Dictionary<string, object> {
{"RoleName", roleName},
{"UserName", user.UserName}
};
RecordAuditTrailEvent(eventName, roleName, properties, eventData);
}
private void RecordAuditTrailEvent(string eventName, string roleName, IDictionary<string, object> properties, IDictionary<string, object> eventData) {
_auditTrailManager.CreateRecord<RoleAuditTrailEventProvider>(eventName, _wca.GetContext().CurrentUser, properties, eventData, eventFilterKey: "role", eventFilterData: roleName);
}
}
}

View File

@@ -0,0 +1,21 @@
using Orchard.AuditTrail.Services;
using Orchard.AuditTrail.Services.Models;
using Orchard.Environment.Extensions;
namespace Orchard.AuditTrail.Providers.Users {
[OrchardFeature("Orchard.AuditTrail.Users")]
public class UserAuditTrailEventProvider : AuditTrailEventProviderBase {
public const string LoggedIn = "LoggedIn";
public const string LoggedOut = "LoggedOut";
public const string LogInFailed = "LogInFailed";
public const string PasswordChanged = "PasswordChanged";
public override void Describe(DescribeContext context) {
context.For("User", T("Users"))
.Event(this, LoggedIn, T("Logged in"), T("A user was successfully logged in."), enableByDefault: true)
.Event(this, LoggedOut, T("Logged out"), T("A user actively logged out."), enableByDefault: true)
.Event(this, LogInFailed, T("Login failed"), T("An attempt to login failed due to incorrect credentials."), enableByDefault: true)
.Event(this, PasswordChanged, T("Password changed"), T("A user's password was changed."), enableByDefault: true);
}
}
}

View File

@@ -0,0 +1,73 @@
using System.Collections.Generic;
using Orchard.AuditTrail.Services;
using Orchard.Environment.Extensions;
using Orchard.Security;
using Orchard.Users.Events;
namespace Orchard.AuditTrail.Providers.Users {
[OrchardFeature("Orchard.AuditTrail.Users")]
public class UserEventHandler : IUserEventHandler {
private readonly IAuditTrailManager _auditTrailManager;
private readonly IWorkContextAccessor _wca;
public UserEventHandler(IAuditTrailManager auditTrailManager, IWorkContextAccessor wca) {
_auditTrailManager = auditTrailManager;
_wca = wca;
}
public void LoggedIn(IUser user) {
RecordAuditTrail(UserAuditTrailEventProvider.LoggedIn, user);
}
public void LoggedOut(IUser user) {
RecordAuditTrail(UserAuditTrailEventProvider.LoggedOut, user);
}
public void LogInFailed(string userNameOrEmail, string password) {
var eventData = new Dictionary<string, object> {
{"UserName", userNameOrEmail}
};
_auditTrailManager.CreateRecord<UserAuditTrailEventProvider>(UserAuditTrailEventProvider.LogInFailed, _wca.GetContext().CurrentUser, properties: null, eventData: eventData, eventFilterKey: "user", eventFilterData: userNameOrEmail);
}
public void ChangedPassword(IUser user) {
RecordAuditTrail(UserAuditTrailEventProvider.PasswordChanged, user);
}
private void RecordAuditTrail(string eventName, IUser user) {
var properties = new Dictionary<string, object> {
{"User", user}
};
var eventData = new Dictionary<string, object> {
{"UserId", user.Id},
{"UserName", user.UserName}
};
_auditTrailManager.CreateRecord<UserAuditTrailEventProvider>(eventName, _wca.GetContext().CurrentUser, properties, eventData, eventFilterKey: "user", eventFilterData: user.UserName);
}
public void Creating(UserContext context) {
}
public void Created(UserContext context) {
}
public void LoggingIn(string userNameOrEmail, string password) {
}
public void AccessDenied(IUser user) {
}
public void SentChallengeEmail(IUser user) {
}
public void ConfirmedEmail(IUser user) {
}
public void Approved(IUser user) {
}
}
}

View File

@@ -0,0 +1,19 @@
<?xml version="1.0"?>
<Orchard>
<Recipe>
<Name>Audit Trail</Name>
<Description>Attaches the AuditTrailPart to the Page content type.</Description>
<Author>The Orchard Team</Author>
<WebSite>http://orchardproject.net</WebSite>
<Tags>developer</Tags>
<Version>1.0</Version>
</Recipe>
<Feature enable="Orchard.AuditTrail" />
<Metadata>
<Types>
<Page>
<AuditTrailPart />
</Page>
</Types>
</Metadata>
</Orchard>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
</staticContent>
<handlers accessPolicy="Script,Read">
<!--
iis7 - for any request to a file exists on disk, return it via native http module.
accessPolicy 'Script' is to allow for a managed 404 page.
-->
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
</handlers>
</system.webServer>
</configuration>

View File

@@ -0,0 +1,23 @@
$(function () {
$(".check-all-container").each(function () {
var container = $(this);
var master = container.find("input[type=\"checkbox\"].check-all-master");
var slaves = container.find("input[type=\"checkbox\"]:not(:disabled).check-all-slave");
var updateMaster = function () {
var allChecked = slaves.filter(":not(:checked)").length == 0;
master.prop("checked", allChecked);
}
master.on("change", function () {
var isChecked = $(this).is(":checked");
slaves.prop("checked", isChecked);
});
slaves.on("change", function () {
updateMaster();
});
updateMaster();
});
});

View File

@@ -0,0 +1,5 @@
$(function () {
$(".disabled-content-wrapper input").prop("disabled", true);
$(".disabled-content-wrapper textarea").prop("disabled", true);
$(".disabled-content-wrapper button").prop("disabled", true);
});

View File

@@ -0,0 +1,11 @@
$(function() {
$(".expando-wrapper > legend").expandoControl(
function(controller) {
return controller.nextAll(".expando");
},
{
collapse: true,
remember: true
}
);
});

View File

@@ -0,0 +1,33 @@
using System;
using Orchard.AuditTrail.Models;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Shapes;
namespace Orchard.AuditTrail.Services {
public class AuditTrailEventDisplayBuilder : IAuditTrailEventDisplayBuilder {
private readonly IEventDataSerializer _serializer;
private readonly IAuditTrailManager _auditTrailManager;
public AuditTrailEventDisplayBuilder(IShapeFactory shapeFactory, IEventDataSerializer serializer, IAuditTrailManager auditTrailManager) {
_serializer = serializer;
_auditTrailManager = auditTrailManager;
New = shapeFactory;
}
public dynamic New { get; set; }
public dynamic BuildDisplay(AuditTrailEventRecord record, string displayType) {
var eventData = _serializer.Deserialize(record.EventData);
var descriptor = _auditTrailManager.DescribeEvent(record.FullEventName);
var auditTrailEventShape = New.AuditTrailEvent(Record: record, EventData: eventData, Descriptor: descriptor);
var metaData = (ShapeMetadata)auditTrailEventShape.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;
}
}
}

View File

@@ -0,0 +1,9 @@
using Orchard.AuditTrail.Services.Models;
namespace Orchard.AuditTrail.Services {
public class AuditTrailEventHandlerBase : Component, IAuditTrailEventHandler {
public virtual void Create(AuditTrailCreateContext context) {}
public virtual void Filter(QueryFilterContext context) {}
public virtual void DisplayFilter(DisplayFilterContext context) { }
}
}

View File

@@ -0,0 +1,7 @@
using Orchard.AuditTrail.Services.Models;
namespace Orchard.AuditTrail.Services {
public abstract class AuditTrailEventProviderBase : Component, IAuditTrailEventProvider {
public abstract void Describe(DescribeContext context);
}
}

View File

@@ -0,0 +1,249 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Orchard.AuditTrail.Helpers;
using Orchard.AuditTrail.Models;
using Orchard.AuditTrail.Services.Models;
using Orchard.Caching;
using Orchard.Collections;
using Orchard.ContentManagement;
using Orchard.Data;
using Orchard.DisplayManagement;
using Orchard.Logging;
using Orchard.Security;
using Orchard.Services;
using Orchard.Settings;
namespace Orchard.AuditTrail.Services {
public class AuditTrailManager : Component, IAuditTrailManager {
private readonly IRepository<AuditTrailEventRecord> _auditTrailRepository;
private readonly IAuditTrailEventProvider _providers;
private readonly IClock _clock;
private readonly IAuditTrailEventHandler _auditTrailEventHandlers;
private readonly IEventDataSerializer _serializer;
private readonly ICacheManager _cacheManager;
private readonly ISiteService _siteService;
private readonly ISignals _signals;
private readonly IShapeFactory _shapeFactory;
public AuditTrailManager(
IRepository<AuditTrailEventRecord> auditTrailRepository,
IAuditTrailEventProvider providers,
IClock clock,
IAuditTrailEventHandler auditTrailEventHandlers,
IEventDataSerializer serializer,
ICacheManager cacheManager,
ISiteService siteService,
ISignals signals,
IShapeFactory shapeFactory) {
_auditTrailRepository = auditTrailRepository;
_providers = providers;
_clock = clock;
_auditTrailEventHandlers = auditTrailEventHandlers;
_serializer = serializer;
_cacheManager = cacheManager;
_siteService = siteService;
_signals = signals;
_shapeFactory = shapeFactory;
}
public IPageOfItems<AuditTrailEventRecord> GetRecords(
int page,
int pageSize,
Filters filters = null,
AuditTrailOrderBy orderBy = AuditTrailOrderBy.DateDescending) {
var query = _auditTrailRepository.Table;
if (filters != null) {
var filterContext = new QueryFilterContext(query, filters);
// Invoke event handlers.
_auditTrailEventHandlers.Filter(filterContext);
// Give each provider a chance to modify the query.
var providersContext = DescribeProviders();
foreach (var queryFilter in providersContext.QueryFilters) {
queryFilter(filterContext);
}
query = filterContext.Query;
}
switch (orderBy) {
case AuditTrailOrderBy.EventAscending:
query = query.OrderBy(x => x.EventName).ThenByDescending(x => x.Id);
break;
case AuditTrailOrderBy.CategoryAscending:
query = query.OrderBy(x => x.Category).ThenByDescending(x => x.Id);
break;
default:
query = query.OrderByDescending(x => x.CreatedUtc).ThenByDescending(x => x.Id);
break;
}
var totalItemCount = query.Count();
var startIndex = (page - 1) * pageSize;
query = query.Skip(startIndex);
if (pageSize > 0)
query = query.Take(pageSize);
return new PageOfItems<AuditTrailEventRecord>(query) {
PageNumber = page,
PageSize = pageSize,
TotalItemCount = totalItemCount
};
}
public AuditTrailEventRecord GetRecord(int id) {
return _auditTrailRepository.Get(id);
}
public dynamic BuildFilterDisplay(Filters filters) {
var filterDisplay = (dynamic)_shapeFactory.Create("AuditTrailFilter");
var filterDisplayContext = new DisplayFilterContext(_shapeFactory, filters, filterDisplay);
// Invoke event handlers.
_auditTrailEventHandlers.DisplayFilter(filterDisplayContext);
// Give each provider a chance to provide a filter display.
var providersContext = DescribeProviders();
foreach (var action in providersContext.FilterDisplays) {
action(filterDisplayContext);
}
return filterDisplay;
}
public AuditTrailEventRecordResult CreateRecord<T>(string eventName, IUser user, IDictionary<string, object> properties = null, IDictionary<string, object> eventData = null, string eventFilterKey = null, string eventFilterData = null) where T : IAuditTrailEventProvider {
var eventDescriptor = DescribeEvent<T>(eventName);
if (!IsEventEnabled(eventDescriptor))
return new AuditTrailEventRecordResult {
Record = null,
IsDisabled = true
};
if (properties == null) properties = new Dictionary<string, object>();
if (eventData == null) eventData = new Dictionary<string, object>();
var context = new AuditTrailCreateContext {
Event = eventName,
User = user,
Properties = properties,
EventData = eventData,
EventFilterKey = eventFilterKey,
EventFilterData = eventFilterData
};
_auditTrailEventHandlers.Create(context);
var record = new AuditTrailEventRecord {
Category = eventDescriptor.CategoryDescriptor.Category,
EventName = eventName,
FullEventName = eventDescriptor.Event,
CreatedUtc = _clock.UtcNow,
UserName = user != null ? user.UserName : null,
EventData = _serializer.Serialize(context.EventData),
EventFilterKey = context.EventFilterKey,
EventFilterData = context.EventFilterData,
Comment = context.Comment
};
_auditTrailRepository.Create(record);
return new AuditTrailEventRecordResult {
Record = record,
IsDisabled = false
};
}
private bool IsEventEnabled(AuditTrailEventDescriptor eventDescriptor) {
if (eventDescriptor.IsMandatory)
return true;
var settingsDictionary = _cacheManager.Get("AuditTrail.EventSettings", context => {
context.Monitor(_signals.When("AuditTrail.EventSettings"));
return _siteService.GetSiteSettings().As<AuditTrailSettingsPart>().EventSettings.ToDictionary(x => x.EventName);
});
var setting = settingsDictionary.ContainsKey(eventDescriptor.Event) ? settingsDictionary[eventDescriptor.Event] : default(AuditTrailEventSetting);
return setting != null ? setting.IsEnabled : eventDescriptor.IsEnabledByDefault;
}
public IEnumerable<AuditTrailCategoryDescriptor> DescribeCategories() {
var context = DescribeProviders();
return context.Describe();
}
public DescribeContext DescribeProviders() {
var context = new DescribeContext();
_providers.Describe(context);
return context;
}
public AuditTrailEventDescriptor DescribeEvent<T>(string eventName) where T : IAuditTrailEventProvider {
var fullyQualifiedEventName = EventNameExtensions.GetFullyQualifiedEventName<T>(eventName);
return DescribeEvent(fullyQualifiedEventName);
}
public AuditTrailEventDescriptor DescribeEvent(string fullyQualifiedEventName) {
var categoryDescriptors = DescribeCategories();
var eventDescriptorQuery =
from c in categoryDescriptors
from e in c.Events
where e.Event == fullyQualifiedEventName
select e;
var eventDescriptors = eventDescriptorQuery.ToArray();
if (!eventDescriptors.Any()) {
throw new ArgumentException(String.Format("No event named '{0}' exists.", fullyQualifiedEventName), "fullyQualifiedEventName");
}
return eventDescriptors.First();
}
public IEnumerable<AuditTrailEventRecord> Trim(TimeSpan retentionPeriod) {
var dateThreshold = (_clock.UtcNow.Latest() - retentionPeriod);
var query = _auditTrailRepository.Table.Where(x => x.CreatedUtc <= dateThreshold);
var records = query.ToArray();
foreach (var record in records) {
_auditTrailRepository.Delete(record);
}
return records;
}
public string SerializeProviderConfiguration(IEnumerable<AuditTrailEventSetting> settings) {
var doc = new XDocument(
new XElement("Events",
settings.Select(x =>
new XElement("Event",
new XAttribute("Name", x.EventName),
new XAttribute("IsEnabled", x.IsEnabled)))));
return doc.ToString(SaveOptions.DisableFormatting);
}
public IEnumerable<AuditTrailEventSetting> DeserializeProviderConfiguration(string data) {
if (String.IsNullOrWhiteSpace(data))
return Enumerable.Empty<AuditTrailEventSetting>();
try {
var doc = XDocument.Parse(data);
return doc.Element("Events").Elements("Event").Select(x => new AuditTrailEventSetting {
EventName = x.Attr<string>("Name"),
IsEnabled = x.Attr<bool>("IsEnabled")
}).ToArray();
}
catch (Exception ex) {
Logger.Error(ex, "Error occurred during deserialization of audit trail settings.");
}
return Enumerable.Empty<AuditTrailEventSetting>();
}
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Linq;
using System.Threading;
using Orchard.AuditTrail.Models;
using Orchard.ContentManagement;
using Orchard.Environment.Extensions;
using Orchard.Logging;
using Orchard.Services;
using Orchard.Settings;
using Orchard.TaskLease.Services;
using Orchard.Tasks;
namespace Orchard.AuditTrail.Services {
[OrchardFeature("Orchard.AuditTrail.Trimming")]
public class AuditTrailTrimmingBackgroundTask : Component, IBackgroundTask {
private static readonly object _sweepLock = new object();
private readonly ISiteService _siteService;
private readonly IClock _clock;
private readonly ITaskLeaseService _taskLeaseService;
private readonly IAuditTrailManager _auditTrailManager;
public AuditTrailTrimmingBackgroundTask(
ISiteService siteService,
IClock clock,
ITaskLeaseService taskLeaseService,
IAuditTrailManager auditTrailManager) {
_siteService = siteService;
_clock = clock;
_taskLeaseService = taskLeaseService;
_auditTrailManager = auditTrailManager;
}
public AuditTrailTrimmingSettingsPart Settings {
get { return _siteService.GetSiteSettings().As<AuditTrailTrimmingSettingsPart>(); }
}
public void Sweep() {
if (Monitor.TryEnter(_sweepLock)) {
try {
Logger.Debug("Beginning sweep.");
// Only allow this task to run on one farm node at a time.
if (_taskLeaseService.Acquire(GetType().FullName, _clock.UtcNow.AddHours(1)) != null) {
// We don't need to check the audit trail for events to remove every minute. Let's stick with twice a day.
if (!GetIsTimeToTrim())
return;
Logger.Debug("Starting audit trail trimming.");
var deletedRecords = _auditTrailManager.Trim(TimeSpan.FromDays(Settings.RetentionPeriod));
Logger.Debug("Audit trail trimming completed. {0} records were deleted.", deletedRecords.Count());
Settings.LastRunUtc = _clock.UtcNow;
}
}
catch (Exception ex) {
Logger.Error(ex, "Error during sweep.");
}
finally {
Monitor.Exit(_sweepLock);
Logger.Debug("Ending sweep.");
}
}
}
private bool GetIsTimeToTrim() {
var lastRun = Settings.LastRunUtc ?? DateTime.MinValue;
var now = _clock.UtcNow;
var interval = TimeSpan.FromHours(Settings.MinimumRunInterval);
return now - lastRun >= interval;
}
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Linq;
using Orchard.AuditTrail.Helpers;
using Orchard.AuditTrail.Services.Models;
using Orchard.Core.Common.ViewModels;
using Orchard.Localization.Services;
namespace Orchard.AuditTrail.Services {
public class CommonAuditTrailEventHandler : AuditTrailEventHandlerBase {
private readonly Lazy<IAuditTrailManager> _auditTrailManager;
private readonly IDateServices _dateServices;
public CommonAuditTrailEventHandler(Lazy<IAuditTrailManager> auditTrailManager, IDateServices dateServices) {
_auditTrailManager = auditTrailManager;
_dateServices = dateServices;
}
public override void Filter(QueryFilterContext context) {
var userName = context.Filters.Get("username");
var category = context.Filters.Get("category");
var from = GetDateFromFilter(context.Filters, "From", "from").Earliest();
var to = GetDateFromFilter(context.Filters, "To", "to").Latest();
var query = context.Query;
if (!String.IsNullOrWhiteSpace(userName)) {
query = query.Where(x => x.UserName == userName);
}
if (!String.IsNullOrWhiteSpace(category)) {
query = query.Where(x => x.Category == category);
}
if (from != null) {
query = query.Where(x => x.CreatedUtc >= from);
}
if (to != null) {
query = query.Where(x => x.CreatedUtc <= to);
}
context.Query = query;
}
public override void DisplayFilter(DisplayFilterContext context) {
var userName = context.Filters.Get("username");
var fromDate = context.Filters.Get("from.Date");
var toDate = context.Filters.Get("to.Date");
var category = context.Filters.Get("category");
var userNameFilterDisplay = context.ShapeFactory.AuditTrailFilter__Common__User(UserName: userName);
var dateFromFilterDisplay = context.ShapeFactory.AuditTrailFilter__Common__Date__From(Editor: new DateTimeEditor {Date = fromDate, ShowDate = true});
var dateToFilterDisplay = context.ShapeFactory.AuditTrailFilter__Common__Date__To(Editor: new DateTimeEditor { Date = toDate, ShowDate = true });
var categoryFilterDisplay = context.ShapeFactory.AuditTrailFilter__Common__Category(
Categories: _auditTrailManager.Value.DescribeCategories().ToArray(),
Category: category);
context.FilterDisplay.Add(dateFromFilterDisplay);
context.FilterDisplay.Add(dateToFilterDisplay);
context.FilterDisplay.Add(categoryFilterDisplay);
context.FilterDisplay.Add(userNameFilterDisplay);
}
private DateTime? GetDateFromFilter(Filters filters, string fieldName, string prefix) {
try {
var dateString = filters.Get(prefix + ".Date");
var timeString = filters.Get(prefix + ".Time");
return _dateServices.ConvertFromLocalString(dateString, timeString);
}
catch (FormatException ex) {
filters.UpdateModel.AddModelError(prefix, T(@"Error parsing ""{0}"" date: {1}", fieldName, ex.Message));
return null;
}
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Newtonsoft.Json;
using Orchard.Logging;
namespace Orchard.AuditTrail.Services {
public class EventDataSerializer : Component, IEventDataSerializer {
public string Serialize(IDictionary<string, object> eventData) {
try {
var json = JsonConvert.SerializeObject(eventData, Formatting.None);
var xml = JsonConvert.DeserializeXNode(json, deserializeRootElementName: "EventData");
return xml.ToString(SaveOptions.DisableFormatting);
}
catch (Exception ex) {
Logger.Error(ex, "Error during serialization of event data.");
}
return null;
}
public IDictionary<string, object> Deserialize(string eventData) {
if (String.IsNullOrWhiteSpace(eventData))
return new Dictionary<string, object>();
try {
var node = XDocument.Parse(eventData);
var json = JsonConvert.SerializeXNode(node, Formatting.None, omitRootObject: true);
return JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
}
catch (Exception ex) {
Logger.Error(ex, "Error during deserialization of event data.");
}
return new Dictionary<string, object>();
}
}
}

View File

@@ -0,0 +1,7 @@
using Orchard.AuditTrail.Models;
namespace Orchard.AuditTrail.Services {
public interface IAuditTrailEventDisplayBuilder : IDependency {
dynamic BuildDisplay(AuditTrailEventRecord record, string displayType);
}
}

View File

@@ -0,0 +1,10 @@
using Orchard.AuditTrail.Services.Models;
using Orchard.Events;
namespace Orchard.AuditTrail.Services {
public interface IAuditTrailEventHandler : IEventHandler {
void Create(AuditTrailCreateContext context);
void Filter(QueryFilterContext context);
void DisplayFilter(DisplayFilterContext context);
}
}

View File

@@ -0,0 +1,9 @@
using Orchard.AuditTrail.Models;
using Orchard.AuditTrail.Services.Models;
using Orchard.Events;
namespace Orchard.AuditTrail.Services {
public interface IAuditTrailEventProvider : IEventHandler {
void Describe(DescribeContext context);
}
}

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using Orchard.AuditTrail.Models;
using Orchard.AuditTrail.Services.Models;
using Orchard.Collections;
using Orchard.Security;
namespace Orchard.AuditTrail.Services {
public interface IAuditTrailManager : IDependency {
/// <summary>
/// Gets a page of event records from the audit trail.
/// </summary>
/// <param name="page">The page number to get records from.</param>
/// <param name="pageSize">The number of records to get.</param>
/// <param name="orderBy">The value to order by.</param>
/// <param name="filters">Optional. An object to filter the records on.</param>
/// <returns>A page of event records.</returns>
IPageOfItems<AuditTrailEventRecord> GetRecords(int page, int pageSize, Filters filters = null, AuditTrailOrderBy orderBy = AuditTrailOrderBy.DateDescending);
/// <summary>
/// Gets a single event record from the audit trail by ID.
/// </summary>
/// <param name="id">The event record ID.</param>
/// <returns>A single event record.</returns>
AuditTrailEventRecord GetRecord(int id);
/// <summary>
/// Builds a shape tree of filter displays.
/// </summary>
/// <param name="filters">Input for each filter builder.</param>
/// <returns>A tree of shapes.</returns>
dynamic BuildFilterDisplay(Filters filters);
/// <summary>
/// Records an audit trail event.
/// </summary>
/// <typeparam name="T">The audit trail event provider type to determine the scope of the event name.</typeparam>
/// <param name="eventName">The shorthand name of the event</param>
/// <param name="user">The user to associate with the event. This is typically the currently loggedin user.</param>
/// <param name="properties">A property bag of custom event data that could be useful for <see cref="IAuditTrailEventHandler"/> implementations. These values aren't stored. Use the eventData parameter to persist additional data with the event.</param>
/// <param name="eventData">A property bag of custom event data that will be stored with the event record.</param>
/// <param name="eventFilterKey">The name of a custom key to use when filtering events.</param>
/// <param name="eventFilterData">The value of a custom filter key to filter on.</param>
/// <returns>The created audit trail event record if the specified event was not disabled.</returns>
AuditTrailEventRecordResult CreateRecord<T>(string eventName, IUser user, IDictionary<string, object> properties = null, IDictionary<string, object> eventData = null, string eventFilterKey = null, string eventFilterData = null) where T : IAuditTrailEventProvider;
/// <summary>
/// Describes all audit trail events provided by the system.
/// </summary>
/// <returns>A list of audit trail category descriptors.</returns>
IEnumerable<AuditTrailCategoryDescriptor> DescribeCategories();
/// <summary>
/// Describes all audit trail event providers.
/// </summary>
DescribeContext DescribeProviders();
/// <summary>
/// Describes a single audit trail event.
/// </summary>
/// <typeparam name="T">The scope of the specified event name.</typeparam>
/// <param name="eventName">The shorthand name of the event.</param>
/// <returns>A single audit trail event descriptor.</returns>
AuditTrailEventDescriptor DescribeEvent<T>(string eventName) where T : IAuditTrailEventProvider;
/// <summary>
/// Describes a single audit trail event.
/// </summary>
/// <param name="fullyQualifiedEventName">The fully qualified event name to describe.</param>
/// <returns>A single audit trail event descriptor.</returns>
AuditTrailEventDescriptor DescribeEvent(string fullyQualifiedEventName);
/// <summary>
/// Trims the audit trail by deleting all records older than the specified retention period.
/// </summary>
/// <returns>A list of deleted records.</returns>
IEnumerable<AuditTrailEventRecord> Trim(TimeSpan retentionPeriod);
/// <summary>
/// Serializes the specified list of settings into a string.
/// </summary>
string SerializeProviderConfiguration(IEnumerable<AuditTrailEventSetting> settings);
/// <summary>
/// Deserializes the specified string into a list of settings.
/// </summary>
IEnumerable<AuditTrailEventSetting> DeserializeProviderConfiguration(string data);
}
}

View File

@@ -0,0 +1,8 @@
using Orchard.AuditTrail.Services.Models;
using Orchard.Events;
namespace Orchard.AuditTrail.Services {
public interface IAuditTrailSettingsEventHandler : IEventHandler {
void Updated(AuditTrailSettingsContext context);
}
}

View File

@@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Orchard.AuditTrail.Services {
public interface IEventDataSerializer : IDependency {
string Serialize(IDictionary<string, object> eventData);
IDictionary<string, object> Deserialize(string eventData);
}
}

View File

@@ -0,0 +1,11 @@
using System.Collections.Generic;
using Orchard.AuditTrail.Models;
using Orchard.Localization;
namespace Orchard.AuditTrail.Services.Models {
public class AuditTrailCategoryDescriptor {
public string Category { get; set; }
public LocalizedString Name { get; set; }
public IEnumerable<AuditTrailEventDescriptor> Events { get; set; }
}
}

View File

@@ -0,0 +1,17 @@
using System.Collections.Generic;
using Orchard.Security;
namespace Orchard.AuditTrail.Services.Models {
public class AuditTrailContext {
public AuditTrailContext() {
EventData = new Dictionary<string, object>();
}
public string Event { get; set; }
public IUser User { get; set; }
public IDictionary<string, object> Properties { get; set; }
public IDictionary<string, object> EventData { get; set; }
public string EventFilterKey { get; set; }
public string EventFilterData { get; set; }
}
}

View File

@@ -0,0 +1,5 @@
namespace Orchard.AuditTrail.Services.Models {
public class AuditTrailCreateContext : AuditTrailContext {
public string Comment { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
using Orchard.Localization;
namespace Orchard.AuditTrail.Services.Models {
public class AuditTrailEventDescriptor {
public AuditTrailCategoryDescriptor CategoryDescriptor { get; set; }
public string Event { get; set; }
public LocalizedString Name { get; set; }
public LocalizedString Description { get; set; }
public bool IsEnabledByDefault { get; set; }
public bool IsMandatory { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
using System;
using Orchard.AuditTrail.Models;
namespace Orchard.AuditTrail.Services.Models {
public class AuditTrailFilterParameters {
public Filters Filters { get; set; }
public string UserName { get; set; }
public DateTime? From { get; set; }
public DateTime? To { get; set; }
}
}

View File

@@ -0,0 +1,7 @@
namespace Orchard.AuditTrail.Services.Models {
public enum AuditTrailOrderBy {
DateDescending,
CategoryAscending,
EventAscending
}
}

View File

@@ -0,0 +1,3 @@
namespace Orchard.AuditTrail.Services.Models {
public class AuditTrailSettingsContext {}
}

View File

@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.Localization;
namespace Orchard.AuditTrail.Services.Models {
public class DescribeContext {
private readonly IDictionary<string, DescribeFor> _describes = new Dictionary<string, DescribeFor>();
private readonly IList<Action<QueryFilterContext>> _queryFilters = new List<Action<QueryFilterContext>>();
private readonly IList<Action<DisplayFilterContext>> _filterDisplays = new List<Action<DisplayFilterContext>>();
public IEnumerable<Action<QueryFilterContext>> QueryFilters {
get { return _queryFilters; }
}
public IEnumerable<Action<DisplayFilterContext>> FilterDisplays {
get { return _filterDisplays; }
}
public IEnumerable<AuditTrailCategoryDescriptor> Describe() {
var query =
from d in _describes.Values
select new AuditTrailCategoryDescriptor {
Category = d.Category,
Name = d.Name,
Events = d.Events
};
return query.ToArray();
}
public DescribeFor For(string category, LocalizedString name) {
DescribeFor describeFor;
if (!_describes.TryGetValue(category, out describeFor)) {
describeFor = new DescribeFor(category, name);
_describes[category] = describeFor;
}
return describeFor;
}
public DescribeContext QueryFilter(Action<QueryFilterContext> queryAction) {
_queryFilters.Add(queryAction);
return this;
}
public DescribeContext DisplayFilter(Action<DisplayFilterContext> displayFilter) {
_filterDisplays.Add(displayFilter);
return this;
}
}
}

View File

@@ -0,0 +1,45 @@
using System.Collections.Generic;
using Orchard.AuditTrail.Helpers;
using Orchard.Localization;
namespace Orchard.AuditTrail.Services.Models {
public class DescribeFor {
private readonly IList<AuditTrailEventDescriptor> _events = new List<AuditTrailEventDescriptor>();
public DescribeFor(string category, LocalizedString name) {
Category = category;
Name = name;
}
public IEnumerable<AuditTrailEventDescriptor> Events {
get { return _events; }
}
public string Category { get; private set; }
public LocalizedString Name { get; private set; }
public DescribeFor Event(
IAuditTrailEventProvider provider,
string eventName,
LocalizedString name,
LocalizedString description,
bool enableByDefault = false,
bool isMandatory = false) {
_events.Add(new AuditTrailEventDescriptor {
CategoryDescriptor = new AuditTrailCategoryDescriptor {
Category = Category,
Name = Name,
Events = Events
},
Event = EventNameExtensions.GetFullyQualifiedEventName(provider.GetType(), eventName),
Name = name,
Description = description,
IsEnabledByDefault = enableByDefault,
IsMandatory = isMandatory
});
return this;
}
}
}

View File

@@ -0,0 +1,6 @@
namespace Orchard.AuditTrail.Services.Models {
public class Diff<T> {
public T OldValue { get; set; }
public T NewValue { get; set; }
}
}

View File

@@ -0,0 +1,5 @@
using System.Collections.Generic;
namespace Orchard.AuditTrail.Services.Models {
public class DiffDictionary<TKey, TValue> : Dictionary<TKey, Diff<TValue>> {}
}

View File

@@ -0,0 +1,15 @@
using Orchard.DisplayManagement;
namespace Orchard.AuditTrail.Services.Models {
public class DisplayFilterContext {
public DisplayFilterContext(IShapeFactory shapeFactory, Filters filters, dynamic filterDisplay) {
ShapeFactory = shapeFactory;
Filters = filters;
FilterDisplay = filterDisplay;
}
public dynamic ShapeFactory { get; set; }
public Filters Filters { get; set; }
public dynamic FilterDisplay { get; set; }
}
}

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using Orchard.ContentManagement;
namespace Orchard.AuditTrail.Services.Models {
public class Filters : Dictionary<string, string> {
public Filters(IUpdateModel updateModel) {
UpdateModel = updateModel;
}
public IUpdateModel UpdateModel { get; set; }
public static Filters From(NameValueCollection nameValues, IUpdateModel updateModel) {
var filters = new Filters(updateModel);
foreach (string nameValue in nameValues) {
filters.Add(nameValue, nameValues[nameValue]);
}
return filters;
}
public Filters AddFilter(string key, string value) {
Add(key, value);
return this;
}
}
}

View File

@@ -0,0 +1,14 @@
using System.Linq;
using Orchard.AuditTrail.Models;
namespace Orchard.AuditTrail.Services.Models {
public class QueryFilterContext {
public QueryFilterContext(IQueryable<AuditTrailEventRecord> query, Filters filters) {
Query = query;
Filters = filters;
}
public IQueryable<AuditTrailEventRecord> Query { get; set; }
public Filters Filters { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
using System.Globalization;
using Orchard.ContentManagement.MetaData.Builders;
namespace Orchard.AuditTrail.Settings {
public class AuditTrailPartSettings {
public AuditTrailPartSettings() {
ShowAuditTrail = true;
ShowAuditTrailCommentInput = true;
ShowAuditTrailLink = true;
}
public bool ShowAuditTrailLink { get; set; }
public bool ShowAuditTrail { get; set; }
public bool ShowAuditTrailCommentInput { get; set; }
public void Build(ContentTypePartDefinitionBuilder builder) {
builder.WithSetting("AuditTrailPartSettings.ShowAuditTrailLink", ShowAuditTrailLink.ToString(CultureInfo.InvariantCulture));
builder.WithSetting("AuditTrailPartSettings.ShowAuditTrail", ShowAuditTrail.ToString(CultureInfo.InvariantCulture));
builder.WithSetting("AuditTrailPartSettings.ShowAuditTrailCommentInput", ShowAuditTrailCommentInput.ToString(CultureInfo.InvariantCulture));
}
}
}

View File

@@ -0,0 +1,30 @@
using System.Collections.Generic;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentManagement.ViewModels;
namespace Orchard.AuditTrail.Settings {
public class AuditTrailPartSettingsEvents : ContentDefinitionEditorEventsBase {
public override IEnumerable<TemplateViewModel> TypePartEditor(ContentTypePartDefinition definition) {
if (definition.PartDefinition.Name != "AuditTrailPart")
yield break;
var settings = definition.Settings.GetModel<AuditTrailPartSettings>();
yield return DefinitionTemplate(settings);
}
public override IEnumerable<TemplateViewModel> TypePartEditorUpdate(ContentTypePartDefinitionBuilder builder, IUpdateModel updateModel) {
if (builder.Name != "AuditTrailPart")
yield break;
var settings = new AuditTrailPartSettings();
updateModel.TryUpdateModel(settings, "AuditTrailPartSettings", null, null);
settings.Build(builder);
yield return DefinitionTemplate(settings);
}
}
}

View File

@@ -0,0 +1,23 @@
using Orchard.AuditTrail.Helpers;
using Orchard.AuditTrail.Services;
using Orchard.AuditTrail.Services.Models;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
namespace Orchard.AuditTrail.Shapes {
public abstract class AuditTrailEventShapeAlteration<T> : IShapeTableProvider where T : IAuditTrailEventProvider {
protected abstract string EventName { get; }
public void Discover(ShapeTableBuilder builder) {
builder.Describe("AuditTrailEvent").OnDisplaying(context => {
var descriptor = (AuditTrailEventDescriptor) context.Shape.Descriptor;
if (descriptor.Event != EventNameExtensions.GetFullyQualifiedEventName<T>(EventName))
return;
OnAlterShape(context);
});
}
protected virtual void OnAlterShape(ShapeDisplayingContext context) {}
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
</staticContent>
<handlers accessPolicy="Script,Read">
<!--
iis7 - for any request to a file exists on disk, return it via native http module.
accessPolicy 'Script' is to allow for a managed 404 page.
-->
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
</handlers>
</system.webServer>
</configuration>

View File

@@ -0,0 +1,3 @@
.audittrail-content-eventmetadata {
margin-bottom: 1em;
}

View File

@@ -0,0 +1,22 @@
section.audittrail-contenttype-eventsummary table.items,
section.audittrail-contentpart-eventsummary table.items {
width: auto;
min-width: 600px;
margin: 0;
}
section.audittrail-contenttype-eventsummary table.items thead th,
section.audittrail-contentpart-eventsummary table.items thead th {
font-weight: bold;
}
section.audittrail-contenttype-eventsummary ul,
section.audittrail-contentpart-eventsummary ul {
list-style-type: square;
margin-left: 1.5em;
}
section.audittrail-contenttype-eventsummary ul li,
section.audittrail-contentpart-eventsummary ul li {
padding-left: 0.5em;
}

View File

@@ -0,0 +1,19 @@

.disabled-content-wrapper {
position: relative;
}
.disabled-content-overlay {
position: absolute;
left: -24px;
top: -16px;
right: -24px;
bottom: -28px;
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NEE1QTFEQkIwMTgzMTFFNDg4ODhCRjg2N0VEQjAxNTUiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NEE1QTFEQkEwMTgzMTFFNDg4ODhCRjg2N0VEQjAxNTUiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjg0Q0EwN0U3RjRCMTExRTM4MDlCRkYyQzE5M0JCODI3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjg0Q0EwN0U4RjRCMTExRTM4MDlCRkYyQzE5M0JCODI3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Xw2MNgAAAO9JREFUeNrs0gENAAAIwzDAv+djg4RWwrJOUvw1EhgAA2AADIABMAAGwAAYAANgAAyAATAABsAAGAADYAAMgAEwAAbAABgAA2AADIABMAAGwAAYAANgAAyAATAABsAAGAADYAAMgAEwAAbAABgAA2AADIABMAAGwAAYAANgAAyAATAABsAAGAADYAAMgAEwAAbAABgAA2AADIABMAAGwAAYAANgAAyAAQwggQEwAAbAABgAA2AADIABMAAGwAAYAANgAAyAATAABsAAGAADYAAMgAEwAAbAABgAA2AADIABMAAGwAAYAANgAAzADSvAAO13A/2zPw4VAAAAAElFTkSuQmCC');
}
.disabled-content-wrapper .edit-item-secondary,
.disabled-content-wrapper .audit-trail.expando-wrapper,
.disabled-content-wrapper .audit-trail-link {
display: none;
}

View File

@@ -0,0 +1,42 @@

.audittrail-filter-section {
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
justify-content: flex-start;
align-items: flex-end;
align-content: flex-end;
margin-bottom: 1em;
border: 1px solid #eaeaea;
background: #f5f5f5;
padding: 4px 10px 3px;
}
.filter-control-group {
margin-right: 10px;
margin-bottom: 6px;
}
.filter-control-group label {
padding-bottom: 0;
}
.filter-control-group select {
padding-top: 2px !important;
padding-bottom: 2px !important;
}
.audittrail-event-metadata-section {
margin-bottom: 1em;
}
.audittrail-event-metadata-section strong {
/*font-weight: bold;*/
}
.audittrail-list-section table .category-column,
.audittrail-list-section table .event-column,
.audittrail-list-section table .user-column,
.audittrail-list-section table .timestamp-column {
white-space: nowrap;
}

Some files were not shown because too many files have changed in this diff Show More