diff --git a/src/Umbraco.Core/DataTableExtensions.cs b/src/Umbraco.Core/DataTableExtensions.cs
new file mode 100644
index 0000000000..38b49cae12
--- /dev/null
+++ b/src/Umbraco.Core/DataTableExtensions.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using umbraco.interfaces;
+
+namespace Umbraco.Core
+{
+
+
+ ///
+ /// Static and extension methods for the DataTable object
+ ///
+ internal static class DataTableExtensions
+ {
+
+ private static readonly Hashtable AliasToNames = new Hashtable();
+ private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
+
+ ///
+ /// Creates a DataTable with the specified alias and columns and uses a callback to populate the headers.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// This has been migrated from the Node class and uses proper locking now. It is now used by the Node class and the
+ /// DynamicDocument extensions for legacy reasons.
+ ///
+ public static DataTable GenerateDataTable(
+ string alias,
+ Func>> getHeaders,
+ Func>, IEnumerable>>>> rowData)
+ {
+ var dt = new DataTable(alias);
+
+ //get the standard column headers from the standard data (not user data)
+ var tableData = rowData();
+ var standardColHeaders = tableData.SelectMany(x => x.Item1).Select(x => x.Key).Distinct();
+
+ var userPropColHeaders = new List();
+ // get user property column headers
+ var propertyHeaders = GetPropertyHeaders(alias, getHeaders);
+ var ide = propertyHeaders.GetEnumerator();
+ while (ide.MoveNext())
+ {
+ userPropColHeaders.Add(ide.Value.ToString());
+ }
+
+ //now add all the columns, standard val headers first, then user val headers
+ foreach (var dc in standardColHeaders.Union(userPropColHeaders).Select(c => new DataColumn(c)))
+ {
+ dt.Columns.Add(dc);
+ }
+
+ //add row data
+ foreach(var r in rowData())
+ {
+ dt.PopulateRow(
+ (Hashtable)AliasToNames[alias],
+ r.Item1,
+ r.Item2);
+ }
+
+ return dt;
+ }
+
+ ///
+ /// Helper method to return this ugly object
+ ///
+ ///
+ ///
+ /// This is for legacy code, I didn't want to go creating custom classes for these
+ ///
+ internal static List>, IEnumerable>>> CreateTableData()
+ {
+ return new List>, IEnumerable>>>();
+ }
+
+ ///
+ /// Helper method to deal with these ugly objects
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// This is for legacy code, I didn't want to go creating custom classes for these
+ ///
+ internal static void AddRowData(
+ List>, IEnumerable>>> rowData,
+ IEnumerable> standardVals,
+ IEnumerable> userVals)
+ {
+ rowData.Add(new System.Tuple>, IEnumerable>>(
+ standardVals,
+ userVals
+ ));
+ }
+
+ private static Hashtable GetPropertyHeaders(string alias, Func>> getHeaders)
+ {
+ using (var l = new UpgradeableReadLock(Lock))
+ {
+ if (AliasToNames.ContainsKey(alias))
+ return (Hashtable)AliasToNames[alias];
+
+ l.UpgradeToWriteLock();
+
+ var headers = getHeaders(alias);
+ var def = new Hashtable();
+ foreach (var pt in headers)
+ def.Add(pt.Key, pt.Value);
+ AliasToNames.Add(alias, def);
+
+ return def;
+ }
+
+ }
+
+ private static void PopulateRow(
+ this DataTable dt,
+ IDictionary aliasesToNames,
+ IEnumerable> standardVals,
+ IEnumerable> userPropertyVals)
+ {
+ var dr = dt.NewRow();
+ foreach (var r in standardVals)
+ {
+ dr[r.Key] = r.Value;
+ }
+ foreach (var p in userPropertyVals.Where(p => p.Value != null))
+ {
+ dr[aliasesToNames[p.Key].ToString()] = p.Value;
+ }
+ dt.Rows.Add(dr);
+ }
+
+ }
+}
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 567064cc6e..9a56378674 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -56,6 +56,7 @@
+
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/nodeFactory/Node.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/nodeFactory/Node.cs
index 3ccf83d5a7..2db509fa87 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/nodeFactory/Node.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/nodeFactory/Node.cs
@@ -9,6 +9,7 @@ using System.Xml.XPath;
using umbraco.cms.businesslogic;
using umbraco.cms.businesslogic.propertytype;
using umbraco.interfaces;
+using Umbraco.Core;
namespace umbraco.NodeFactory
{
@@ -358,30 +359,11 @@ namespace umbraco.NodeFactory
public DataTable ChildrenAsTable()
{
- if (Children.Count > 0)
- {
- DataTable dt = generateDataTable(Children[0]);
-
- string firstNodeTypeAlias = Children[0].NodeTypeAlias;
-
- foreach (Node n in Children)
- {
- if (n.NodeTypeAlias == firstNodeTypeAlias)
- {
- DataRow dr = dt.NewRow();
- populateRow(ref dr, n, getPropertyHeaders(n));
- dt.Rows.Add(dr);
- }
- }
- return dt;
- }
- else
- return new DataTable();
+ return Children.Count > 0 ? GenerateDataTable(Children[0]) : new DataTable();
}
public DataTable ChildrenAsTable(string nodeTypeAliasFilter)
{
-
if (Children.Count > 0)
{
@@ -399,94 +381,58 @@ namespace umbraco.NodeFactory
if (nodeFound)
{
- DataTable dt = generateDataTable(Firstnode);
-
- foreach (Node n in Children)
- {
- if (n.NodeTypeAlias == nodeTypeAliasFilter)
- {
- DataRow dr = dt.NewRow();
- populateRow(ref dr, n, getPropertyHeaders(n));
- dt.Rows.Add(dr);
- }
- }
- return dt;
+ return GenerateDataTable(Firstnode);
}
- else
- {
- return new DataTable();
- }
- }
- else
return new DataTable();
+ }
+ return new DataTable();
}
- private DataTable generateDataTable(Node SchemaNode)
+ private DataTable GenerateDataTable(INode node)
{
- DataTable NodeAsDataTable = new DataTable(SchemaNode.NodeTypeAlias);
- string[] defaultColumns = {
- "Id", "NodeName", "NodeTypeAlias", "CreateDate", "UpdateDate", "CreatorName",
- "WriterName", "Url"
- };
- foreach (string s in defaultColumns)
- {
- DataColumn dc = new DataColumn(s);
- NodeAsDataTable.Columns.Add(dc);
- }
-
- // add properties
- Hashtable propertyHeaders = getPropertyHeaders(SchemaNode);
- IDictionaryEnumerator ide = propertyHeaders.GetEnumerator();
- while (ide.MoveNext())
- {
- DataColumn dc = new DataColumn(ide.Value.ToString());
- NodeAsDataTable.Columns.Add(dc);
- }
-
- return NodeAsDataTable;
+ //use new utility class to create table so that we don't have to maintain code in many places, just one
+ var dt = Umbraco.Core.DataTableExtensions.GenerateDataTable(
+ //pass in the alias
+ node.NodeTypeAlias,
+ //pass in the callback to extract the Dictionary of user defined aliases to their names
+ alias =>
+ {
+ var ct = ContentType.GetByAlias(alias);
+ return ct.PropertyTypes.ToDictionary(x => x.Alias, x => x.Name);
+ },
+ //pass in a callback to populate the datatable, yup its a bit ugly but it's already legacy and we just want to maintain code in one place.
+ () =>
+ {
+ //create all row data
+ var tableData = Umbraco.Core.DataTableExtensions.CreateTableData();
+ //loop through each child and create row data for it
+ foreach (Node n in Children)
+ {
+ var standardVals = new Dictionary()
+ {
+ {"Id", n.Id},
+ {"NodeName", n.Name},
+ {"NodeTypeAlias", n.NodeTypeAlias},
+ {"CreateDate", n.CreateDate},
+ {"UpdateDate", n.UpdateDate},
+ {"CreatorName", n.CreatorName},
+ {"WriterName", n.WriterName},
+ {"Url", library.NiceUrl(n.Id)}
+ };
+ var userVals = new Dictionary();
+ foreach (var p in from Property p in n.Properties where p.Value != null select p)
+ {
+ userVals[p.Alias] = p.Value;
+ }
+ //add the row data
+ Umbraco.Core.DataTableExtensions.AddRowData(tableData, standardVals, userVals);
+ }
+ return tableData;
+ }
+ );
+ return dt;
}
- private Hashtable getPropertyHeaders(Node SchemaNode)
- {
- if (_aliasToNames.ContainsKey(SchemaNode.NodeTypeAlias))
- return (Hashtable)_aliasToNames[SchemaNode.NodeTypeAlias];
- else
- {
- ContentType ct = ContentType.GetByAlias(SchemaNode.NodeTypeAlias);
- Hashtable def = new Hashtable();
- foreach (PropertyType pt in ct.PropertyTypes)
- def.Add(pt.Alias, pt.Name);
- System.Web.HttpContext.Current.Application.Lock();
- _aliasToNames.Add(SchemaNode.NodeTypeAlias, def);
- System.Web.HttpContext.Current.Application.UnLock();
-
- return def;
- }
- }
-
- private void populateRow(ref DataRow dr, Node n, Hashtable AliasesToNames)
- {
- dr["Id"] = n.Id;
- dr["NodeName"] = n.Name;
- dr["NodeTypeAlias"] = n.NodeTypeAlias;
- dr["CreateDate"] = n.CreateDate;
- dr["UpdateDate"] = n.UpdateDate;
- dr["CreatorName"] = n.CreatorName;
- dr["WriterName"] = n.WriterName;
- dr["Url"] = library.NiceUrl(n.Id);
-
- int counter = 8;
- foreach (Property p in n.Properties)
- {
- if (p.Value != null)
- {
- dr[AliasesToNames[p.Alias].ToString()] = p.Value;
- counter++;
- }
- }
- }
-
-
private void initializeStructure()
{
// Load parent if it exists and is a node
diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicDocumentExtensions.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicDocumentExtensions.cs
index bbafe791aa..055d4cf416 100644
--- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicDocumentExtensions.cs
+++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicDocumentExtensions.cs
@@ -22,15 +22,15 @@ namespace umbraco.MacroEngines.Library
internal static INode ConvertToNode(this DynamicDocument doc)
{
- var node = new SimpleNode(doc);
+ var node = new ConvertedNode(doc);
return node;
}
- private class SimpleNode : INode
+ private class ConvertedNode : INode
{
private readonly DynamicDocument _doc;
- public SimpleNode(DynamicDocument doc)
+ public ConvertedNode(DynamicDocument doc)
{
_doc = doc;
template = doc.TemplateId;