diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Orchard.AuditTrail.csproj b/src/Orchard.Web/Modules/Orchard.AuditTrail/Orchard.AuditTrail.csproj index 235f13d75..0bc6645e7 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Orchard.AuditTrail.csproj +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Orchard.AuditTrail.csproj @@ -69,9 +69,14 @@ + + False + Lib\XmlDiffPatch\xmldiffpatch.dll + + @@ -141,6 +146,10 @@ + + + + diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/ContentAuditTrailEventShapes.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/ContentAuditTrailEventShapes.cs index 9b6fe391f..a301a1ae5 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/ContentAuditTrailEventShapes.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/ContentAuditTrailEventShapes.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Orchard.AuditTrail.Helpers; using Orchard.AuditTrail.Models; using Orchard.ContentManagement; @@ -8,8 +9,11 @@ using Orchard.Environment; namespace Orchard.AuditTrail.Providers.Content { public class ContentAuditTrailEventShapes : IShapeTableProvider { private readonly Work _contentManager; - public ContentAuditTrailEventShapes(Work contentManager) { + private readonly IDiffGramAnalyzer _analyzer; + + public ContentAuditTrailEventShapes(Work contentManager, IDiffGramAnalyzer analyzer) { _contentManager = contentManager; + _analyzer = analyzer; } public void Discover(ShapeTableBuilder builder) { @@ -25,6 +29,14 @@ namespace Orchard.AuditTrail.Providers.Content { var contentItem = _contentManager.Value.Get(contentItemId); var previousVersion = previousContentItemVersionId > 0 ? _contentManager.Value.Get(contentItemId, VersionOptions.VersionRecord(previousContentItemVersionId)) : default(ContentItem); + if (previousVersion != null) { + var previousVersionXml = _contentManager.Value.Export(previousVersion); + var currentVersionXml = _contentManager.Value.Export(contentItem); + var diffGram = _analyzer.GenerateDiffGram(previousVersionXml, currentVersionXml); + var diffNodes = _analyzer.Analyze(previousVersionXml, diffGram).ToArray(); + context.Shape.DiffNodes = diffNodes; + } + context.Shape.ContentItem = contentItem; context.Shape.PreviousVersion = previousVersion; }); diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/DiffGramAnalyzer.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/DiffGramAnalyzer.cs new file mode 100644 index 000000000..d1029b10c --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/DiffGramAnalyzer.cs @@ -0,0 +1,98 @@ +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 Analyze(XElement original, XElement diffGram) { + var stack = new Stack(); + + stack.Push(new XElement("original", original)); + + using (var reader = diffGram.CreateReader()) { + while (!reader.EOF) { + var doRead = true; + if (reader.LocalName == "xmldiff") + reader.Read(); + 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(); + doRead = false; + yield return + new DiffNode { + Type = DiffType.Change, + ElementName = attributeName, + Previous = originalValue, + Current = currentValue + }; + } + else { + var originalContent = currentElement.Value; + var currentContent = reader.ReadElementContentAsString(); + stack.Pop(); + doRead = false; + yield return + new DiffNode { + Type = DiffType.Change, + ElementName = currentElement.Name.ToString(), + Previous = originalContent, + Current = currentContent + }; + } + break; + case "add": + reader.Read(); + var addedElementContent = reader.ReadElementContentAsString(); + yield return new DiffNode { Type = DiffType.Addition, ElementName = reader.Name, Current = addedElementContent }; + break; + } + } + break; + case XmlNodeType.EndElement: + stack.Pop(); + break; + } + if (doRead) + reader.Read(); + } + } + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/DiffNode.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/DiffNode.cs new file mode 100644 index 000000000..b7e88b108 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/DiffNode.cs @@ -0,0 +1,8 @@ +namespace Orchard.AuditTrail.Providers.Content { + public class DiffNode { + public DiffType Type { get; set; } + public string ElementName { get; set; } + public string Previous { get; set; } + public string Current { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/DiffType.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/DiffType.cs new file mode 100644 index 000000000..53eb53105 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/DiffType.cs @@ -0,0 +1,6 @@ +namespace Orchard.AuditTrail.Providers.Content { + public enum DiffType { + Change, + Addition + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/IDiffGramAnalyzer.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/IDiffGramAnalyzer.cs new file mode 100644 index 000000000..13e0a55c5 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Providers/Content/IDiffGramAnalyzer.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using System.Xml.Linq; + +namespace Orchard.AuditTrail.Providers.Content { + public interface IDiffGramAnalyzer : IDependency { + XElement GenerateDiffGram(XElement element1, XElement element2); + IEnumerable Analyze(XElement original, XElement diffGram); + } +} \ No newline at end of file