diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 6067cade4a..4fe812dbeb 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -30,7 +30,7 @@ - + diff --git a/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs b/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs index 1e59c8e920..52499bee7d 100644 --- a/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs @@ -48,7 +48,6 @@ namespace Umbraco.Core.Persistence.Factories template.IsMasterTemplate = true; } - //TODO: Change this to ParentId: http://issues.umbraco.org/issue/U4-5846 if(dto.NodeDto.ParentId > 0) template.MasterTemplateId = new Lazy(() => dto.NodeDto.ParentId); diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs index 4366aaa4a6..14957ac49f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Core.Models; namespace Umbraco.Core.Persistence.Repositories @@ -10,12 +11,17 @@ namespace Umbraco.Core.Persistence.Repositories IEnumerable GetAll(params string[] aliases); IEnumerable GetChildren(int masterTemplateId); + IEnumerable GetChildren(string alias); + + IEnumerable GetDescendants(int masterTemplateId); + IEnumerable GetDescendants(string alias); /// /// Returns a template as a template node which can be traversed (parent, children) /// /// /// + [Obsolete("Use GetDescendants instead")] TemplateNode GetTemplateNode(string alias); /// @@ -24,6 +30,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// /// + [Obsolete("Use GetDescendants instead")] TemplateNode FindTemplateInTree(TemplateNode anyNode, string alias); /// diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index 235c18a7a8..53e9954fd0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -67,7 +67,15 @@ namespace Umbraco.Core.Persistence.Repositories protected override ITemplate PerformGet(int id) { - return GetAll(new[] { id }).FirstOrDefault(); + var sql = GetBaseQuery(false).Where(x => x.NodeId == id); + var result = Database.Fetch(sql).FirstOrDefault(); + if (result == null) return null; + + //look up the simple template definitions that have a master template assigned, this is used + // later to populate the template item's properties + var childIds = GetAxisDefinitions(result).ToArray(); + + return MapFromDto(result, childIds); } protected override IEnumerable PerformGetAll(params int[] ids) @@ -89,20 +97,15 @@ namespace Umbraco.Core.Persistence.Repositories //look up the simple template definitions that have a master template assigned, this is used // later to populate the template item's properties - var childIdsSql = new Sql() - .Select("nodeId,alias,parentID") - .From() - .InnerJoin() - .On(dto => dto.NodeId, dto => dto.NodeId) - .Where(t => t.ParentId > 0); - var childIds = Database.Fetch(childIdsSql) - .Select(x => new UmbracoEntity + var childIds = (ids.Any() + ? GetAxisDefinitions(dtos.ToArray()) + : dtos.Select(x => new UmbracoEntity { - Id = x.nodeId, - ParentId = x.parentID, - Name = x.alias - }); - + Id = x.NodeId, + ParentId = x.NodeDto.ParentId, + Name = x.Alias + })).ToArray(); + return dtos.Select(d => MapFromDto(d, childIds)); } @@ -118,19 +121,7 @@ namespace Umbraco.Core.Persistence.Repositories //look up the simple template definitions that have a master template assigned, this is used // later to populate the template item's properties - var childIdsSql = new Sql() - .Select("nodeId,alias,parentID") - .From() - .InnerJoin() - .On(dto => dto.NodeId, dto => dto.NodeId) - .Where(t => t.ParentId > 0); - var childIds = Database.Fetch(childIdsSql) - .Select(x => new UmbracoEntity - { - Id = x.nodeId, - ParentId = x.parentID, - Name = x.alias - }); + var childIds = GetAxisDefinitions(dtos.ToArray()).ToArray(); return dtos.Select(d => MapFromDto(d, childIds)); } @@ -143,9 +134,9 @@ namespace Umbraco.Core.Persistence.Repositories { var sql = new Sql(); sql.Select(isCount ? "COUNT(*)" : "*") - .From() - .InnerJoin() - .On(left => left.NodeId, right => right.NodeId) + .From(SqlSyntax) + .InnerJoin(SqlSyntax) + .On(SqlSyntax, left => left.NodeId, right => right.NodeId) .Where(x => x.NodeObjectType == NodeObjectTypeId); return sql; } @@ -348,22 +339,52 @@ namespace Umbraco.Core.Persistence.Repositories #endregion - private ITemplate MapFromDto(TemplateDto dto, IEnumerable childDefinitions) + private IEnumerable GetAxisDefinitions(params TemplateDto[] templates) + { + //look up the simple template definitions that have a master template assigned, this is used + // later to populate the template item's properties + var childIdsSql = new Sql() + .Select("nodeId,alias,parentID") + .From(SqlSyntax) + .InnerJoin(SqlSyntax) + .On(SqlSyntax, dto => dto.NodeId, dto => dto.NodeId) + //lookup axis's + .Where("umbracoNode." + SqlSyntax.GetQuotedColumnName("id") + " IN (@parentIds) OR umbracoNode.parentID IN (@childIds)", + new {parentIds = templates.Select(x => x.NodeDto.ParentId), childIds = templates.Select(x => x.NodeId)}); + + var childIds = Database.Fetch(childIdsSql) + .Select(x => new UmbracoEntity + { + Id = x.nodeId, + ParentId = x.parentID, + Name = x.alias + }); + return childIds; + } + + /// + /// Maps from a dto to an ITemplate + /// + /// + /// + /// This is a collection of template definitions ... either all templates, or the collection of child templates and it's parent template + /// + /// + private ITemplate MapFromDto(TemplateDto dto, IUmbracoEntity[] axisDefinitions) { string csViewName = string.Concat(dto.Alias, ".cshtml"); string vbViewName = string.Concat(dto.Alias, ".vbhtml"); string masterpageName = string.Concat(dto.Alias, ".master"); var factory = new TemplateFactory(); - var template = factory.BuildEntity(dto, childDefinitions); + var template = factory.BuildEntity(dto, axisDefinitions); if (dto.NodeDto.ParentId > 0) { - //TODO: Fix this n+1 query! - var masterTemplate = Get(dto.NodeDto.ParentId); + var masterTemplate = axisDefinitions.FirstOrDefault(x => x.Id == dto.NodeDto.ParentId); if (masterTemplate != null) { - template.MasterTemplateAlias = masterTemplate.Alias; + template.MasterTemplateAlias = masterTemplate.Name; template.MasterTemplateId = new Lazy(() => dto.NodeDto.ParentId); } } @@ -460,49 +481,105 @@ namespace Umbraco.Core.Persistence.Repositories if (dto == null) return null; - return Get(dto.NodeId); + return MapFromDto(dto, GetAxisDefinitions(dto).ToArray()); } public IEnumerable GetAll(params string[] aliases) { + var sql = GetBaseQuery(false); + if (aliases.Any()) { - foreach (var id in aliases) - { - yield return Get(id); - } - } - else - { - var nodeDtos = Database.Fetch("WHERE nodeObjectType = @NodeObjectType", new { NodeObjectType = NodeObjectTypeId }); - foreach (var nodeDto in nodeDtos) - { - yield return Get(nodeDto.NodeId); - } + sql.Where("cmsTemplate.alias IN (@aliases)", new {aliases = aliases}); } + var dtos = Database.Fetch(sql).ToArray(); + var axisDefos = GetAxisDefinitions(dtos).ToArray(); + return dtos.Select(x => MapFromDto(x, axisDefos)); } public IEnumerable GetChildren(int masterTemplateId) { - //TODO: Fix this N+1! - - List found; - if (masterTemplateId == -1) + var sql = GetBaseQuery(false); + if (masterTemplateId <= 0) { - var sql = GetBaseQuery(false).Where(x => x.ParentId <= 0); - found = Database.Fetch(sql); + sql.Where(x => x.ParentId <= 0); } else { - var sql = GetBaseQuery(false).Where(x => x.ParentId == masterTemplateId); - found = Database.Fetch(sql); + sql.Where(x => x.ParentId == masterTemplateId); } - foreach (var templateDto in found) + var dtos = Database.Fetch(sql).ToArray(); + var axisDefos = GetAxisDefinitions(dtos).ToArray(); + return dtos.Select(x => MapFromDto(x, axisDefos)); + } + + public IEnumerable GetChildren(string alias) + { + var sql = GetBaseQuery(false); + if (alias.IsNullOrWhiteSpace()) { - yield return Get(templateDto.NodeId); + sql.Where(x => x.ParentId <= 0); } + else + { + //unfortunately SQLCE doesn't support scalar subqueries in the where clause, otherwise we could have done this + // in a single query, now we have to lookup the path to acheive the same thing + var parent = Database.ExecuteScalar(new Sql().Select("nodeId").From(SqlSyntax).Where(dto => dto.Alias == alias)); + if (parent.HasValue == false) return Enumerable.Empty(); + + sql.Where(x => x.ParentId == parent.Value); + } + + var dtos = Database.Fetch(sql).ToArray(); + var axisDefos = GetAxisDefinitions(dtos).ToArray(); + return dtos.Select(x => MapFromDto(x, axisDefos)); + } + + public IEnumerable GetDescendants(int masterTemplateId) + { + var sql = GetBaseQuery(false); + if (masterTemplateId > 0) + { + //unfortunately SQLCE doesn't support scalar subqueries in the where clause, otherwise we could have done this + // in a single query, now we have to lookup the path to acheive the same thing + var path = Database.ExecuteScalar( + new Sql().Select(SqlSyntax.GetQuotedColumnName("path")) + .From(SqlSyntax) + .InnerJoin(SqlSyntax) + .On(SqlSyntax, dto => dto.NodeId, dto => dto.NodeId) + .Where(dto => dto.NodeId == masterTemplateId)); + + if (path.IsNullOrWhiteSpace()) return Enumerable.Empty(); + + sql.Where(@"(umbracoNode." + SqlSyntax.GetQuotedColumnName("path") + @" LIKE @query)", new { query = path + ",%" }); + } + + var dtos = Database.Fetch(sql).ToArray(); + var axisDefos = GetAxisDefinitions(dtos).ToArray(); + return dtos.Select(x => MapFromDto(x, axisDefos)); + + } + + public IEnumerable GetDescendants(string alias) + { + var sql = GetBaseQuery(false); + if (alias.IsNullOrWhiteSpace() == false) + { + //unfortunately SQLCE doesn't support scalar subqueries in the where clause, otherwise we could have done this + // in a single query, now we have to lookup the path to acheive the same thing + var path = Database.ExecuteScalar( + "SELECT umbracoNode.path FROM cmsTemplate INNER JOIN umbracoNode ON cmsTemplate.nodeId = umbracoNode.id WHERE cmsTemplate.alias = @alias", new { alias = alias }); + + if (path.IsNullOrWhiteSpace()) return Enumerable.Empty(); + + sql.Where(@"(umbracoNode." + SqlSyntax.GetQuotedColumnName("path") + @" LIKE @query)", new {query = path + ",%" }); + } + + var dtos = Database.Fetch(sql).ToArray(); + var axisDefos = GetAxisDefinitions(dtos).ToArray(); + return dtos.Select(x => MapFromDto(x, axisDefos)); } /// @@ -510,13 +587,9 @@ namespace Umbraco.Core.Persistence.Repositories /// /// /// + [Obsolete("Use GetDescendants instead")] public TemplateNode GetTemplateNode(string alias) - { - //in order to do this we need to get all of the templates and then organize, - // TODO: unfortunately our db structure does not use the path correctly for templates so we cannot just look - // up a template tree easily. - //TODO: We do use the 'path' now, so this might be able to be fixed up - + { //first get all template objects var allTemplates = GetAll().ToArray(); @@ -525,23 +598,17 @@ namespace Umbraco.Core.Persistence.Repositories { return null; } - - //then we need to get all template Dto's because those contain the master property - var sql = GetBaseQuery(false); - var allDtos = Database.Fetch(sql).ToArray(); - var selfDto = allDtos.Single(x => x.NodeId == selfTemplate.Id); - - //need to get the top-most node of the current tree - var top = selfDto; - while (top.NodeDto.ParentId > 0) + + var top = selfTemplate; + while (top.MasterTemplateAlias.IsNullOrWhiteSpace() == false) { - top = allDtos.Single(x => x.NodeId == top.NodeDto.ParentId); + top = allTemplates.Single(x => x.Alias == top.MasterTemplateAlias); } - var topNode = new TemplateNode(allTemplates.Single(x => x.Id == top.NodeId)); - var childIds = allDtos.Where(x => x.NodeDto.ParentId == top.NodeId).Select(x => x.NodeId); + var topNode = new TemplateNode(allTemplates.Single(x => x.Id == top.Id)); + var childTemplates = allTemplates.Where(x => x.MasterTemplateAlias == top.Alias); //This now creates the hierarchy recursively - topNode.Children = CreateChildren(topNode, childIds, allTemplates, allDtos); + topNode.Children = CreateChildren(topNode, childTemplates, allTemplates); //now we'll return the TemplateNode requested return FindTemplateInTree(topNode, alias); @@ -568,6 +635,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// /// + [Obsolete("Use GetDescendants instead")] public TemplateNode FindTemplateInTree(TemplateNode anyNode, string alias) { //first get the root @@ -658,12 +726,12 @@ namespace Umbraco.Core.Persistence.Repositories return validFile && validExtension; } - private static IEnumerable CreateChildren(TemplateNode parent, IEnumerable childIds, ITemplate[] allTemplates, TemplateDto[] allDtos) + private static IEnumerable CreateChildren(TemplateNode parent, IEnumerable childTemplates, ITemplate[] allTemplates) { var children = new List(); - foreach (var i in childIds) + foreach (var childTemplate in childTemplates) { - var template = allTemplates.Single(x => x.Id == i); + var template = allTemplates.Single(x => x.Id == childTemplate.Id); var child = new TemplateNode(template) { Parent = parent @@ -673,10 +741,11 @@ namespace Umbraco.Core.Persistence.Repositories children.Add(child); //get this node's children - var kids = allDtos.Where(x => x.NodeDto.ParentId == i).Select(x => x.NodeId).ToArray(); + var local = childTemplate; + var kids = allTemplates.Where(x => x.MasterTemplateAlias == local.Alias); //recurse - child.Children = CreateChildren(child, kids, allTemplates, allDtos); + child.Children = CreateChildren(child, kids, allTemplates); } return children; } diff --git a/src/Umbraco.Core/Services/FileService.cs b/src/Umbraco.Core/Services/FileService.cs index 29ccfbf6bf..4dcbd5bdc8 100644 --- a/src/Umbraco.Core/Services/FileService.cs +++ b/src/Umbraco.Core/Services/FileService.cs @@ -319,6 +319,53 @@ namespace Umbraco.Core.Services } } + public IEnumerable GetTemplateDescendants(string alias) + { + using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork())) + { + return repository.GetDescendants(alias); + } + } + + /// + /// Gets the template descendants + /// + /// + /// + public IEnumerable GetTemplateDescendants(int masterTemplateId) + { + using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork())) + { + return repository.GetDescendants(masterTemplateId); + } + } + + /// + /// Gets the template children + /// + /// + /// + public IEnumerable GetTemplateChildren(string alias) + { + using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork())) + { + return repository.GetChildren(alias); + } + } + + /// + /// Gets the template children + /// + /// + /// + public IEnumerable GetTemplateChildren(int masterTemplateId) + { + using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork())) + { + return repository.GetChildren(masterTemplateId); + } + } + /// /// Returns a template as a template node which can be traversed (parent, children) /// diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index 7803abe98e..e1c0f1428c 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -130,10 +130,38 @@ namespace Umbraco.Core.Services ITemplate GetTemplate(int id); /// - /// Returns a template as a template node which can be traversed (parent, children) + /// Gets the template descendants /// /// /// + IEnumerable GetTemplateDescendants(string alias); + + /// + /// Gets the template descendants + /// + /// + /// + IEnumerable GetTemplateDescendants(int masterTemplateId); + + /// + /// Gets the template children + /// + /// + /// + IEnumerable GetTemplateChildren(string alias); + + /// + /// Gets the template children + /// + /// + /// + IEnumerable GetTemplateChildren(int masterTemplateId); + + /// + /// Returns a template as a template node which can be traversed (parent, children) + /// + /// + /// TemplateNode GetTemplateNode(string alias); /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index b13529f8ea..56935ed7f7 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -37,13 +37,13 @@ false - - False - ..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.dll + + ..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.dll + True - - False - ..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll + + ..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll + True False @@ -1387,7 +1387,6 @@ - - + - \ No newline at end of file diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs index 37de741007..0f6fc03b80 100644 --- a/src/Umbraco.Web/UmbracoApplication.cs +++ b/src/Umbraco.Web/UmbracoApplication.cs @@ -30,9 +30,13 @@ namespace Umbraco.Web base.OnApplicationStarted(sender, e); if (ApplicationContext.Current.IsConfigured && GlobalSettings.DebugMode) - { - _mw = new ManifestWatcher(LoggerResolver.Current.Logger); - _mw.Start(Directory.GetDirectories(IOHelper.MapPath("~/App_Plugins/"))); + { + var appPluginFolder = IOHelper.MapPath("~/App_Plugins/"); + if (Directory.Exists(appPluginFolder)) + { + _mw = new ManifestWatcher(LoggerResolver.Current.Logger); + _mw.Start(Directory.GetDirectories(IOHelper.MapPath("~/App_Plugins/"))); + } } } diff --git a/src/Umbraco.Web/packages.config b/src/Umbraco.Web/packages.config index 6cdd67fe04..f3ecf6b064 100644 --- a/src/Umbraco.Web/packages.config +++ b/src/Umbraco.Web/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/Umbraco.Web/umbraco.presentation/NotFoundHandlers.cs b/src/Umbraco.Web/umbraco.presentation/NotFoundHandlers.cs index 6854bb9a4d..27e62888d4 100644 --- a/src/Umbraco.Web/umbraco.presentation/NotFoundHandlers.cs +++ b/src/Umbraco.Web/umbraco.presentation/NotFoundHandlers.cs @@ -326,8 +326,7 @@ namespace umbraco { HttpContext.Current.Request.ServerVariables["SERVER_NAME"], ApplicationContext.Current.Services.EntityService, new PublishedContentQuery(UmbracoContext.Current.ContentCache, UmbracoContext.Current.MediaCache), - ApplicationContext.Current.Services.DomainService, - ApplicationContext.Current.Services.LocalizationService); + ApplicationContext.Current.Services.DomainService); if (error404.HasValue) { diff --git a/src/umbraco.businesslogic/packages.config b/src/umbraco.businesslogic/packages.config index 65ef682839..4a0a1d249b 100644 --- a/src/umbraco.businesslogic/packages.config +++ b/src/umbraco.businesslogic/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/umbraco.businesslogic/umbraco.businesslogic.csproj b/src/umbraco.businesslogic/umbraco.businesslogic.csproj index c34a55d08b..e134c33eab 100644 --- a/src/umbraco.businesslogic/umbraco.businesslogic.csproj +++ b/src/umbraco.businesslogic/umbraco.businesslogic.csproj @@ -106,13 +106,13 @@ false - - False - ..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.dll + + ..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.dll + True - - False - ..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll + + ..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll + True False @@ -295,5 +295,4 @@ - \ No newline at end of file