From 7e6fdcb05e0113bb8b006b8743eb16a3e07fa64d Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Dec 2013 18:10:44 +1100 Subject: [PATCH] back ported template repo and file service updates --- src/Umbraco.Core/Models/TemplateNode.cs | 34 ++ .../Interfaces/ITemplateRepository.cs | 15 + .../Repositories/TemplateRepository.cs | 151 ++++++++ src/Umbraco.Core/Services/FileService.cs | 215 ++++++----- src/Umbraco.Core/Services/IFileService.cs | 15 + src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Repositories/TemplateRepositoryTest.cs | 336 ++++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + 8 files changed, 674 insertions(+), 94 deletions(-) create mode 100644 src/Umbraco.Core/Models/TemplateNode.cs create mode 100644 src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs diff --git a/src/Umbraco.Core/Models/TemplateNode.cs b/src/Umbraco.Core/Models/TemplateNode.cs new file mode 100644 index 0000000000..1fc51f8498 --- /dev/null +++ b/src/Umbraco.Core/Models/TemplateNode.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Models +{ + /// + /// Represents a template in a template tree + /// + public class TemplateNode + { + public TemplateNode(ITemplate template) + { + Template = template; + Children = new List(); + } + + /// + /// The current template + /// + public ITemplate Template { get; set; } + + /// + /// The children of the current template + /// + public IEnumerable Children { get; set; } + + /// + /// The parent template to the current template + /// + /// + /// Will be null if there is no parent + /// + public TemplateNode Parent { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs index 90d6f68adc..318b840819 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs @@ -7,5 +7,20 @@ namespace Umbraco.Core.Persistence.Repositories { ITemplate Get(string alias); IEnumerable GetAll(params string[] aliases); + + /// + /// Returns a template as a template node which can be traversed (parent, children) + /// + /// + /// + TemplateNode GetTemplateNode(string alias); + + /// + /// Given a template node in a tree, this will find the template node with the given alias if it is found in the hierarchy, otherwise null + /// + /// + /// + /// + TemplateNode FindTemplateInTree(TemplateNode anyNode, string alias); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index 5cc827680b..e3b69a786e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -273,6 +273,32 @@ namespace Umbraco.Core.Persistence.Repositories protected override void PersistDeletedItem(ITemplate entity) { + + //TODO: This isn't the most ideal way to delete a template tree, because below it will actually end up + // recursing back to this method for each descendant and re-looking up the template list causing an extrac + // SQL call - not ideal but there shouldn't ever be a heaping list of descendant templates. + //The easiest way to overcome this is to expose the underlying cache upwards so that the repository has access + // to it, then in the PersistDeletedTemplate we wouldn't recurse the underlying function, we'd just call + // PersistDeletedItem with a Template object and clear it's cache. + + var sql = new Sql(); + sql.Select("*").From().Where(dto => dto.Master != null || dto.NodeId == entity.Id); + var dtos = Database.Fetch(sql); + var self = dtos.Single(x => x.NodeId == entity.Id); + var allChildren = dtos.Except(new[] { self }); + var hierarchy = GenerateTemplateHierarchy(self, allChildren); + //remove ourselves + hierarchy.Remove(self); + //change the order so it goes bottom up! + hierarchy.Reverse(); + + //delete the hierarchy + foreach (var descendant in hierarchy) + { + PersistDeletedTemplate(descendant); + } + + //now we can delete this one base.PersistDeletedItem(entity); //Check for file under the Masterpages filesystem @@ -298,6 +324,34 @@ namespace Umbraco.Core.Persistence.Repositories #endregion + private void PersistDeletedTemplate(TemplateDto dto) + { + //we need to get the real template for this item unfortunately to remove it + var template = Get(dto.NodeId); + if (template != null) + { + //NOTE: We must cast here so that it goes to the outter method to + // ensure the cache is updated. + PersistDeletedItem((IEntity)template); + } + } + + /// + /// Returns a list of templates in order of descendants from the parent + /// + /// + /// + /// + private static List GenerateTemplateHierarchy(TemplateDto template, IEnumerable allChildTemplates) + { + var hierarchy = new List { template }; + foreach (var t in allChildTemplates.Where(x => x.Master == template.NodeId)) + { + hierarchy.AddRange(GenerateTemplateHierarchy(t, allChildTemplates)); + } + return hierarchy; + } + private void PopulateViewTemplate(ITemplate template, string fileName) { string content = string.Empty; @@ -379,6 +433,103 @@ namespace Umbraco.Core.Persistence.Repositories } + /// + /// Returns a template as a template node which can be traversed (parent, children) + /// + /// + /// + public TemplateNode GetTemplateNode(string alias) + { + //in order to do this we need to get all of the templates and then organize, unfortunately + // our db structure does not use the path correctly for templates so we cannot just look + // up a template tree easily. + + //first get all template objects + var allTemplates = GetAll().ToArray(); + + var selfTemplate = allTemplates.SingleOrDefault(x => x.Alias == alias); + if (selfTemplate == null) + { + return null; + } + + //then we need to get all template Dto's because those contain the master property + var sql = new Sql(); + sql.Select("*").From(); + 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.Master.HasValue) + { + top = allDtos.Single(x => x.NodeId == top.Master.Value); + } + + var topNode = new TemplateNode(allTemplates.Single(x => x.Id == top.NodeId)); + var childIds = allDtos.Where(x => x.Master == top.NodeId).Select(x => x.NodeId); + //This now creates the hierarchy recursively + topNode.Children = CreateChildren(topNode, childIds, allTemplates, allDtos); + + //now we'll return the TemplateNode requested + return FindTemplateInTree(topNode, alias); + } + + private static TemplateNode WalkTree(TemplateNode current, string alias) + { + //now walk the tree to find the node + if (current.Template.Alias == alias) + { + return current; + } + foreach (var c in current.Children) + { + var found = WalkTree(c, alias); + if (found != null) return found; + } + return null; + } + + /// + /// Given a template node in a tree, this will find the template node with the given alias if it is found in the hierarchy, otherwise null + /// + /// + /// + /// + public TemplateNode FindTemplateInTree(TemplateNode anyNode, string alias) + { + //first get the root + var top = anyNode; + while (top.Parent != null) + { + top = top.Parent; + } + return WalkTree(top, alias); + } + + private static IEnumerable CreateChildren(TemplateNode parent, IEnumerable childIds, ITemplate[] allTemplates, TemplateDto[] allDtos) + { + var children = new List(); + foreach (var i in childIds) + { + var template = allTemplates.Single(x => x.Id == i); + var child = new TemplateNode(template) + { + Parent = parent + }; + + //add to our list + children.Add(child); + + //get this node's children + var kids = allDtos.Where(x => x.Master == i).Select(x => x.NodeId).ToArray(); + + //recurse + child.Children = CreateChildren(child, kids, allTemplates, allDtos); + } + return children; + } + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/FileService.cs b/src/Umbraco.Core/Services/FileService.cs index c912793e05..5d4f2b1da8 100644 --- a/src/Umbraco.Core/Services/FileService.cs +++ b/src/Umbraco.Core/Services/FileService.cs @@ -13,24 +13,24 @@ namespace Umbraco.Core.Services /// public class FileService : IFileService { - private readonly RepositoryFactory _repositoryFactory; + private readonly RepositoryFactory _repositoryFactory; private readonly IUnitOfWorkProvider _fileUowProvider; private readonly IDatabaseUnitOfWorkProvider _dataUowProvider; public FileService() : this(new RepositoryFactory()) - {} + { } public FileService(RepositoryFactory repositoryFactory) - : this(new FileUnitOfWorkProvider(), new PetaPocoUnitOfWorkProvider(), repositoryFactory) + : this(new FileUnitOfWorkProvider(), new PetaPocoUnitOfWorkProvider(), repositoryFactory) { } - public FileService(IUnitOfWorkProvider fileProvider, IDatabaseUnitOfWorkProvider dataProvider, RepositoryFactory repositoryFactory) + public FileService(IUnitOfWorkProvider fileProvider, IDatabaseUnitOfWorkProvider dataProvider, RepositoryFactory repositoryFactory) { - _repositoryFactory = repositoryFactory; - _fileUowProvider = fileProvider; - _dataUowProvider = dataProvider; + _repositoryFactory = repositoryFactory; + _fileUowProvider = fileProvider; + _dataUowProvider = dataProvider; } /// @@ -65,19 +65,19 @@ namespace Umbraco.Core.Services /// public void SaveStylesheet(Stylesheet stylesheet, int userId = 0) { - if (SavingStylesheet.IsRaisedEventCancelled(new SaveEventArgs(stylesheet), this)) - return; - - var uow = _fileUowProvider.GetUnitOfWork(); + if (SavingStylesheet.IsRaisedEventCancelled(new SaveEventArgs(stylesheet), this)) + return; + + var uow = _fileUowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateStylesheetRepository(uow, _dataUowProvider.GetUnitOfWork())) - { - repository.AddOrUpdate(stylesheet); - uow.Commit(); + { + repository.AddOrUpdate(stylesheet); + uow.Commit(); - SavedStylesheet.RaiseEvent(new SaveEventArgs(stylesheet, false), this); - } + SavedStylesheet.RaiseEvent(new SaveEventArgs(stylesheet, false), this); + } - Audit.Add(AuditTypes.Save, string.Format("Save Stylesheet performed by user"), userId, -1); + Audit.Add(AuditTypes.Save, string.Format("Save Stylesheet performed by user"), userId, -1); } /// @@ -92,15 +92,15 @@ namespace Umbraco.Core.Services { var stylesheet = repository.Get(name); - if (DeletingStylesheet.IsRaisedEventCancelled(new DeleteEventArgs(stylesheet), this)) - return; + if (DeletingStylesheet.IsRaisedEventCancelled(new DeleteEventArgs(stylesheet), this)) + return; - repository.Delete(stylesheet); - uow.Commit(); + repository.Delete(stylesheet); + uow.Commit(); - DeletedStylesheet.RaiseEvent(new DeleteEventArgs(stylesheet, false), this); + DeletedStylesheet.RaiseEvent(new DeleteEventArgs(stylesheet, false), this); - Audit.Add(AuditTypes.Delete, string.Format("Delete Stylesheet performed by user"), userId, -1); + Audit.Add(AuditTypes.Delete, string.Format("Delete Stylesheet performed by user"), userId, -1); } } @@ -146,19 +146,19 @@ namespace Umbraco.Core.Services /// public void SaveScript(Script script, int userId = 0) { - if (SavingScript.IsRaisedEventCancelled(new SaveEventArgs