mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +08:00
Merged from 1.x.
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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> }
|
||||
|
||||
15
src/Orchard.Web/Modules/Orchard.AuditTrail/AdminMenu.cs
Normal file
15
src/Orchard.Web/Modules/Orchard.AuditTrail/AdminMenu.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
29
src/Orchard.Web/Modules/Orchard.AuditTrail/Migrations.cs
Normal file
29
src/Orchard.Web/Modules/Orchard.AuditTrail/Migrations.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Orchard.AuditTrail.Models {
|
||||
public class AuditTrailEventRecordResult {
|
||||
public AuditTrailEventRecord Record { get; set; }
|
||||
public bool IsDisabled { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Orchard.AuditTrail.Models {
|
||||
public class AuditTrailEventSetting {
|
||||
public string EventName { get; set; }
|
||||
public bool IsEnabled { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/Orchard.Web/Modules/Orchard.AuditTrail/Module.txt
Normal file
37
src/Orchard.Web/Modules/Orchard.AuditTrail/Module.txt
Normal 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
|
||||
@@ -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>
|
||||
30
src/Orchard.Web/Modules/Orchard.AuditTrail/Permissions.cs
Normal file
30
src/Orchard.Web/Modules/Orchard.AuditTrail/Permissions.cs
Normal 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. */
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/Orchard.Web/Modules/Orchard.AuditTrail/Placement.info
Normal file
11
src/Orchard.Web/Modules/Orchard.AuditTrail/Placement.info
Normal 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>
|
||||
@@ -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")]
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Orchard.AuditTrail.Providers.Content {
|
||||
public enum DiffType {
|
||||
Change,
|
||||
Addition
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
$(function() {
|
||||
$(".expando-wrapper > legend").expandoControl(
|
||||
function(controller) {
|
||||
return controller.nextAll(".expando");
|
||||
},
|
||||
{
|
||||
collapse: true,
|
||||
remember: true
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) { }
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Orchard.AuditTrail.Models;
|
||||
|
||||
namespace Orchard.AuditTrail.Services {
|
||||
public interface IAuditTrailEventDisplayBuilder : IDependency {
|
||||
dynamic BuildDisplay(AuditTrailEventRecord record, string displayType);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Orchard.AuditTrail.Services.Models;
|
||||
using Orchard.Events;
|
||||
|
||||
namespace Orchard.AuditTrail.Services {
|
||||
public interface IAuditTrailSettingsEventHandler : IEventHandler {
|
||||
void Updated(AuditTrailSettingsContext context);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Orchard.AuditTrail.Services.Models {
|
||||
public class AuditTrailCreateContext : AuditTrailContext {
|
||||
public string Comment { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Orchard.AuditTrail.Services.Models {
|
||||
public enum AuditTrailOrderBy {
|
||||
DateDescending,
|
||||
CategoryAscending,
|
||||
EventAscending
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace Orchard.AuditTrail.Services.Models {
|
||||
public class AuditTrailSettingsContext {}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Orchard.AuditTrail.Services.Models {
|
||||
public class Diff<T> {
|
||||
public T OldValue { get; set; }
|
||||
public T NewValue { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Orchard.AuditTrail.Services.Models {
|
||||
public class DiffDictionary<TKey, TValue> : Dictionary<TKey, Diff<TValue>> {}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {}
|
||||
}
|
||||
}
|
||||
16
src/Orchard.Web/Modules/Orchard.AuditTrail/Styles/Web.config
Normal file
16
src/Orchard.Web/Modules/Orchard.AuditTrail/Styles/Web.config
Normal 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>
|
||||
@@ -0,0 +1,3 @@
|
||||
.audittrail-content-eventmetadata {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user