From 5914690ad8839721ad60612af7f05e393e0065cd Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 25 Mar 2013 10:09:02 -0100 Subject: [PATCH 01/11] Allow leading sign in number field so that negative numbers are supported --- src/umbraco.editorControls/numberfield/numberField.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/umbraco.editorControls/numberfield/numberField.cs b/src/umbraco.editorControls/numberfield/numberField.cs index f5552e62ec..104cce102e 100644 --- a/src/umbraco.editorControls/numberfield/numberField.cs +++ b/src/umbraco.editorControls/numberfield/numberField.cs @@ -75,8 +75,8 @@ namespace umbraco.editorControls set { int integer; - - if (int.TryParse(value, NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out integer)) + + if (int.TryParse(value, NumberStyles.AllowThousands | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out integer)) { base.Text = integer.ToString(); } From 0e392b398c3b2f5708e904b46624a020a1ccd8e9 Mon Sep 17 00:00:00 2001 From: Stephen Roberts Date: Thu, 14 Mar 2013 11:08:02 +0000 Subject: [PATCH 02/11] Fixed Property order for document types with Master document types. --- .../umbraco.presentation/umbraco/controls/ContentControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentControl.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentControl.cs index 2b8df31950..0603bf125f 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentControl.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentControl.cs @@ -133,7 +133,7 @@ namespace umbraco.controls // Iterate through the property types and add them to the tab // zb-00036 #29889 : fix property types getter to get the right set of properties // ge : had a bit of a corrupt db and got weird NRE errors so rewrote this to catch the error and rethrow with detail - foreach (PropertyType propertyType in tab.GetPropertyTypes(_content.ContentType.Id)) + foreach (PropertyType propertyType in tab.GetPropertyTypes(_content.ContentType.Id).OrderBy(x=>x.SortOrder)) { // table.Rows.Add(addControl(_content.getProperty(editPropertyType.Alias), tp)); var property = _content.getProperty(propertyType); From d1e02f506c8712f9e3df9bc388cf93573b2a19cb Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 25 Mar 2013 07:51:44 -0100 Subject: [PATCH 03/11] PropertyGroupId can be null --- .../umbraco/controls/ContentTypeControlNew.ascx.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs index 97328a6363..331e65f01d 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs @@ -651,7 +651,7 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); propertyType.DataTypeDefinitionId = dataTypeDefinition.Id; propertyType.DataTypeId = dataTypeDefinition.ControlId; - if (propertyType.PropertyGroupId.Value != gpw.GenricPropertyControl.Tab) + if (propertyType.PropertyGroupId == null || propertyType.PropertyGroupId.Value != gpw.GenricPropertyControl.Tab) { if (gpw.GenricPropertyControl.Tab == 0) { From ab40822a984dca1412ec670db29eec8c9ee3392b Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 25 Mar 2013 10:30:55 -0100 Subject: [PATCH 04/11] Fix merge issue by re-applying Stephen Roberts' patch --- .../umbraco.presentation/umbraco/controls/ContentControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentControl.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentControl.cs index e9ceb046bc..16cb49792e 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentControl.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentControl.cs @@ -209,7 +209,7 @@ namespace umbraco.controls // zb-00036 #29889 : fix property types getter to get the right set of properties // ge : had a bit of a corrupt db and got weird NRE errors so rewrote this to catch the error and rethrow with detail var propertyTypes = tab.GetPropertyTypes(_content.ContentType.Id); - foreach (var propertyType in propertyTypes) + foreach (var propertyType in propertyTypes.OrderBy(x => x.SortOrder)) { var property = _content.getProperty(propertyType); if (property != null && tabPage != null) From 3ea9963166b878d4e03f337c783474cf8158bb1d Mon Sep 17 00:00:00 2001 From: Brian Powell Date: Mon, 25 Mar 2013 10:39:35 -0100 Subject: [PATCH 05/11] Fixes U4-1904 / U4-1905 --- .../Persistence/Querying/PocoToSqlExpressionHelper.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs index dc5b608e25..456607a348 100644 --- a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs @@ -468,6 +468,9 @@ namespace Umbraco.Core.Persistence.Querying if(fieldType == typeof(DateTime)) return "'" + EscapeParam(((DateTime)value).ToString(CultureInfo.InvariantCulture)) + "'"; + if (fieldType == typeof(bool)) + return ((bool)value) ? Convert.ToString(1, CultureInfo.InvariantCulture) : Convert.ToString(0, CultureInfo.InvariantCulture); + return ShouldQuoteValue(fieldType) ? "'" + EscapeParam(value) + "'" : value.ToString(); From 185c5d78f13776a337ce346398b761fc2a9b1184 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 25 Mar 2013 11:27:54 -0100 Subject: [PATCH 06/11] Query changed due to fix fe02cef72d87 that uses 1/0 instead of True/False --- .../Querying/ContentTypeRepositorySqlClausesTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs b/src/Umbraco.Tests/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs index cff1983b07..c05833ee3b 100644 --- a/src/Umbraco.Tests/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs +++ b/src/Umbraco.Tests/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs @@ -22,7 +22,7 @@ namespace Umbraco.Tests.Persistence.Querying .InnerJoin("[umbracoNode]") .On("[cmsContentType].[nodeId] = [umbracoNode].[id]") .Where("[umbracoNode].[nodeObjectType] = 'a2cb7800-f571-4787-9638-bc48539a0efb'") - .Where("[cmsDocumentType].[IsDefault] = 'True'"); + .Where("[cmsDocumentType].[IsDefault] = 1"); var sql = new Sql(); sql.Select("*") @@ -52,7 +52,7 @@ namespace Umbraco.Tests.Persistence.Querying .InnerJoin("[umbracoNode]") .On("[cmsContentType].[nodeId] = [umbracoNode].[id]") .Where("[umbracoNode].[nodeObjectType] = 'a2cb7800-f571-4787-9638-bc48539a0efb'") - .Where("[cmsDocumentType].[IsDefault] = 'True'") + .Where("[cmsDocumentType].[IsDefault] = 1") .Where("[umbracoNode].[id] = 1050"); var sql = new Sql(); From bc9fd04a36359c10323f19b29aceaf1ddcae242a Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 25 Mar 2013 12:49:43 -0100 Subject: [PATCH 07/11] Related to U4-1847 - Tabs weren't imported with the correct sort order and when a datatype can't be found we should silently fail and not create the property that wants that datatype (that's how v4 did it). --- src/Umbraco.Core/Services/PackagingService.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index dd50757586..3b1a98a510 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -291,7 +291,7 @@ namespace Umbraco.Core.Services contentType.AllowedAsRoot = infoElement.Element("AllowAtRoot").Value.ToLowerInvariant().Equals("true"); UpdateContentTypesAllowedTemplates(contentType, infoElement.Element("AllowedTemplates"), defaultTemplateElement); - UpdateContentTypesTabs(contentType, documentType.Element("Tab")); + UpdateContentTypesTabs(contentType, documentType.Element("Tabs")); UpdateContentTypesProperties(contentType, documentType.Element("GenericProperties")); return contentType; @@ -375,14 +375,12 @@ namespace Umbraco.Core.Services { dataTypeDefinition = dataTypeDefinitions.First(); } - else - { - throw new Exception( - String.Format( - "Packager: Error handling creation of PropertyType '{0}'. Could not find DataTypeDefintion with unique id '{1}' nor one referencing the DataType with control id '{2}'.", - property.Element("Name").Value, dataTypeDefinitionId, dataTypeId)); - } } + + // For backwards compatibility, if no datatype with that ID can be found, we're letting this fail silently. + // This means that the property will not be created. + if(dataTypeDefinition == null) + continue; var propertyType = new PropertyType(dataTypeDefinition) { From 9ef5890636aa217b9262e1a66fb9db6653cf63b3 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 25 Mar 2013 13:33:32 -0100 Subject: [PATCH 08/11] Changing sortorder on tabs now also re-orders the tabs in the contenttype editor --- .../umbraco/controls/ContentTypeControlNew.ascx.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs index 331e65f01d..ed98b073c7 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs @@ -187,6 +187,7 @@ namespace umbraco.controls // reload content type (due to caching) LoadContentType(); + BindTabs(); // Only if the doctype alias changed, cause a regeneration of the xml cache file since // the xml element names will need to be updated to reflect the new alias @@ -897,7 +898,7 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); dt.Columns.Add("id"); dt.Columns.Add("order"); - foreach (var grp in _contentType.PropertyTypeGroups) + foreach (var grp in _contentType.PropertyTypeGroups.OrderBy(p => p.SortOrder)) { if (grp.ContentTypeId == _contentType.Id && grp.ParentId == 0) { From 633d88c8c43a39fa046a8c5be3c3c3e92ecc7bc4 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Mon, 25 Mar 2013 20:54:21 +0600 Subject: [PATCH 09/11] Changed DefaultServerMessenger to have an overridable method to send the request, added some internals visible to. --- src/Umbraco.Core/Properties/AssemblyInfo.cs | 4 +- .../Sync/DefaultServerMessenger.cs | 65 ++++++++++++++----- src/Umbraco.Web/Properties/AssemblyInfo.cs | 2 +- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index 23f59eaa87..b4b6869944 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -39,4 +39,6 @@ using System.Security.Permissions; [assembly: InternalsVisibleTo("Umbraco.Web")] [assembly: InternalsVisibleTo("Umbraco.Web.UI")] [assembly: InternalsVisibleTo("UmbracoExamine")] -[assembly: InternalsVisibleTo("Umbraco.Courier.Persistence")] \ No newline at end of file +[assembly: InternalsVisibleTo("Umbraco.Courier.Persistence")] + +[assembly: InternalsVisibleTo("Concorde.Sync")] \ No newline at end of file diff --git a/src/Umbraco.Core/Sync/DefaultServerMessenger.cs b/src/Umbraco.Core/Sync/DefaultServerMessenger.cs index 0f9e81d7fd..5e59f05d75 100644 --- a/src/Umbraco.Core/Sync/DefaultServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DefaultServerMessenger.cs @@ -311,18 +311,11 @@ namespace Umbraco.Core.Sync { if (servers == null) throw new ArgumentNullException("servers"); if (refresher == null) throw new ArgumentNullException("refresher"); - Type arrayType = null; - if (ids != null) + + Type arrayType; + if (!ValidateIdArray(ids, out arrayType)) { - foreach (var id in ids) - { - if (!(id is int) && (!(id is Guid))) - throw new ArgumentException("The id must be either an int or a Guid"); - if (arrayType == null) - arrayType = id.GetType(); - if (arrayType != id.GetType()) - throw new ArgumentException("The array must contain the same type of " + arrayType); - } + throw new ArgumentException("The id must be either an int or a Guid"); } //Now, check if we are using Distrubuted calls. If there are no servers in the list then we @@ -336,6 +329,35 @@ namespace Umbraco.Core.Sync EnsureLazyUsernamePasswordDelegateResolved(); + PerformDistributedCall(servers, refresher, dispatchType, ids, arrayType, jsonPayload); + } + + private bool ValidateIdArray(IEnumerable ids, out Type arrayType) + { + arrayType = null; + if (ids != null) + { + foreach (var id in ids) + { + if (!(id is int) && (!(id is Guid))) + return false; // + if (arrayType == null) + arrayType = id.GetType(); + if (arrayType != id.GetType()) + throw new ArgumentException("The array must contain the same type of " + arrayType); + } + } + return true; + } + + protected virtual void PerformDistributedCall( + IEnumerable servers, + ICacheRefresher refresher, + MessageType dispatchType, + IEnumerable ids = null, + Type idArrayType = null, + string jsonPayload = null) + { //We are using distributed calls, so lets make them... try { @@ -351,6 +373,7 @@ namespace Umbraco.Core.Sync LogStartDispatch(); // Go through each configured node submitting a request asynchronously + //NOTE: 'asynchronously' in this case does not mean that it will continue while we give the page back to the user! foreach (var n in servers) { //set the server address @@ -363,14 +386,19 @@ namespace Umbraco.Core.Sync asyncResultsList.Add( cacheRefresher.BeginRefreshByJson( refresher.UniqueIdentifier, jsonPayload, _login, _password, null, null)); - break; + break; case MessageType.RefreshAll: asyncResultsList.Add( cacheRefresher.BeginRefreshAll( refresher.UniqueIdentifier, _login, _password, null, null)); break; case MessageType.RefreshById: - if (arrayType == typeof(int)) + if (idArrayType == null) + { + throw new InvalidOperationException("Cannot refresh by id if the idArrayType is null"); + } + + if (idArrayType == typeof(int)) { var serializer = new JavaScriptSerializer(); var jsonIds = serializer.Serialize(ids.Cast().ToArray()); @@ -384,9 +412,9 @@ namespace Umbraco.Core.Sync //so we'll just iterate asyncResultsList.AddRange( ids.Select(i => cacheRefresher.BeginRefreshByGuid( - refresher.UniqueIdentifier, (Guid) i, _login, _password, null, null))); + refresher.UniqueIdentifier, (Guid)i, _login, _password, null, null))); } - + break; case MessageType.RemoveById: //we don't currently support bulk removing so we'll iterate @@ -420,7 +448,12 @@ namespace Umbraco.Core.Sync cacheRefresher.EndRefreshAll(t); break; case MessageType.RefreshById: - if (arrayType == typeof(int)) + if (idArrayType == null) + { + throw new InvalidOperationException("Cannot refresh by id if the idArrayType is null"); + } + + if (idArrayType == typeof(int)) { cacheRefresher.EndRefreshById(t); } diff --git a/src/Umbraco.Web/Properties/AssemblyInfo.cs b/src/Umbraco.Web/Properties/AssemblyInfo.cs index c2cb1294a9..2b22aa97f8 100644 --- a/src/Umbraco.Web/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Web/Properties/AssemblyInfo.cs @@ -32,4 +32,4 @@ using System.Security; [assembly: InternalsVisibleTo("Umbraco.Web.UI")] [assembly: InternalsVisibleTo("Umbraco.Courier.Persistence")] [assembly: InternalsVisibleTo("umbraco.webservices")] - +[assembly: InternalsVisibleTo("Concorde.Sync")] From dfa1e73ddf19aec1016183f0423b6a5657ff2a65 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Mon, 25 Mar 2013 18:54:12 -0100 Subject: [PATCH 10/11] Continueing implementation of the EntityRepository and Service. --- .../Models/UmbracoObjectTypesExtensions.cs | 8 + .../Factories/UmbracoEntityFactory.cs | 14 +- .../Mappers/UmbracoEntityMapper.cs | 57 +++++ .../Repositories/EntityRepository.cs | 163 ++++++------ src/Umbraco.Core/Services/EntityService.cs | 236 +++++++++++++----- src/Umbraco.Core/Umbraco.Core.csproj | 1 + 6 files changed, 337 insertions(+), 142 deletions(-) create mode 100644 src/Umbraco.Core/Persistence/Mappers/UmbracoEntityMapper.cs diff --git a/src/Umbraco.Core/Models/UmbracoObjectTypesExtensions.cs b/src/Umbraco.Core/Models/UmbracoObjectTypesExtensions.cs index af4476f394..84067fabd7 100644 --- a/src/Umbraco.Core/Models/UmbracoObjectTypesExtensions.cs +++ b/src/Umbraco.Core/Models/UmbracoObjectTypesExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Umbraco.Core.CodeAnnotations; namespace Umbraco.Core.Models @@ -8,6 +9,8 @@ namespace Umbraco.Core.Models /// public static class UmbracoObjectTypesExtensions { + private static readonly Dictionary UmbracoObjectTypeCache = new Dictionary(); + /// /// Get an UmbracoObjectTypes value from it's name /// @@ -45,10 +48,15 @@ namespace Umbraco.Core.Models /// a GUID value of the UmbracoObjectTypes public static Guid GetGuid(this UmbracoObjectTypes umbracoObjectType) { + if (UmbracoObjectTypeCache.ContainsKey(umbracoObjectType)) + return UmbracoObjectTypeCache[umbracoObjectType]; + var attribute = umbracoObjectType.GetType().FirstAttribute(); if (attribute == null) return Guid.Empty; + UmbracoObjectTypeCache.Add(umbracoObjectType, attribute.ObjectId); + return attribute.ObjectId; } diff --git a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs index 41ce480a44..e07110809c 100644 --- a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs @@ -1,12 +1,12 @@ using System.Globalization; using Umbraco.Core.Models; -using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.Repositories; namespace Umbraco.Core.Persistence.Factories { - internal class UmbracoEntityFactory : IEntityFactory + internal class UmbracoEntityFactory : IEntityFactory { - public UmbracoEntity BuildEntity(NodeDto dto) + public UmbracoEntity BuildEntity(EntityRepository.UmbracoEntityDto dto) { var entity = new UmbracoEntity(dto.Trashed) { @@ -20,15 +20,15 @@ namespace Umbraco.Core.Persistence.Factories ParentId = dto.ParentId, Path = dto.Path, SortOrder = dto.SortOrder, - HasChildren = false, - IsPublished = false + HasChildren = dto.Children > 0, + IsPublished = dto.HasPublishedVersion.HasValue && dto.HasPublishedVersion.Value > 0 }; return entity; } - public NodeDto BuildDto(UmbracoEntity entity) + public EntityRepository.UmbracoEntityDto BuildDto(UmbracoEntity entity) { - var node = new NodeDto + var node = new EntityRepository.UmbracoEntityDto { CreateDate = entity.CreateDate, Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)), diff --git a/src/Umbraco.Core/Persistence/Mappers/UmbracoEntityMapper.cs b/src/Umbraco.Core/Persistence/Mappers/UmbracoEntityMapper.cs new file mode 100644 index 0000000000..d1abd4043f --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/UmbracoEntityMapper.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Concurrent; +using System.Linq.Expressions; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + [MapperFor(typeof (IUmbracoEntity))] + public sealed class UmbracoEntityMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCache = + new ConcurrentDictionary(); + + //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it + // otherwise that would fail because there is no public constructor. + public UmbracoEntityMapper() + { + BuildMap(); + } + + #region Overrides of BaseMapper + + internal override void BuildMap() + { + CacheMap(src => src.Id, dto => dto.NodeId); + CacheMap(src => src.CreateDate, dto => dto.CreateDate); + CacheMap(src => src.Level, dto => dto.Level); + CacheMap(src => src.ParentId, dto => dto.ParentId); + CacheMap(src => src.Path, dto => dto.Path); + CacheMap(src => src.SortOrder, dto => dto.SortOrder); + CacheMap(src => src.Name, dto => dto.Text); + CacheMap(src => src.Trashed, dto => dto.Trashed); + CacheMap(src => src.Key, dto => dto.UniqueId); + CacheMap(src => src.CreatorId, dto => dto.UserId); + } + + internal override string Map(string propertyName) + { + if (!PropertyInfoCache.ContainsKey(propertyName)) + return string.Empty; + + var dtoTypeProperty = PropertyInfoCache[propertyName]; + + return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo); + } + + internal override void CacheMap(Expression> sourceMember, + Expression> destinationMember) + { + var property = base.ResolveMapping(sourceMember, destinationMember); + PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs index d63d48a43d..6d80670c89 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs @@ -19,18 +19,10 @@ namespace Umbraco.Core.Persistence.Repositories internal class EntityRepository : DisposableObject, IEntityRepository { private readonly IDatabaseUnitOfWork _work; - private readonly IDictionary> _propertyHandler; public EntityRepository(IDatabaseUnitOfWork work) { _work = work; - _propertyHandler = new Dictionary> - { - { - new Guid(Constants.ObjectTypes.Document), - UpdateIsPublished - } - }; } /// @@ -53,35 +45,28 @@ namespace Umbraco.Core.Persistence.Repositories public virtual IUmbracoEntity Get(int id) { - var sql = GetBaseWhere(id); - var nodeDto = _work.Database.FirstOrDefault(sql); + var sql = GetBaseWhere(GetBase, false, id); + var nodeDto = _work.Database.FirstOrDefault(sql); if (nodeDto == null) return null; var factory = new UmbracoEntityFactory(); var entity = factory.BuildEntity(nodeDto); - - //TODO Update HasChildren and IsPublished - if (_propertyHandler.ContainsKey(entity.NodeObjectTypeId)) - { - return _propertyHandler[entity.NodeObjectTypeId](entity); - } return entity; } public virtual IUmbracoEntity Get(int id, Guid objectTypeId) { - var sql = GetBaseWhere(objectTypeId, id); - var nodeDto = _work.Database.FirstOrDefault(sql); + bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document); + var sql = GetBaseWhere(GetBase, isContent, objectTypeId, id).Append(GetGroupBy()); + var nodeDto = _work.Database.FirstOrDefault(sql); if (nodeDto == null) return null; var factory = new UmbracoEntityFactory(); var entity = factory.BuildEntity(nodeDto); - //TODO Update HasChildren and IsPublished - return entity; } @@ -96,15 +81,15 @@ namespace Umbraco.Core.Persistence.Repositories } else { - var sql = GetBaseWhere(objectTypeId); - var dtos = _work.Database.Fetch(sql); + bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document); + var sql = GetBaseWhere(GetBase, isContent, objectTypeId).Append(GetGroupBy()); + var dtos = _work.Database.Fetch(sql); var factory = new UmbracoEntityFactory(); foreach (var dto in dtos) { var entity = factory.BuildEntity(dto); - //TODO Update HasChildren and IsPublished properties yield return entity; } } @@ -112,80 +97,102 @@ namespace Umbraco.Core.Persistence.Repositories public virtual IEnumerable GetByQuery(IQuery query) { - var sqlClause = GetBaseQuery(); + var sqlClause = GetBase(false); var translator = new SqlTranslator(sqlClause, query); - var sql = translator.Translate(); + var sql = translator.Translate().Append(GetGroupBy()); - var dtos = _work.Database.Fetch(sql); + var dtos = _work.Database.Fetch(sql); var factory = new UmbracoEntityFactory(); var list = dtos.Select(factory.BuildEntity).Cast().ToList(); - //TODO Update HasChildren and IsPublished properties - return list; } public virtual IEnumerable GetByQuery(IQuery query, Guid objectTypeId) { - var sqlClause = GetBaseWhere(objectTypeId); + bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document); + var sqlClause = GetBaseWhere(GetBase, isContent, objectTypeId); var translator = new SqlTranslator(sqlClause, query); - var sql = translator.Translate(); + var sql = translator.Translate().Append(GetGroupBy()); - var dtos = _work.Database.Fetch(sql); + var dtos = _work.Database.Fetch(sql); var factory = new UmbracoEntityFactory(); var list = dtos.Select(factory.BuildEntity).Cast().ToList(); - //TODO Update HasChildren and IsPublished properties - return list; } #endregion - private void UpdateHasChildren(IUmbracoEntity entity) - {} - - private IUmbracoEntity UpdateIsPublished(IUmbracoEntity entity) - { - var umbracoEntity = entity as UmbracoEntity; - if (umbracoEntity != null) - { - umbracoEntity.IsPublished = true; - return umbracoEntity; - } - - return entity; - } - #region Sql Statements - protected virtual Sql GetBaseQuery() + protected virtual Sql GetBase(bool isContent) + { + var columns = new List + { + "main.id", + "main.trashed", + "main.parentID", + "main.nodeUser", + "main.level", + "main.path", + "main.sortOrder", + "main.uniqueID", + "main.text", + "main.nodeObjectType", + "main.createDate", + "COUNT(parent.parentID) as children", + isContent + ? "SUM(CONVERT(int, document.published)) as published" + : "SUM(0) as published" + }; + + var sql = new Sql() + .Select(columns.ToArray()) + .From("FROM umbracoNode main") + .LeftJoin("umbracoNode parent").On("parent.parentID = main.id"); + + + //NOTE Should this account for newest = 1 ? Scenarios: unsaved, saved not published, published + + if (isContent) + sql.LeftJoin("LEFT JOIN cmsDocument document").On("document.nodeId = main.id"); + + return sql; + } + + protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, Guid id) + { + var sql = baseQuery(isContent) + .Where("main.nodeObjectType = @NodeObjectType", new {NodeObjectType = id}); + return sql; + } + + protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, int id) + { + var sql = baseQuery(isContent) + .Where("main.id = @Id", new {Id = id}) + .Append(GetGroupBy()); + return sql; + } + + protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, Guid objectId, int id) + { + var sql = baseQuery(isContent) + .Where("main.id = @Id AND main.nodeObjectType = @NodeObjectType", + new {Id = id, NodeObjectType = objectId}); + return sql; + } + + protected virtual Sql GetGroupBy() { var sql = new Sql() - .From(); - return sql; - } - - protected virtual Sql GetBaseWhere(Guid id) - { - var sql = GetBaseQuery() - .Where(x => x.NodeObjectType == id); - return sql; - } - - protected virtual Sql GetBaseWhere(int id) - { - var sql = GetBaseQuery() - .Where(x => x.NodeId == id); - return sql; - } - - protected virtual Sql GetBaseWhere(Guid objectId, int id) - { - var sql = GetBaseWhere(objectId) - .Where(x => x.NodeId == id); + .GroupBy("main.id", "main.trashed", "main.parentID", "main.nodeUser", "main.level", + "main.path", "main.sortOrder", "main.uniqueID", "main.text", + "main.nodeObjectType", "main.createDate") + .OrderBy("main.sortOrder"); return sql; } @@ -201,5 +208,19 @@ namespace Umbraco.Core.Persistence.Repositories { UnitOfWork.DisposeIfDisposable(); } + + #region umbracoNode POCO - Extends NodeDto + [TableName("umbracoNode")] + [PrimaryKey("id")] + [ExplicitColumns] + internal class UmbracoEntityDto : NodeDto + { + [Column("children")] + public int Children { get; set; } + + [Column("published")] + public int? HasPublishedVersion { get; set; } + } + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/EntityService.cs b/src/Umbraco.Core/Services/EntityService.cs index a56d3a2f55..bfc0b2dd9f 100644 --- a/src/Umbraco.Core/Services/EntityService.cs +++ b/src/Umbraco.Core/Services/EntityService.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; -using System.Linq; +using Umbraco.Core.CodeAnnotations; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; @@ -13,18 +14,15 @@ namespace Umbraco.Core.Services { private readonly IDatabaseUnitOfWorkProvider _uowProvider; private readonly RepositoryFactory _repositoryFactory; - - public EntityService() - : this(new RepositoryFactory()) - { } - - public EntityService(RepositoryFactory repositoryFactory) - : this(new PetaPocoUnitOfWorkProvider(), repositoryFactory) - { } - - public EntityService(IDatabaseUnitOfWorkProvider provider) - : this(provider, new RepositoryFactory()) - { } + private readonly Dictionary _supportedObjectTypes = new Dictionary + { + {typeof(IDataTypeDefinition).FullName, UmbracoObjectTypes.DataType}, + {typeof(IContent).FullName, UmbracoObjectTypes.Document}, + {typeof(IContentType).FullName, UmbracoObjectTypes.DocumentType}, + {typeof(IMedia).FullName, UmbracoObjectTypes.Media}, + {typeof(IMediaType).FullName, UmbracoObjectTypes.MediaType}, + {typeof(ITemplate).FullName, UmbracoObjectTypes.Template} + }; public EntityService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory) { @@ -51,6 +49,34 @@ namespace Umbraco.Core.Services } } + var objectType = GetObjectType(id); + + //TODO Implementing loading from the various services + throw new NotImplementedException(); + } + + /// + /// Gets an UmbracoEntity by its Id and UmbracoObjectType, and optionally loads the complete object graph. + /// + /// + /// By default this will load the base type with a minimum set of properties. + /// + /// Id of the object to retrieve + /// UmbracoObjectType of the entity to retrieve + /// Optional bool to load the complete object graph when set to False. + /// An + public virtual IUmbracoEntity Get(int id, UmbracoObjectTypes umbracoObjectType, bool loadBaseType = true) + { + if (loadBaseType) + { + var objectTypeId = umbracoObjectType.GetGuid(); + using (var repository = _repositoryFactory.CreateEntityRepository(_uowProvider.GetUnitOfWork())) + { + return repository.Get(id, objectTypeId); + } + } + + //TODO Implementing loading from the various services throw new NotImplementedException(); } @@ -75,15 +101,23 @@ namespace Umbraco.Core.Services } } + Mandate.That(_supportedObjectTypes.ContainsKey(typeof (T).FullName), () => + { + throw + new NotSupportedException + ("The passed in type is not supported"); + }); + var objectType = _supportedObjectTypes[typeof(T).FullName]; + //TODO Implementing loading from the various services throw new NotImplementedException(); } /// - /// + /// Gets the parent of entity by its id /// - /// - /// + /// Id of the entity to retrieve the Parent for + /// An public virtual IUmbracoEntity GetParent(int id) { using (var repository = _repositoryFactory.CreateEntityRepository(_uowProvider.GetUnitOfWork())) @@ -97,10 +131,29 @@ namespace Umbraco.Core.Services } /// - /// + /// Gets the parent of entity by its id and UmbracoObjectType /// - /// - /// + /// Id of the entity to retrieve the Parent for + /// UmbracoObjectType of the parent to retrieve + /// An + public virtual IUmbracoEntity GetParent(int id, UmbracoObjectTypes umbracoObjectType) + { + using (var repository = _repositoryFactory.CreateEntityRepository(_uowProvider.GetUnitOfWork())) + { + var entity = repository.Get(id); + if (entity.ParentId == -1 || entity.ParentId == -20 || entity.ParentId == -21) + return null; + + var objectTypeId = umbracoObjectType.GetGuid(); + return repository.Get(entity.ParentId, objectTypeId); + } + } + + /// + /// Gets a collection of children by the parents Id + /// + /// Id of the parent to retrieve children for + /// An enumerable list of objects public virtual IEnumerable GetChildren(int id) { using (var repository = _repositoryFactory.CreateEntityRepository(_uowProvider.GetUnitOfWork())) @@ -113,10 +166,28 @@ namespace Umbraco.Core.Services } /// - /// + /// Gets a collection of children by the parents Id and UmbracoObjectType /// - /// - /// + /// Id of the parent to retrieve children for + /// UmbracoObjectType of the children to retrieve + /// An enumerable list of objects + public virtual IEnumerable GetChildren(int id, UmbracoObjectTypes umbracoObjectType) + { + var objectTypeId = umbracoObjectType.GetGuid(); + using (var repository = _repositoryFactory.CreateEntityRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.ParentId == id); + var contents = repository.GetByQuery(query, objectTypeId); + + return contents; + } + } + + /// + /// Gets a collection of descendents by the parents Id + /// + /// Id of entity to retrieve descendents for + /// An enumerable list of objects public virtual IEnumerable GetDescendents(int id) { using (var repository = _repositoryFactory.CreateEntityRepository(_uowProvider.GetUnitOfWork())) @@ -130,13 +201,32 @@ namespace Umbraco.Core.Services } /// - /// + /// Gets a collection of descendents by the parents Id /// - /// - /// - public virtual IEnumerable GetRootEntities(UmbracoObjectTypes objectType) + /// Id of entity to retrieve descendents for + /// UmbracoObjectType of the descendents to retrieve + /// An enumerable list of objects + public virtual IEnumerable GetDescendents(int id, UmbracoObjectTypes umbracoObjectType) { - var objectTypeId = objectType.GetGuid(); + var objectTypeId = umbracoObjectType.GetGuid(); + using (var repository = _repositoryFactory.CreateEntityRepository(_uowProvider.GetUnitOfWork())) + { + var entity = repository.Get(id); + var query = Query.Builder.Where(x => x.Path.StartsWith(entity.Path) && x.Id != id); + var entities = repository.GetByQuery(query, objectTypeId); + + return entities; + } + } + + /// + /// Gets a collection of the entities at the root, which corresponds to the entities with a Parent Id of -1. + /// + /// UmbracoObjectType of the root entities to retrieve + /// An enumerable list of objects + public virtual IEnumerable GetRootEntities(UmbracoObjectTypes umbracoObjectType) + { + var objectTypeId = umbracoObjectType.GetGuid(); using (var repository = _repositoryFactory.CreateEntityRepository(_uowProvider.GetUnitOfWork())) { var query = Query.Builder.Where(x => x.ParentId == -1); @@ -147,25 +237,30 @@ namespace Umbraco.Core.Services } /// - /// + /// Gets a collection of all of a given type. /// - /// - /// + /// Type of the entities to retrieve + /// An enumerable list of objects public virtual IEnumerable GetAll() where T : IUmbracoEntity { - //TODO Implement this so the type passed in is verified against types on UmbracoObjectTypes - //and then used to get all through the method below. - throw new NotImplementedException(); + Mandate.That(_supportedObjectTypes.ContainsKey(typeof(T).FullName), () => + { + throw new NotSupportedException + ("The passed in type is not supported"); + }); + var objectType = _supportedObjectTypes[typeof (T).FullName]; + + return GetAll(objectType); } /// - /// + /// Gets a collection of all of a given type. /// - /// - /// - public virtual IEnumerable GetAll(UmbracoObjectTypes objectType) + /// UmbracoObjectType of the entities to return + /// An enumerable list of objects + public virtual IEnumerable GetAll(UmbracoObjectTypes umbracoObjectType) { - var objectTypeId = objectType.GetGuid(); + var objectTypeId = umbracoObjectType.GetGuid(); using (var repository = _repositoryFactory.CreateEntityRepository(_uowProvider.GetUnitOfWork())) { return repository.GetAll(objectTypeId); @@ -173,10 +268,10 @@ namespace Umbraco.Core.Services } /// - /// + /// Gets a collection of /// - /// - /// + /// Guid id of the UmbracoObjectType + /// An enumerable list of objects public virtual IEnumerable GetAll(Guid objectTypeId) { using (var repository = _repositoryFactory.CreateEntityRepository(_uowProvider.GetUnitOfWork())) @@ -186,48 +281,61 @@ namespace Umbraco.Core.Services } /// - /// + /// Gets the UmbracoObjectType from the integer id of an IUmbracoEntity. /// - /// - /// + /// Id of the entity + /// public virtual UmbracoObjectTypes GetObjectType(int id) { - //TODO Implement so the entity is fetched from the db and then the Guid is used to resolve the UmbracoObjectType - throw new NotImplementedException(); + using (var uow = _uowProvider.GetUnitOfWork()) + { + var sql = new Sql().Select("nodeObjectType").From().Where(x => x.NodeId == id); + var nodeObjectTypeId = uow.Database.ExecuteScalar(sql); + var objectTypeId = new Guid(nodeObjectTypeId); + return UmbracoObjectTypesExtensions.GetUmbracoObjectType(objectTypeId); + } } /// - /// + /// Gets the UmbracoObjectType from an IUmbracoEntity. /// - /// - /// + /// + /// public virtual UmbracoObjectTypes GetObjectType(IUmbracoEntity entity) { - //TODO Implement this so the entity is cast to UmbracoEntity - if valid get the guid id and then resolve the UmbracoObjectType - //otherwise fetch the IUmbracoEntity from the db and do it similar to above. - throw new NotImplementedException(); + var entityImpl = entity as UmbracoEntity; + if (entityImpl == null) + return GetObjectType(entity.Id); + + return UmbracoObjectTypesExtensions.GetUmbracoObjectType(entityImpl.NodeObjectTypeId); } /// - /// + /// Gets the Type of an entity by its Id /// - /// - /// - public virtual Type GetModelType(int id) + /// Id of the entity + /// Type of the entity + public virtual Type GetEntityType(int id) { - //TODO Implement so the IUmbracoEntity is fetched from the db and then used to resolve the real type, ie. IContent, IMedia etc. - throw new NotImplementedException(); + var objectType = GetObjectType(id); + return GetEntityType(objectType); } /// - /// + /// Gets the Type of an entity by its /// - /// - /// - public virtual Type GetModelType(UmbracoObjectTypes objectType) + /// + /// Type of the entity + public virtual Type GetEntityType(UmbracoObjectTypes umbracoObjectType) { - //TODO Implement so the real type is returned fro the UmbracoObjectType's attribute. - throw new NotImplementedException(); + var attribute = umbracoObjectType.GetType().FirstAttribute(); + if (attribute == null) + throw new NullReferenceException("The passed in UmbracoObjectType does not contain an UmbracoObjectTypeAttribute, which is used to retrieve the Type."); + + if(attribute.ModelType == null) + throw new NullReferenceException("The passed in UmbracoObjectType does not contain a Type definition"); + + return attribute.ModelType; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 477e3696e8..95bedb1f6d 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -309,6 +309,7 @@ + From b058b1e345dc06be0da8dcbe7a29bf3e66344d65 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Tue, 26 Mar 2013 04:59:56 -0100 Subject: [PATCH 11/11] Removing LEFT JOIN from query as its already part of the builder --- src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs index 6d80670c89..a7b221916c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs @@ -158,7 +158,7 @@ namespace Umbraco.Core.Persistence.Repositories //NOTE Should this account for newest = 1 ? Scenarios: unsaved, saved not published, published if (isContent) - sql.LeftJoin("LEFT JOIN cmsDocument document").On("document.nodeId = main.id"); + sql.LeftJoin("cmsDocument document").On("document.nodeId = main.id"); return sql; }