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