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;