From c783900097d3e715657be01905d0528a968e4762 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 11 Jan 2016 17:52:29 +0100 Subject: [PATCH 1/4] Support folders for package installs (for document types and data types) and imports for document types --- .../Repositories/EntityContainerRepository.cs | 8 + .../Services/ContentTypeService.cs | 24 ++- src/Umbraco.Core/Services/DataTypeService.cs | 10 ++ .../Services/IContentTypeService.cs | 2 + src/Umbraco.Core/Services/IDataTypeService.cs | 1 + src/Umbraco.Core/Services/PackagingService.cs | 166 +++++++++++++++--- src/Umbraco.Core/Services/ServiceContext.cs | 8 +- src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs | 1 + 8 files changed, 193 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs index 6fd5045e04..6412dc00cf 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs @@ -47,6 +47,14 @@ namespace Umbraco.Core.Persistence.Repositories return nodeDto == null ? null : CreateEntity(nodeDto); } + public EntityContainer Get(string name, int level, Guid umbracoObjectTypeId) + { + var sql = GetBaseQuery(false).Where("text=@name AND level=@level AND nodeObjectType=@umbracoObjectTypeId", new { name, level, umbracoObjectTypeId }); + + var nodeDto = Database.Fetch(sql).FirstOrDefault(); + return nodeDto == null ? null : CreateEntity(nodeDto); + } + protected override IEnumerable PerformGetAll(params int[] ids) { throw new NotImplementedException(); diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index eff7202201..2a4f1de0be 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -141,16 +141,26 @@ namespace Umbraco.Core.Services } } + public EntityContainer GetMediaTypeContainer(string name, int level) + { + var uow = UowProvider.GetUnitOfWork(); + using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) + { + var container = repo.Get(name, level, Constants.ObjectTypes.MediaTypeContainerGuid); + return container; + } + } + public EntityContainer GetContentTypeContainer(Guid containerId) { return GetContainer(containerId, Constants.ObjectTypes.DocumentTypeGuid); } - + public EntityContainer GetMediaTypeContainer(Guid containerId) { return GetContainer(containerId, Constants.ObjectTypes.MediaTypeGuid); } - + private EntityContainer GetContainer(Guid containerId, Guid containedObjectType) { var uow = UowProvider.GetUnitOfWork(); @@ -163,6 +173,16 @@ namespace Umbraco.Core.Services } } + public EntityContainer GetContentTypeContainer(string name, int level) + { + var uow = UowProvider.GetUnitOfWork(); + using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) + { + var container = repo.Get(name, level, Constants.ObjectTypes.DocumentTypeContainerGuid); + return container; + } + } + public void DeleteContentTypeContainer(int containerId, int userId = 0) { var uow = UowProvider.GetUnitOfWork(); diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index bd505ec283..f2bc753fec 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -78,6 +78,16 @@ namespace Umbraco.Core.Services } } + public EntityContainer GetContainer(string name, int level) + { + var uow = UowProvider.GetUnitOfWork(); + using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) + { + var container = repo.Get(name, level, Constants.ObjectTypes.DataTypeContainerGuid); + return container; + } + } + public void SaveContainer(EntityContainer container, int userId = 0) { if (container.ContainedObjectType != Constants.ObjectTypes.DataTypeGuid) diff --git a/src/Umbraco.Core/Services/IContentTypeService.cs b/src/Umbraco.Core/Services/IContentTypeService.cs index 9e59308a39..69a162db42 100644 --- a/src/Umbraco.Core/Services/IContentTypeService.cs +++ b/src/Umbraco.Core/Services/IContentTypeService.cs @@ -24,8 +24,10 @@ namespace Umbraco.Core.Services void SaveMediaTypeContainer(EntityContainer container, int userId = 0); EntityContainer GetContentTypeContainer(int containerId); EntityContainer GetContentTypeContainer(Guid containerId); + EntityContainer GetContentTypeContainer(string folderName, int level); EntityContainer GetMediaTypeContainer(int containerId); EntityContainer GetMediaTypeContainer(Guid containerId); + EntityContainer GetMediaTypeContainer(string folderName, int level); void DeleteMediaTypeContainer(int folderId, int userId = 0); void DeleteContentTypeContainer(int containerId, int userId = 0); diff --git a/src/Umbraco.Core/Services/IDataTypeService.cs b/src/Umbraco.Core/Services/IDataTypeService.cs index ce9e4a3901..eb88814b9e 100644 --- a/src/Umbraco.Core/Services/IDataTypeService.cs +++ b/src/Umbraco.Core/Services/IDataTypeService.cs @@ -14,6 +14,7 @@ namespace Umbraco.Core.Services void SaveContainer(EntityContainer container, int userId = 0); EntityContainer GetContainer(int containerId); EntityContainer GetContainer(Guid containerId); + EntityContainer GetContainer(string folderName, int level); void DeleteContainer(int containerId, int userId = 0); /// diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index a3c4f0f8a2..d6844651e3 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; +using System.Web; +using System.Web.UI.WebControls; using System.Xml.Linq; using System.Xml.XPath; using Newtonsoft.Json; @@ -11,12 +13,14 @@ using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Packaging; using Umbraco.Core.Packaging.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; +using Content = Umbraco.Core.Models.Content; namespace Umbraco.Core.Services { @@ -34,6 +38,7 @@ namespace Umbraco.Core.Services private readonly IDataTypeService _dataTypeService; private readonly IFileService _fileService; private readonly ILocalizationService _localizationService; + private readonly IEntityService _entityService; private readonly RepositoryFactory _repositoryFactory; private readonly IDatabaseUnitOfWorkProvider _uowProvider; private Dictionary _importedContentTypes; @@ -50,6 +55,7 @@ namespace Umbraco.Core.Services IDataTypeService dataTypeService, IFileService fileService, ILocalizationService localizationService, + IEntityService entityService, IUserService userService, RepositoryFactory repositoryFactory, IDatabaseUnitOfWorkProvider uowProvider) @@ -62,6 +68,7 @@ namespace Umbraco.Core.Services _dataTypeService = dataTypeService; _fileService = fileService; _localizationService = localizationService; + _entityService = entityService; _repositoryFactory = repositoryFactory; _uowProvider = uowProvider; _userService = userService; @@ -344,6 +351,9 @@ namespace Umbraco.Core.Services //Otherwise something like uSync won't work. var fields = new List>(); var isSingleDocTypeImport = unsortedDocumentTypes.Count == 1; + + var importedFolders = CreateContentTypeFolderStructure(unsortedDocumentTypes); + if (isSingleDocTypeImport == false) { //NOTE Here we sort the doctype XElements based on dependencies @@ -404,6 +414,15 @@ namespace Umbraco.Core.Services } } + foreach (var contentType in _importedContentTypes) + { + var ct = contentType.Value; + if (importedFolders.ContainsKey(ct.Alias)) + { + ct.ParentId = importedFolders[ct.Alias]; + } + } + //Save the newly created/updated IContentType objects var list = _importedContentTypes.Select(x => x.Value).ToList(); _contentTypeService.Save(list, userId); @@ -435,6 +454,52 @@ namespace Umbraco.Core.Services return list; } + private Dictionary CreateContentTypeFolderStructure(IEnumerable unsortedDocumentTypes) + { + var importedFolders = new Dictionary(); + foreach (var documentType in unsortedDocumentTypes) + { + var foldersAttribute = documentType.Attribute("Folders"); + if (foldersAttribute != null) + { + var containerId = 0; + var alias = documentType.Element("Info").Element("Alias").Value; + + var folders = foldersAttribute.Value.Split('/'); + var rootFolder = HttpUtility.UrlDecode(folders[0]); + var current = _contentTypeService.GetContentTypeContainer(rootFolder, 1); + + var rootFolderId = current != null + ? current.Id + : _contentTypeService.CreateContentTypeContainer(-1, rootFolder).Result; + + current = _contentTypeService.GetContentTypeContainer(rootFolderId); + + for (var i = 1; i < folders.Length; i++) + { + var folderName = HttpUtility.UrlDecode(folders[i]); + current = CreateContentTypeChildFolder(folderName, current); + containerId = current.Id; + } + + importedFolders.Add(alias, containerId); + } + } + + return importedFolders; + } + + private EntityContainer CreateContentTypeChildFolder(string folderName, IUmbracoEntity current) + { + var children = _entityService.GetChildren(current.Id).ToArray(); + var found = children.Any(x => x.Name.InvariantEquals(folderName)); + var containerId = found + ? children.Single(x => x.Name.InvariantEquals(folderName)).Id + : _contentTypeService.CreateContentTypeContainer(current.Id, folderName).Result; + + return _contentTypeService.GetContentTypeContainer(containerId); + } + private IContentType CreateContentTypeFromXml(XElement documentType) { var infoElement = documentType.Element("Info"); @@ -657,13 +722,13 @@ namespace Umbraco.Core.Services if (sortOrderElement != null) int.TryParse(sortOrderElement.Value, out sortOrder); var propertyType = new PropertyType(dataTypeDefinition, property.Element("Alias").Value) - { - Name = property.Element("Name").Value, - Description = property.Element("Description") != null ? property.Element("Description").Value : null, - Mandatory = property.Element("Mandatory") != null ? property.Element("Mandatory").Value.ToLowerInvariant().Equals("true") : false, - ValidationRegExp = property.Element("Validation") != null ? property.Element("Validation").Value : null, - SortOrder = sortOrder - }; + { + Name = property.Element("Name").Value, + Description = property.Element("Description") != null ? property.Element("Description").Value : null, + Mandatory = property.Element("Mandatory") != null ? property.Element("Mandatory").Value.ToLowerInvariant().Equals("true") : false, + ValidationRegExp = property.Element("Validation") != null ? property.Element("Validation").Value : null, + SortOrder = sortOrder + }; var tab = property.Element("Tab").Value; if (string.IsNullOrEmpty(tab)) @@ -801,6 +866,8 @@ namespace Umbraco.Core.Services ? (from doc in element.Elements("DataType") select doc).ToList() : new List { element }; + var importedFolders = CreateDataTypeFolderStructure(dataTypeElements); + foreach (var dataTypeElement in dataTypeElements) { var dataTypeDefinitionName = dataTypeElement.Attribute("Name").Value; @@ -810,6 +877,10 @@ namespace Umbraco.Core.Services var dataTypeDefinitionId = new Guid(dataTypeElement.Attribute("Definition").Value); var databaseTypeAttribute = dataTypeElement.Attribute("DatabaseType"); + + var parentId = -1; + if (importedFolders.ContainsKey(dataTypeDefinitionName)) + parentId = importedFolders[dataTypeDefinitionName]; var definition = _dataTypeService.GetDataTypeDefinitionById(dataTypeDefinitionId); //If the datatypedefinition doesn't already exist we create a new new according to the one in the package xml @@ -823,11 +894,12 @@ namespace Umbraco.Core.Services if (legacyPropertyEditorId != Guid.Empty) { var dataTypeDefinition = new DataTypeDefinition(-1, legacyPropertyEditorId) - { - Key = dataTypeDefinitionId, - Name = dataTypeDefinitionName, - DatabaseType = databaseType - }; + { + Key = dataTypeDefinitionId, + Name = dataTypeDefinitionName, + DatabaseType = databaseType, + ParentId = parentId + }; dataTypes.Add(dataTypeDefinitionName, dataTypeDefinition); } else @@ -837,12 +909,18 @@ namespace Umbraco.Core.Services { Key = dataTypeDefinitionId, Name = dataTypeDefinitionName, - DatabaseType = databaseType + DatabaseType = databaseType, + ParentId = parentId }; dataTypes.Add(dataTypeDefinitionName, dataTypeDefinition); } } + else + { + definition.ParentId = parentId; + _dataTypeService.Save(definition, userId); + } } var list = dataTypes.Select(x => x.Value).ToList(); @@ -865,6 +943,52 @@ namespace Umbraco.Core.Services return list; } + private Dictionary CreateDataTypeFolderStructure(IEnumerable datatypeElements) + { + var importedFolders = new Dictionary(); + foreach (var datatypeElement in datatypeElements) + { + var foldersAttribute = datatypeElement.Attribute("Folders"); + if (foldersAttribute != null) + { + var containerId = 0; + var name = datatypeElement.Attribute("Name").Value; + + var folders = foldersAttribute.Value.Split('/'); + var rootFolder = HttpUtility.UrlDecode(folders[0]); + var current = _dataTypeService.GetContainer(rootFolder, 1); + + var rootFolderId = current != null + ? current.Id + : _dataTypeService.CreateContainer(-1, rootFolder).Result; + + current = _dataTypeService.GetContainer(rootFolderId); + + for (var i = 1; i < folders.Length; i++) + { + var folderName = HttpUtility.UrlDecode(folders[i]); + current = CreateDataTypeChildFolder(folderName, current); + containerId = current.Id; + } + + importedFolders.Add(name, containerId); + } + } + + return importedFolders; + } + + private EntityContainer CreateDataTypeChildFolder(string folderName, IUmbracoEntity current) + { + var children = _entityService.GetChildren(current.Id).ToArray(); + var found = children.Any(x => x.Name.InvariantEquals(folderName)); + var containerId = found + ? children.Single(x => x.Name.InvariantEquals(folderName)).Id + : _dataTypeService.CreateContainer(current.Id, folderName).Result; + + return _dataTypeService.GetContainer(containerId); + } + private void SavePrevaluesFromXml(List dataTypes, IEnumerable dataTypeElements) { foreach (var dataTypeElement in dataTypeElements) @@ -1104,9 +1228,9 @@ namespace Umbraco.Core.Services if (existingLanguage == null) { var langauge = new Language(isoCode) - { - CultureName = languageElement.Attribute("FriendlyName").Value - }; + { + CultureName = languageElement.Attribute("FriendlyName").Value + }; _localizationService.Save(langauge); list.Add(langauge); } @@ -1388,11 +1512,11 @@ namespace Umbraco.Core.Services } var field = new TopologicalSorter.DependencyField - { - Alias = elementCopy.Element("Alias").Value, - Item = new Lazy(() => elementCopy), - DependsOn = dependencies.ToArray() - }; + { + Alias = elementCopy.Element("Alias").Value, + Item = new Lazy(() => elementCopy), + DependsOn = dependencies.ToArray() + }; fields.Add(field); } diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index f8a78aed3d..76be1e2151 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -279,14 +279,14 @@ namespace Umbraco.Core.Services if (_localizationService == null) _localizationService = new Lazy(() => new LocalizationService(provider, repositoryFactory, logger, eventMessagesFactory)); - if (_packagingService == null) - _packagingService = new Lazy(() => new PackagingService(logger, _contentService.Value, _contentTypeService.Value, _mediaService.Value, _macroService.Value, _dataTypeService.Value, _fileService.Value, _localizationService.Value, _userService.Value, repositoryFactory, provider)); - if (_entityService == null) _entityService = new Lazy(() => new EntityService( - provider, repositoryFactory, logger, eventMessagesFactory, + provider, repositoryFactory, logger, eventMessagesFactory, _contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value, _memberService.Value, _memberTypeService.Value, cache.RuntimeCache)); + + if (_packagingService == null) + _packagingService = new Lazy(() => new PackagingService(logger, _contentService.Value, _contentTypeService.Value, _mediaService.Value, _macroService.Value, _dataTypeService.Value, _fileService.Value, _localizationService.Value, _entityService.Value, _userService.Value, repositoryFactory, provider)); if (_relationService == null) _relationService = new Lazy(() => new RelationService(provider, repositoryFactory, logger, eventMessagesFactory, _entityService.Value)); diff --git a/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs index fd2dc02292..e6c497d1b6 100644 --- a/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs @@ -393,6 +393,7 @@ namespace Umbraco.Tests.Mvc new Mock().Object, new Mock().Object, new Mock().Object, + new Mock().Object, new Mock().Object, new RepositoryFactory(CacheHelper.CreateDisabledCacheHelper(), logger, Mock.Of(), umbracoSettings), new Mock().Object), From 79f0e2563c2ccf425a122de677b70aa2dc1245db Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 11 Jan 2016 22:01:05 +0100 Subject: [PATCH 2/4] Allows for creation of packages with folder structures for doctypes and datatypes and exporting document types with folder structures Fixes installing packages while maintaining the exported folder structure --- .../Services/EntityXmlSerializer.cs | 34 +++++++++--- src/Umbraco.Core/Services/PackagingService.cs | 34 ++++++------ .../dialogs/exportDocumenttype.aspx.cs | 28 ++++++++-- .../PackageInstance/CreatedPackage.cs | 52 ++++++++++++++++--- .../datatype/DataTypeDefinition.cs | 13 +++-- .../businesslogic/web/DocumentType.cs | 14 +++-- 6 files changed, 134 insertions(+), 41 deletions(-) diff --git a/src/Umbraco.Core/Services/EntityXmlSerializer.cs b/src/Umbraco.Core/Services/EntityXmlSerializer.cs index 09c4dfcf48..ef73744da8 100644 --- a/src/Umbraco.Core/Services/EntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/EntityXmlSerializer.cs @@ -1,6 +1,8 @@ +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Net.Http.Formatting; using System.Xml; using System.Xml.Linq; using Umbraco.Core.Configuration; @@ -138,6 +140,18 @@ namespace Umbraco.Core.Services /// IDataTypeDefinition type to export /// containing the xml representation of the IDataTypeDefinition object public XElement Serialize(IDataTypeService dataTypeService, IDataTypeDefinition dataTypeDefinition) + { + return Serialize(dataTypeService, dataTypeDefinition, string.Empty); + } + + /// + /// Exports an item to xml as an + /// + /// + /// IDataTypeDefinition type to export + /// The path of folders for this data type separated by a backslash, for example: `SEO/Meta` + /// containing the xml representation of the IDataTypeDefinition object + public XElement Serialize(IDataTypeService dataTypeService, IDataTypeDefinition dataTypeDefinition, string folders) { var prevalues = new XElement("PreValues"); var prevalueList = dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeDefinition.Id) @@ -161,6 +175,8 @@ namespace Umbraco.Core.Services xml.Add(new XAttribute("Id", dataTypeDefinition.PropertyEditorAlias)); xml.Add(new XAttribute("Definition", dataTypeDefinition.Key)); xml.Add(new XAttribute("DatabaseType", dataTypeDefinition.DatabaseType.ToString())); + if(string.IsNullOrWhiteSpace(folders) == false) + xml.Add(new XAttribute("Folders", folders)); return xml; } @@ -322,8 +338,9 @@ namespace Umbraco.Core.Services /// /// /// Content type to export + /// The path of folders for this content type separated by a backslash, for example: `SEO/Meta` /// containing the xml representation of the IContentType object - public XElement Serialize(IDataTypeService dataTypeService, IContentType contentType) + public XElement Serialize(IDataTypeService dataTypeService, IContentType contentType, string folders = "") { var info = new XElement("Info", new XElement("Name", contentType.Name), @@ -397,11 +414,16 @@ namespace Umbraco.Core.Services tabs.Add(tab); } - return new XElement("DocumentType", - info, - structure, - genericProperties, - tabs); + var xml = new XElement("DocumentType", + info, + structure, + genericProperties, + tabs); + + if(string.IsNullOrWhiteSpace(folders) == false) + xml.Add(new XAttribute("Folders", folders)); + + return xml; } /// diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index d6844651e3..9c9915f972 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -462,27 +462,25 @@ namespace Umbraco.Core.Services var foldersAttribute = documentType.Attribute("Folders"); if (foldersAttribute != null) { - var containerId = 0; var alias = documentType.Element("Info").Element("Alias").Value; - var folders = foldersAttribute.Value.Split('/'); var rootFolder = HttpUtility.UrlDecode(folders[0]); var current = _contentTypeService.GetContentTypeContainer(rootFolder, 1); - var rootFolderId = current != null - ? current.Id - : _contentTypeService.CreateContentTypeContainer(-1, rootFolder).Result; + if (current == null) + { + var rootFolderId = _contentTypeService.CreateContentTypeContainer(-1, rootFolder).Result; + current = _contentTypeService.GetContentTypeContainer(rootFolderId); + } - current = _contentTypeService.GetContentTypeContainer(rootFolderId); + importedFolders.Add(alias, current.Id); for (var i = 1; i < folders.Length; i++) { var folderName = HttpUtility.UrlDecode(folders[i]); current = CreateContentTypeChildFolder(folderName, current); - containerId = current.Id; + importedFolders[alias] = current.Id; } - - importedFolders.Add(alias, containerId); } } @@ -877,7 +875,7 @@ namespace Umbraco.Core.Services var dataTypeDefinitionId = new Guid(dataTypeElement.Attribute("Definition").Value); var databaseTypeAttribute = dataTypeElement.Attribute("DatabaseType"); - + var parentId = -1; if (importedFolders.ContainsKey(dataTypeDefinitionName)) parentId = importedFolders[dataTypeDefinitionName]; @@ -951,27 +949,25 @@ namespace Umbraco.Core.Services var foldersAttribute = datatypeElement.Attribute("Folders"); if (foldersAttribute != null) { - var containerId = 0; var name = datatypeElement.Attribute("Name").Value; - var folders = foldersAttribute.Value.Split('/'); var rootFolder = HttpUtility.UrlDecode(folders[0]); var current = _dataTypeService.GetContainer(rootFolder, 1); - var rootFolderId = current != null - ? current.Id - : _dataTypeService.CreateContainer(-1, rootFolder).Result; + if (current == null) + { + var rootFolderId = _dataTypeService.CreateContainer(-1, rootFolder).Result; + current = _dataTypeService.GetContainer(rootFolderId); + } - current = _dataTypeService.GetContainer(rootFolderId); + importedFolders.Add(name, current.Id); for (var i = 1; i < folders.Length; i++) { var folderName = HttpUtility.UrlDecode(folders[i]); current = CreateDataTypeChildFolder(folderName, current); - containerId = current.Id; + importedFolders[name] = current.Id; } - - importedFolders.Add(name, containerId); } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/exportDocumenttype.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/exportDocumenttype.aspx.cs index 1baf170182..2be17aa8e1 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/exportDocumenttype.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/exportDocumenttype.aspx.cs @@ -1,8 +1,10 @@ using System; using System.Collections; +using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; +using System.Linq; using System.Web; using System.Web.SessionState; using System.Web.UI; @@ -11,6 +13,7 @@ using System.Web.UI.HtmlControls; using umbraco.cms.businesslogic.web; using System.Xml; +using Umbraco.Core; namespace umbraco.presentation.dialogs { @@ -30,13 +33,32 @@ namespace umbraco.presentation.dialogs if (documentTypeId > 0) { cms.businesslogic.web.DocumentType dt = new cms.businesslogic.web.DocumentType(documentTypeId); - if (dt != null) + if (dt != null) { - Response.AddHeader("Content-Disposition", "attachment;filename=" + dt.Alias + ".udt"); + var folderNames = string.Empty; + if (dt.Level != 1) + { + var folders = new List(); + + var current = dt.Parent; + while (current.Level >= 1) + { + if (current.nodeObjectType == Constants.ObjectTypes.DocumentTypeContainerGuid) + folders.Add(HttpUtility.UrlEncode(current.Text)); + + if (current.Level == 1) + break; + current = current.Parent; + } + + folderNames = string.Join("/", folders.ToArray().Reverse()); + } + + Response.AddHeader("Content-Disposition", "attachment;filename=" + dt.Alias + ".udt"); Response.ContentType = "application/octet-stream"; XmlDocument doc = new XmlDocument(); - doc.AppendChild(dt.ToXml(doc)); + doc.AppendChild(dt.ToXml(doc, folderNames)); XmlWriterSettings writerSettings = new XmlWriterSettings(); diff --git a/src/umbraco.cms/businesslogic/Packager/PackageInstance/CreatedPackage.cs b/src/umbraco.cms/businesslogic/Packager/PackageInstance/CreatedPackage.cs index 29eaaf25ea..b883f44e79 100644 --- a/src/umbraco.cms/businesslogic/Packager/PackageInstance/CreatedPackage.cs +++ b/src/umbraco.cms/businesslogic/Packager/PackageInstance/CreatedPackage.cs @@ -13,6 +13,7 @@ using umbraco.cms.businesslogic.web; using umbraco.cms.businesslogic.macro; using Umbraco.Core.IO; using Umbraco.Core.Models; +using Umbraco.Core.Services; using File = System.IO.File; using Template = umbraco.cms.businesslogic.template.Template; @@ -221,9 +222,29 @@ namespace umbraco.cms.businesslogic.packager } } + foreach (DocumentType d in dtl) { - docTypes.AppendChild(d.ToXml(_packageManifest)); + var folderNames = string.Empty; + if (d.Level != 1) + { + var folders = new List(); + + var current = d.Parent; + while (current.Level >= 1) + { + if (current.nodeObjectType == Constants.ObjectTypes.DocumentTypeContainerGuid) + folders.Add(HttpUtility.UrlEncode(current.Text)); + + if (current.Level == 1) + break; + current = current.Parent; + } + + folderNames = string.Join("/", folders.ToArray().Reverse()); + } + + docTypes.AppendChild(d.ToXml(_packageManifest, folderNames)); } AppendElement(docTypes); @@ -295,7 +316,28 @@ namespace umbraco.cms.businesslogic.packager if (int.TryParse(dtId, out outInt)) { datatype.DataTypeDefinition dtd = new datatype.DataTypeDefinition(outInt); - dataTypes.AppendChild(dtd.ToXml(_packageManifest)); + + var folderNames = string.Empty; + var dataTypeService = ApplicationContext.Current.Services.DataTypeService; + var dataTypeDefinition = dataTypeService.GetDataTypeDefinitionById(dtd.Id); + if (dataTypeDefinition.Level != 1) + { + var folders = new List(); + + var current = dataTypeService.GetContainer(dataTypeDefinition.ParentId); + while (current.Level >= 1) + { + folders.Add(HttpUtility.UrlEncode(current.Name)); + + if (current.Level == 1) + break; + current = dataTypeService.GetContainer(current.ParentId); + } + + folderNames = string.Join("/", folders.ToArray().Reverse()); + } + + dataTypes.AppendChild(dtd.ToXml(_packageManifest, folderNames)); } } AppendElement(dataTypes); @@ -371,16 +413,14 @@ namespace umbraco.cms.businesslogic.packager } } - + private void AddDocumentType(DocumentType dt, ref List dtl) { - if (dt.MasterContentType != 0) + if (dt.MasterContentType != 0 && dt.Parent.nodeObjectType == Constants.ObjectTypes.DocumentTypeGuid) { //first add masters var mDocT = new DocumentType(dt.MasterContentType); - AddDocumentType(mDocT, ref dtl); - } if (dtl.Contains(dt) == false) diff --git a/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs b/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs index 1e86544ab9..19acec8403 100644 --- a/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs +++ b/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs @@ -142,11 +142,16 @@ namespace umbraco.cms.businesslogic.datatype public XmlElement ToXml(XmlDocument xd) { - var serializer = new EntityXmlSerializer(); - var xml = serializer.Serialize(ApplicationContext.Current.Services.DataTypeService, DataTypeItem); - return (XmlElement)xml.GetXmlNode(xd); - + return ToXml(xd, string.Empty); } + + public XmlElement ToXml(XmlDocument xd, string folders) + { + var serializer = new EntityXmlSerializer(); + var xml = serializer.Serialize(ApplicationContext.Current.Services.DataTypeService, DataTypeItem, folders); + return (XmlElement)xml.GetXmlNode(xd); + } + #endregion #region Static methods diff --git a/src/umbraco.cms/businesslogic/web/DocumentType.cs b/src/umbraco.cms/businesslogic/web/DocumentType.cs index 27c2c4cb9f..2e8137d75c 100644 --- a/src/umbraco.cms/businesslogic/web/DocumentType.cs +++ b/src/umbraco.cms/businesslogic/web/DocumentType.cs @@ -473,14 +473,19 @@ namespace umbraco.cms.businesslogic.web } public XmlElement ToXml(XmlDocument xd) + { + return ToXml(xd, string.Empty); + } + + public XmlElement ToXml(XmlDocument xd, string folders) { var exporter = new EntityXmlSerializer(); - var xml = exporter.Serialize(ApplicationContext.Current.Services.DataTypeService, ContentType); + var xml = exporter.Serialize(ApplicationContext.Current.Services.DataTypeService, ContentType, folders); //convert the Linq to Xml structure to the old .net xml structure var xNode = xml.GetXmlNode(); var doc = (XmlElement)xd.ImportNode(xNode, true); - return doc; + return doc; } [Obsolete("Obsolete, Use SetDefaultTemplate(null) on Umbraco.Core.Models.ContentType", false)] @@ -538,7 +543,10 @@ namespace umbraco.cms.businesslogic.web protected override void setupNode() { var contentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(Id); - SetupNode(contentType); + + // If it's null, it's probably a folder + if (contentType != null) + SetupNode(contentType); } #endregion From f0b0cd1641c057c88453b602f615f5035a95d951 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 12 Jan 2016 14:32:45 +0100 Subject: [PATCH 3/4] adds success checking to the CreateContentTypeContainer results, adds overload instead of optional parameter (breaking change), changes GetContainer(string folderName, int level) to return IEnumerable and changed name to GetContainers - since there could be multiple results for the same container name at a given level. --- .../Repositories/EntityContainerRepository.cs | 6 +- .../Services/ContentTypeService.cs | 10 ++-- src/Umbraco.Core/Services/DataTypeService.cs | 5 +- .../Services/EntityXmlSerializer.cs | 13 ++++- .../Services/IContentTypeService.cs | 4 +- src/Umbraco.Core/Services/IDataTypeService.cs | 2 +- src/Umbraco.Core/Services/PackagingService.cs | 55 ++++++++++++++----- 7 files changed, 65 insertions(+), 30 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs index 6412dc00cf..f782b47ac1 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs @@ -47,12 +47,10 @@ namespace Umbraco.Core.Persistence.Repositories return nodeDto == null ? null : CreateEntity(nodeDto); } - public EntityContainer Get(string name, int level, Guid umbracoObjectTypeId) + public IEnumerable Get(string name, int level, Guid umbracoObjectTypeId) { var sql = GetBaseQuery(false).Where("text=@name AND level=@level AND nodeObjectType=@umbracoObjectTypeId", new { name, level, umbracoObjectTypeId }); - - var nodeDto = Database.Fetch(sql).FirstOrDefault(); - return nodeDto == null ? null : CreateEntity(nodeDto); + return Database.Fetch(sql).Select(CreateEntity); } protected override IEnumerable PerformGetAll(params int[] ids) diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 2a4f1de0be..64ec8a90e0 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -141,13 +141,12 @@ namespace Umbraco.Core.Services } } - public EntityContainer GetMediaTypeContainer(string name, int level) + public IEnumerable GetMediaTypeContainers(string name, int level) { var uow = UowProvider.GetUnitOfWork(); using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) { - var container = repo.Get(name, level, Constants.ObjectTypes.MediaTypeContainerGuid); - return container; + return repo.Get(name, level, Constants.ObjectTypes.MediaTypeContainerGuid); } } @@ -173,13 +172,12 @@ namespace Umbraco.Core.Services } } - public EntityContainer GetContentTypeContainer(string name, int level) + public IEnumerable GetContentTypeContainers(string name, int level) { var uow = UowProvider.GetUnitOfWork(); using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) { - var container = repo.Get(name, level, Constants.ObjectTypes.DocumentTypeContainerGuid); - return container; + return repo.Get(name, level, Constants.ObjectTypes.DocumentTypeContainerGuid); } } diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index f2bc753fec..4259a17035 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -78,13 +78,12 @@ namespace Umbraco.Core.Services } } - public EntityContainer GetContainer(string name, int level) + public IEnumerable GetContainers(string name, int level) { var uow = UowProvider.GetUnitOfWork(); using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow)) { - var container = repo.Get(name, level, Constants.ObjectTypes.DataTypeContainerGuid); - return container; + return repo.Get(name, level, Constants.ObjectTypes.DataTypeContainerGuid); } } diff --git a/src/Umbraco.Core/Services/EntityXmlSerializer.cs b/src/Umbraco.Core/Services/EntityXmlSerializer.cs index ef73744da8..193fa24577 100644 --- a/src/Umbraco.Core/Services/EntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/EntityXmlSerializer.cs @@ -333,6 +333,17 @@ namespace Umbraco.Core.Services return xml; } + /// + /// Exports an item to xml as an + /// + /// + /// Content type to export + /// containing the xml representation of the IContentType object + public XElement Serialize(IDataTypeService dataTypeService, IContentType contentType) + { + return Serialize(dataTypeService, contentType, string.Empty); + } + /// /// Exports an item to xml as an /// @@ -340,7 +351,7 @@ namespace Umbraco.Core.Services /// Content type to export /// The path of folders for this content type separated by a backslash, for example: `SEO/Meta` /// containing the xml representation of the IContentType object - public XElement Serialize(IDataTypeService dataTypeService, IContentType contentType, string folders = "") + public XElement Serialize(IDataTypeService dataTypeService, IContentType contentType, string folders) { var info = new XElement("Info", new XElement("Name", contentType.Name), diff --git a/src/Umbraco.Core/Services/IContentTypeService.cs b/src/Umbraco.Core/Services/IContentTypeService.cs index 69a162db42..9929f4b04b 100644 --- a/src/Umbraco.Core/Services/IContentTypeService.cs +++ b/src/Umbraco.Core/Services/IContentTypeService.cs @@ -24,10 +24,10 @@ namespace Umbraco.Core.Services void SaveMediaTypeContainer(EntityContainer container, int userId = 0); EntityContainer GetContentTypeContainer(int containerId); EntityContainer GetContentTypeContainer(Guid containerId); - EntityContainer GetContentTypeContainer(string folderName, int level); + IEnumerable GetContentTypeContainers(string folderName, int level); EntityContainer GetMediaTypeContainer(int containerId); EntityContainer GetMediaTypeContainer(Guid containerId); - EntityContainer GetMediaTypeContainer(string folderName, int level); + IEnumerable GetMediaTypeContainers(string folderName, int level); void DeleteMediaTypeContainer(int folderId, int userId = 0); void DeleteContentTypeContainer(int containerId, int userId = 0); diff --git a/src/Umbraco.Core/Services/IDataTypeService.cs b/src/Umbraco.Core/Services/IDataTypeService.cs index eb88814b9e..6a204667b4 100644 --- a/src/Umbraco.Core/Services/IDataTypeService.cs +++ b/src/Umbraco.Core/Services/IDataTypeService.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Services void SaveContainer(EntityContainer container, int userId = 0); EntityContainer GetContainer(int containerId); EntityContainer GetContainer(Guid containerId); - EntityContainer GetContainer(string folderName, int level); + IEnumerable GetContainers(string folderName, int level); void DeleteContainer(int containerId, int userId = 0); /// diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index 9c9915f972..e42cd02b4e 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -465,11 +465,18 @@ namespace Umbraco.Core.Services var alias = documentType.Element("Info").Element("Alias").Value; var folders = foldersAttribute.Value.Split('/'); var rootFolder = HttpUtility.UrlDecode(folders[0]); - var current = _contentTypeService.GetContentTypeContainer(rootFolder, 1); + //level 1 = root level folders, there can only be one with the same name + var current = _contentTypeService.GetContentTypeContainers(rootFolder, 1).FirstOrDefault(); if (current == null) { - var rootFolderId = _contentTypeService.CreateContentTypeContainer(-1, rootFolder).Result; + var tryCreateFolder = _contentTypeService.CreateContentTypeContainer(-1, rootFolder); + if (tryCreateFolder == false) + { + _logger.Error("Could not create folder: " + rootFolder, tryCreateFolder.Exception); + throw tryCreateFolder.Exception; + } + var rootFolderId = tryCreateFolder.Result; current = _contentTypeService.GetContentTypeContainer(rootFolderId); } @@ -491,11 +498,19 @@ namespace Umbraco.Core.Services { var children = _entityService.GetChildren(current.Id).ToArray(); var found = children.Any(x => x.Name.InvariantEquals(folderName)); - var containerId = found - ? children.Single(x => x.Name.InvariantEquals(folderName)).Id - : _contentTypeService.CreateContentTypeContainer(current.Id, folderName).Result; + if (found) + { + var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; + return _contentTypeService.GetContentTypeContainer(containerId); + } - return _contentTypeService.GetContentTypeContainer(containerId); + var tryCreateFolder = _contentTypeService.CreateContentTypeContainer(current.Id, folderName); + if (tryCreateFolder == false) + { + _logger.Error("Could not create folder: " + folderName, tryCreateFolder.Exception); + throw tryCreateFolder.Exception; + } + return _contentTypeService.GetContentTypeContainer(tryCreateFolder.Result); } private IContentType CreateContentTypeFromXml(XElement documentType) @@ -952,12 +967,18 @@ namespace Umbraco.Core.Services var name = datatypeElement.Attribute("Name").Value; var folders = foldersAttribute.Value.Split('/'); var rootFolder = HttpUtility.UrlDecode(folders[0]); - var current = _dataTypeService.GetContainer(rootFolder, 1); + //there will only be a single result by name for level 1 (root) containers + var current = _dataTypeService.GetContainers(rootFolder, 1).FirstOrDefault(); if (current == null) { - var rootFolderId = _dataTypeService.CreateContainer(-1, rootFolder).Result; - current = _dataTypeService.GetContainer(rootFolderId); + var tryCreateFolder = _dataTypeService.CreateContainer(-1, rootFolder); + if (tryCreateFolder == false) + { + _logger.Error("Could not create folder: " + rootFolder, tryCreateFolder.Exception); + throw tryCreateFolder.Exception; + } + current = _dataTypeService.GetContainer(tryCreateFolder.Result); } importedFolders.Add(name, current.Id); @@ -978,11 +999,19 @@ namespace Umbraco.Core.Services { var children = _entityService.GetChildren(current.Id).ToArray(); var found = children.Any(x => x.Name.InvariantEquals(folderName)); - var containerId = found - ? children.Single(x => x.Name.InvariantEquals(folderName)).Id - : _dataTypeService.CreateContainer(current.Id, folderName).Result; + if (found) + { + var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; + return _dataTypeService.GetContainer(containerId); + } - return _dataTypeService.GetContainer(containerId); + var tryCreateFolder = _dataTypeService.CreateContainer(current.Id, folderName); + if (tryCreateFolder == false) + { + _logger.Error("Could not create folder: " + folderName, tryCreateFolder.Exception); + throw tryCreateFolder.Exception; + } + return _dataTypeService.GetContainer(tryCreateFolder.Result); } private void SavePrevaluesFromXml(List dataTypes, IEnumerable dataTypeElements) From 06b7305d5b6b4efbb3f9e1c8ac1a825394b98bfb Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 12 Jan 2016 14:39:20 +0100 Subject: [PATCH 4/4] removes erroneous null check --- .../dialogs/exportDocumenttype.aspx.cs | 57 +++++++++---------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/exportDocumenttype.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/exportDocumenttype.aspx.cs index 2be17aa8e1..81c6835fcf 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/exportDocumenttype.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/exportDocumenttype.aspx.cs @@ -32,45 +32,42 @@ namespace umbraco.presentation.dialogs int documentTypeId = int.Parse(helper.Request("nodeID")); if (documentTypeId > 0) { - cms.businesslogic.web.DocumentType dt = new cms.businesslogic.web.DocumentType(documentTypeId); - if (dt != null) - { - var folderNames = string.Empty; - if (dt.Level != 1) - { - var folders = new List(); + var dt = new cms.businesslogic.web.DocumentType(documentTypeId); + var folderNames = string.Empty; + if (dt.Level != 1) + { + var folders = new List(); - var current = dt.Parent; - while (current.Level >= 1) - { - if (current.nodeObjectType == Constants.ObjectTypes.DocumentTypeContainerGuid) - folders.Add(HttpUtility.UrlEncode(current.Text)); + var current = dt.Parent; + while (current.Level >= 1) + { + if (current.nodeObjectType == Constants.ObjectTypes.DocumentTypeContainerGuid) + folders.Add(HttpUtility.UrlEncode(current.Text)); - if (current.Level == 1) - break; - current = current.Parent; - } - - folderNames = string.Join("/", folders.ToArray().Reverse()); + if (current.Level == 1) + break; + current = current.Parent; } - Response.AddHeader("Content-Disposition", "attachment;filename=" + dt.Alias + ".udt"); - Response.ContentType = "application/octet-stream"; + folderNames = string.Join("/", folders.ToArray().Reverse()); + } - XmlDocument doc = new XmlDocument(); - doc.AppendChild(dt.ToXml(doc, folderNames)); + Response.AddHeader("Content-Disposition", "attachment;filename=" + dt.Alias + ".udt"); + Response.ContentType = "application/octet-stream"; + + XmlDocument doc = new XmlDocument(); + doc.AppendChild(dt.ToXml(doc, folderNames)); - XmlWriterSettings writerSettings = new XmlWriterSettings(); - writerSettings.Indent = true; + XmlWriterSettings writerSettings = new XmlWriterSettings(); + writerSettings.Indent = true; - XmlWriter xmlWriter = XmlWriter.Create(Response.OutputStream, writerSettings); - doc.Save(xmlWriter); + XmlWriter xmlWriter = XmlWriter.Create(Response.OutputStream, writerSettings); + doc.Save(xmlWriter); - //Response.Write(editDataType.ToXml(new XmlDocument()).OuterXml); - } - } - } + //Response.Write(editDataType.ToXml(new XmlDocument()).OuterXml); + } + } #region Web Form Designer generated code override protected void OnInit(EventArgs e)