From eca823ea900ba3650f2f38d2201026196debdbc3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 15 Nov 2013 13:49:20 +1100 Subject: [PATCH 1/2] Fix for U4-3529 Ysod editing Partial View --- src/Umbraco.Web/UI/Controls/InsertMacroSplitButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/UI/Controls/InsertMacroSplitButton.cs b/src/Umbraco.Web/UI/Controls/InsertMacroSplitButton.cs index 4fa9146dbc..5c6db74513 100644 --- a/src/Umbraco.Web/UI/Controls/InsertMacroSplitButton.cs +++ b/src/Umbraco.Web/UI/Controls/InsertMacroSplitButton.cs @@ -137,7 +137,7 @@ namespace Umbraco.Web.UI.Controls private bool DoesMacroHaveParameters(int macroId) { - return ApplicationContext.DatabaseContext.Database.ExecuteScalar(string.Format("select 1 from cmsMacroProperty where macro = {0}", macroId)) == 1; + return ApplicationContext.DatabaseContext.Database.ExecuteScalar(string.Format("SELECT COUNT(*) from cmsMacroProperty where macro = {0}", macroId)) > 0; } } } From b4c005c1408d12d9078a1a1587a8003d95fd3a75 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 15 Nov 2013 14:53:26 +1100 Subject: [PATCH 2/2] Fixes up the EntityRepository to use dynamics for media as well so that *all* fields in the main table are returned so we can store this information in AdditionalData, now that this is working nicely we should make the entity repo lookup the properties for content too just like we're doing for media since these get put into AdditionalData as well and there's really not much overhead to do that. Conflicts: src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs --- src/Umbraco.Core/Models/UmbracoObjectTypes.cs | 1 + .../Factories/UmbracoEntityFactory.cs | 36 +++- .../Repositories/EntityRepository.cs | 185 ++++++++++++------ 3 files changed, 153 insertions(+), 69 deletions(-) diff --git a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs index e04e20ff0a..c9967c0688 100644 --- a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs +++ b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs @@ -61,6 +61,7 @@ namespace Umbraco.Core.Models [FriendlyName("Member Group")] MemberGroup, + //TODO: What is a 'Content Item' supposed to be??? /// /// Content Item /// diff --git a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs index 226294be0f..cdbab3d0c1 100644 --- a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs @@ -1,15 +1,33 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; +using System.Reflection; using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Persistence.Repositories; namespace Umbraco.Core.Persistence.Factories { internal class UmbracoEntityFactory : IEntityFactory { + internal void AddAdditionalData(UmbracoEntity entity, IDictionary originalEntityProperties) + { + var entityProps = TypeHelper.GetPublicProperties(typeof(IUmbracoEntity)).Select(x => x.Name).ToArray(); + + //figure out what extra properties we have that are not on the IUmbracoEntity and add them to additional data + foreach (var k in originalEntityProperties.Keys + .Select(x => new { orig = x, title = x.ConvertCase(StringAliasCaseType.PascalCase) }) + .Where(x => entityProps.InvariantContains(x.title) == false)) + { + entity.AdditionalData[k.title] = originalEntityProperties[k.orig]; + } + } + internal UmbracoEntity BuildEntityFromDynamic(dynamic d) { + var asDictionary = (IDictionary)d; + var entity = new UmbracoEntity(d.trashed) { CreateDate = d.createDate, @@ -23,14 +41,12 @@ namespace Umbraco.Core.Persistence.Factories Path = d.path, SortOrder = d.sortOrder, HasChildren = d.children > 0, - ContentTypeAlias = d.alias ?? string.Empty, - ContentTypeIcon = d.icon ?? string.Empty, - ContentTypeThumbnail = d.thumbnail ?? string.Empty, + ContentTypeAlias = asDictionary.ContainsKey("alias") ? (d.alias ?? string.Empty) : string.Empty, + ContentTypeIcon = asDictionary.ContainsKey("icon") ? (d.icon ?? string.Empty) : string.Empty, + ContentTypeThumbnail = asDictionary.ContainsKey("thumbnail") ? (d.thumbnail ?? string.Empty) : string.Empty, UmbracoProperties = new List() }; - var asDictionary = (IDictionary)d; - var publishedVersion = default(Guid); //some content items don't have a published version if (asDictionary.ContainsKey("publishedVersion") && asDictionary["publishedVersion"] != null) @@ -38,12 +54,18 @@ namespace Umbraco.Core.Persistence.Factories Guid.TryParse(d.publishedVersion.ToString(), out publishedVersion); } var newestVersion = default(Guid); - Guid.TryParse(d.newestVersion.ToString(), out newestVersion); + if (asDictionary.ContainsKey("newestVersion") && d.newestVersion != null) + { + Guid.TryParse(d.newestVersion.ToString(), out newestVersion); + } entity.IsPublished = publishedVersion != default(Guid) || (newestVersion != default(Guid) && publishedVersion == newestVersion); entity.IsDraft = newestVersion != default(Guid) && (publishedVersion == default(Guid) || publishedVersion != newestVersion); entity.HasPendingChanges = (publishedVersion != default(Guid) && newestVersion != default(Guid)) && publishedVersion != newestVersion; - + + //Now we can assign the additional data! + AddAdditionalData(entity, asDictionary); + return entity; } diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs index bbfea4082a..c935d1626c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs @@ -49,15 +49,43 @@ namespace Umbraco.Core.Persistence.Repositories #region Query Methods - public virtual IUmbracoEntity Get(int id) + public IUmbracoEntity GetByKey(Guid key) { - var sql = GetBaseWhere(GetBase, false, false, id); - var nodeDto = _work.Database.FirstOrDefault(sql); + var sql = GetBaseWhere(GetBase, false, false, key); + var nodeDto = _work.Database.FirstOrDefault(sql); if (nodeDto == null) return null; var factory = new UmbracoEntityFactory(); - var entity = factory.BuildEntity(nodeDto); + var entity = factory.BuildEntityFromDynamic(nodeDto); + + return entity; + } + + public IUmbracoEntity GetByKey(Guid key, Guid objectTypeId) + { + bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document); + bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media); + var sql = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId, key).Append(GetGroupBy(isContent, isMedia)); + var nodeDto = _work.Database.FirstOrDefault(sql); + if (nodeDto == null) + return null; + + var factory = new UmbracoEntityFactory(); + var entity = factory.BuildEntityFromDynamic(nodeDto); + + return entity; + } + + public virtual IUmbracoEntity Get(int id) + { + var sql = GetBaseWhere(GetBase, false, false, id); + var nodeDto = _work.Database.FirstOrDefault(sql); + if (nodeDto == null) + return null; + + var factory = new UmbracoEntityFactory(); + var entity = factory.BuildEntityFromDynamic(nodeDto); return entity; } @@ -67,12 +95,13 @@ namespace Umbraco.Core.Persistence.Repositories bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document); bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media); var sql = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId, id).Append(GetGroupBy(isContent, isMedia)); - var nodeDto = _work.Database.FirstOrDefault(sql); + + var nodeDto = _work.Database.FirstOrDefault(sql); if (nodeDto == null) return null; var factory = new UmbracoEntityFactory(); - var entity = factory.BuildEntity(nodeDto); + var entity = factory.BuildEntityFromDynamic(nodeDto); return entity; } @@ -91,16 +120,27 @@ namespace Umbraco.Core.Persistence.Repositories bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document); bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media); var sql = GetBaseWhere(GetBase, isContent, isMedia, string.Empty, objectTypeId).Append(GetGroupBy(isContent, isMedia)); - var dtos = isMedia - ? _work.Database.Fetch( - new UmbracoEntityRelator().Map, sql) - : _work.Database.Fetch(sql); + var factory = new UmbracoEntityFactory(); - foreach (var dto in dtos) + if (isMedia) { - var entity = factory.BuildEntity(dto); - yield return entity; + //for now treat media differently + //TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields + var entities = _work.Database.Fetch( + new UmbracoEntityRelator().Map, sql); + foreach (var entity in entities) + { + yield return entity; + } + } + else + { + var dtos = _work.Database.Fetch(sql); + foreach (var entity in dtos.Select(dto => factory.BuildEntityFromDynamic(dto))) + { + yield return entity; + } } } } @@ -112,10 +152,10 @@ namespace Umbraco.Core.Persistence.Repositories var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate().Append(GetGroupBy(false, false)); - var dtos = _work.Database.Fetch(sql); + var dtos = _work.Database.Fetch(sql); var factory = new UmbracoEntityFactory(); - var list = dtos.Select(factory.BuildEntity).Cast().ToList(); + var list = dtos.Select(factory.BuildEntityFromDynamic).Cast().ToList(); return list; } @@ -134,42 +174,22 @@ namespace Umbraco.Core.Persistence.Repositories if (isMedia) { - //treat media differently for now - var dtos = _work.Database.Fetch( + //treat media differently for now + //TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields + var entities = _work.Database.Fetch( new UmbracoEntityRelator().Map, sql); - return dtos.Select(factory.BuildEntity).Cast().ToArray(); + return entities; } else { - //use dynamic so that we can get ALL properties from the SQL - //we'll have to stitch stuff together manually but we can get our - //additional data to put in the dictionary. + //use dynamic so that we can get ALL properties from the SQL so we can chuck that data into our AdditionalData var dtos = _work.Database.Fetch(sql); - var entityProps = TypeHelper.GetPublicProperties(typeof (IUmbracoEntity)).Select(x => x.Name).ToArray(); - var result = new List(); - foreach (var d in dtos) - { - //build the initial entity - IUmbracoEntity entity = factory.BuildEntityFromDynamic(d); - - //convert the dynamic row to dictionary - var asDictionary = (IDictionary) d; - - //figure out what extra properties we have that are not on the IUmbracoEntity and add them to additional data - foreach (var k in asDictionary.Keys - .Select(x => new {orig = x, title = x.ConvertCase(StringAliasCaseType.PascalCase)}) - .Where(x => entityProps.InvariantContains(x.title) == false)) - { - entity.AdditionalData[k.title] = asDictionary[k.orig]; - } - - result.Add(entity); - } - return result; + return dtos.Select(factory.BuildEntityFromDynamic).Cast().ToList(); } } #endregion + #region Sql Statements @@ -239,26 +259,42 @@ namespace Umbraco.Core.Persistence.Repositories return sql; } - protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, bool isMedia, string additionWhereStatement, Guid id) + protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, bool isMedia, string additionWhereStatement, Guid nodeObjectType) { var sql = baseQuery(isContent, isMedia, additionWhereStatement) - .Where("umbracoNode.nodeObjectType = @NodeObjectType", new { NodeObjectType = id }); + .Where("umbracoNode.nodeObjectType = @NodeObjectType", new { NodeObjectType = nodeObjectType }); return sql; } protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, bool isMedia, int id) { - var sql = baseQuery(isContent, isMedia, " AND umbracoNode.id = '"+ id +"'") + var sql = baseQuery(isContent, isMedia, " AND umbracoNode.id = '" + id + "'") .Where("umbracoNode.id = @Id", new { Id = id }) .Append(GetGroupBy(isContent, isMedia)); return sql; } - protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, bool isMedia, Guid objectId, int id) + protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, bool isMedia, Guid key) { - var sql = baseQuery(isContent, isMedia, " AND umbracoNode.id = '"+ id +"'") + var sql = baseQuery(isContent, isMedia, " AND umbracoNode.uniqueID = '" + key + "'") + .Where("umbracoNode.uniqueID = @UniqueID", new { UniqueID = key }) + .Append(GetGroupBy(isContent, isMedia)); + return sql; + } + + protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, bool isMedia, Guid nodeObjectType, int id) + { + var sql = baseQuery(isContent, isMedia, " AND umbracoNode.id = '" + id + "'") .Where("umbracoNode.id = @Id AND umbracoNode.nodeObjectType = @NodeObjectType", - new {Id = id, NodeObjectType = objectId}); + new { Id = id, NodeObjectType = nodeObjectType }); + return sql; + } + + protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, bool isMedia, Guid nodeObjectType, Guid key) + { + var sql = baseQuery(isContent, isMedia, " AND umbracoNode.uniqueID = '" + key + "'") + .Where("umbracoNode.uniqueID = @UniqueID AND umbracoNode.nodeObjectType = @NodeObjectType", + new { UniqueID = key, NodeObjectType = nodeObjectType }); return sql; } @@ -352,11 +388,20 @@ namespace Umbraco.Core.Persistence.Repositories public string UmbracoFile { get; set; } } + /// + /// This is a special relator in that it is not returning a DTO but a real resolved entity and that it accepts + /// a dynamic instance. + /// + /// + /// We're doing this because when we query the db, we want to use dynamic so that it returns all available fields not just the ones + /// defined on the entity so we can them to additional data + /// internal class UmbracoEntityRelator { - internal UmbracoEntityDto Current; + internal UmbracoEntity Current; + private readonly UmbracoEntityFactory _factory = new UmbracoEntityFactory(); - internal UmbracoEntityDto Map(UmbracoEntityDto a, UmbracoPropertyDto p) + internal UmbracoEntity Map(dynamic a, UmbracoPropertyDto p) { // Terminating call. Since we can return null from this function // we need to be ready for PetaPoco to callback later with null @@ -364,28 +409,44 @@ namespace Umbraco.Core.Persistence.Repositories if (a == null) return Current; - // Is this the same UmbracoEntityDto as the current one we're processing - if (Current != null && Current.UniqueId == a.UniqueId) + // Is this the same UmbracoEntity as the current one we're processing + if (Current != null && Current.Key == a.uniqueID) { - // Yes, just add this UmbracoPropertyDto to the current UmbracoEntityDto's collection - Current.UmbracoPropertyDtos.Add(p); - - // Return null to indicate we're not done with this UmbracoEntityDto yet + // Yes, just add this UmbracoProperty to the current UmbracoEntity's collection + if (Current.UmbracoProperties == null) + { + Current.UmbracoProperties = new List(); + } + Current.UmbracoProperties.Add(new UmbracoEntity.UmbracoProperty + { + DataTypeControlId = p.DataTypeControlId, + Value = p.UmbracoFile + }); + // Return null to indicate we're not done with this UmbracoEntity yet return null; } - // This is a different UmbracoEntityDto to the current one, or this is the + // This is a different UmbracoEntity to the current one, or this is the // first time through and we don't have a Tab yet // Save the current UmbracoEntityDto var prev = Current; - // Setup the new current UmbracoEntityDto - Current = a; - Current.UmbracoPropertyDtos = new List(); - Current.UmbracoPropertyDtos.Add(p); + // Setup the new current UmbracoEntity + + Current = _factory.BuildEntityFromDynamic(a); - // Return the now populated previous UmbracoEntityDto (or null if first time through) + //add the property/create the prop list if null + Current.UmbracoProperties = new List + { + new UmbracoEntity.UmbracoProperty + { + DataTypeControlId = p.DataTypeControlId, + Value = p.UmbracoFile + } + }; + + // Return the now populated previous UmbracoEntity (or null if first time through) return prev; } }