diff --git a/src/Umbraco.Core/Services/IContentTypeServiceBase.cs b/src/Umbraco.Core/Services/IContentTypeServiceBase.cs index 01519bc1c3..7902774f4d 100644 --- a/src/Umbraco.Core/Services/IContentTypeServiceBase.cs +++ b/src/Umbraco.Core/Services/IContentTypeServiceBase.cs @@ -78,7 +78,7 @@ namespace Umbraco.Cms.Core.Services /// bool HasContainerInPath(params int[] ids); - Attempt> CreateContainer(int parentContainerId, string name, int userId = Constants.Security.SuperUserId); + Attempt> CreateContainer(int parentContainerId, Guid key, string name, int userId = Constants.Security.SuperUserId); Attempt SaveContainer(EntityContainer container, int userId = Constants.Security.SuperUserId); EntityContainer GetContainer(int containerId); EntityContainer GetContainer(Guid containerId); diff --git a/src/Umbraco.Core/Services/IDataTypeService.cs b/src/Umbraco.Core/Services/IDataTypeService.cs index c7b13c04e1..727851ef02 100644 --- a/src/Umbraco.Core/Services/IDataTypeService.cs +++ b/src/Umbraco.Core/Services/IDataTypeService.cs @@ -17,7 +17,7 @@ namespace Umbraco.Cms.Core.Services /// IReadOnlyDictionary> GetReferences(int id); - Attempt> CreateContainer(int parentId, string name, int userId = Constants.Security.SuperUserId); + Attempt> CreateContainer(int parentId, Guid key, string name, int userId = Constants.Security.SuperUserId); Attempt SaveContainer(EntityContainer container, int userId = Constants.Security.SuperUserId); EntityContainer GetContainer(int containerId); EntityContainer GetContainer(Guid containerId); @@ -68,7 +68,7 @@ namespace Umbraco.Cms.Core.Services /// to save /// Id of the user issuing the save void Save(IEnumerable dataTypeDefinitions, int userId = Constants.Security.SuperUserId); - + /// /// Deletes an /// diff --git a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs index 8f9352ce22..c691b74a0c 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs @@ -546,13 +546,33 @@ namespace Umbraco.Cms.Infrastructure.Packaging { var alias = documentType.Element("Info").Element("Alias").Value; var folders = foldersAttribute.Value.Split(Constants.CharArrays.ForwardSlash); + + var folderKeysAttribute = documentType.Attribute("FolderKeys"); + + var folderKeys = Array.Empty(); + if (folderKeysAttribute != null) + { + folderKeys = folderKeysAttribute.Value.Split(Constants.CharArrays.ForwardSlash).Select(x=>Guid.Parse(x)).ToArray(); + } + var rootFolder = WebUtility.UrlDecode(folders[0]); - //level 1 = root level folders, there can only be one with the same name - var current = _contentTypeService.GetContainers(rootFolder, 1).FirstOrDefault(); + + EntityContainer current; + Guid? rootFolderKey = null; + if (folderKeys.Length == folders.Length && folderKeys.Length > 0) + { + rootFolderKey = folderKeys[0]; + current = _contentTypeService.GetContainer(rootFolderKey.Value); + } + else + { + //level 1 = root level folders, there can only be one with the same name + current = _contentTypeService.GetContainers(rootFolder, 1).FirstOrDefault(); + } if (current == null) { - var tryCreateFolder = _contentTypeService.CreateContainer(-1, rootFolder); + var tryCreateFolder = _contentTypeService.CreateContainer(-1, rootFolderKey ?? Guid.NewGuid(), rootFolder); if (tryCreateFolder == false) { _logger.LogError(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); @@ -567,7 +587,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging for (var i = 1; i < folders.Length; i++) { var folderName = WebUtility.UrlDecode(folders[i]); - current = CreateContentTypeChildFolder(folderName, current); + Guid? folderKey = (folderKeys.Length == folders.Length) ? folderKeys[i] : null; + current = CreateContentTypeChildFolder(folderName, folderKey ?? Guid.NewGuid(), current); importedFolders[alias] = current.Id; } } @@ -576,17 +597,17 @@ namespace Umbraco.Cms.Infrastructure.Packaging return importedFolders; } - private EntityContainer CreateContentTypeChildFolder(string folderName, IUmbracoEntity current) + private EntityContainer CreateContentTypeChildFolder(string folderName, Guid folderKey, IUmbracoEntity current) { var children = _entityService.GetChildren(current.Id).ToArray(); - var found = children.Any(x => x.Name.InvariantEquals(folderName)); + var found = children.Any(x => x.Name.InvariantEquals(folderName) ||x.Key.Equals(folderKey)); if (found) { var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; return _contentTypeService.GetContainer(containerId); } - var tryCreateFolder = _contentTypeService.CreateContainer(current.Id, folderName); + var tryCreateFolder = _contentTypeService.CreateContainer(current.Id, folderKey, folderName); if (tryCreateFolder == false) { _logger.LogError(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); @@ -1057,17 +1078,26 @@ namespace Umbraco.Cms.Infrastructure.Packaging foreach (var datatypeElement in datatypeElements) { var foldersAttribute = datatypeElement.Attribute("Folders"); + if (foldersAttribute != null) { var name = datatypeElement.Attribute("Name").Value; var folders = foldersAttribute.Value.Split(Constants.CharArrays.ForwardSlash); + var folderKeysAttribute = datatypeElement.Attribute("FolderKeys"); + + var folderKeys = Array.Empty(); + if (folderKeysAttribute != null) + { + folderKeys = folderKeysAttribute.Value.Split(Constants.CharArrays.ForwardSlash).Select(x=>Guid.Parse(x)).ToArray(); + } var rootFolder = WebUtility.UrlDecode(folders[0]); + var rootFolderKey = folderKeys.Length > 0 ? folderKeys[0] : Guid.NewGuid(); //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 tryCreateFolder = _dataTypeService.CreateContainer(-1, rootFolder); + var tryCreateFolder = _dataTypeService.CreateContainer(-1, rootFolderKey, rootFolder); if (tryCreateFolder == false) { _logger.LogError(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); @@ -1081,7 +1111,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging for (var i = 1; i < folders.Length; i++) { var folderName = WebUtility.UrlDecode(folders[i]); - current = CreateDataTypeChildFolder(folderName, current); + Guid? folderKey = (folderKeys.Length == folders.Length) ? folderKeys[i] : null; + current = CreateDataTypeChildFolder(folderName, folderKey ?? Guid.NewGuid(), current); importedFolders[name] = current.Id; } } @@ -1090,17 +1121,17 @@ namespace Umbraco.Cms.Infrastructure.Packaging return importedFolders; } - private EntityContainer CreateDataTypeChildFolder(string folderName, IUmbracoEntity current) + private EntityContainer CreateDataTypeChildFolder(string folderName, Guid folderKey, IUmbracoEntity current) { var children = _entityService.GetChildren(current.Id).ToArray(); - var found = children.Any(x => x.Name.InvariantEquals(folderName)); + var found = children.Any(x => x.Name.InvariantEquals(folderName) ||x.Key.Equals(folderKey)); if (found) { var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; return _dataTypeService.GetContainer(containerId); } - var tryCreateFolder = _dataTypeService.CreateContainer(current.Id, folderName); + var tryCreateFolder = _dataTypeService.CreateContainer(current.Id, folderKey,folderName); if (tryCreateFolder == false) { _logger.LogError(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs index fd23db2615..8f0c8ce49b 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs @@ -830,7 +830,7 @@ namespace Umbraco.Cms.Core.Services.Implement protected Guid ContainerObjectType => EntityContainer.GetContainerObjectType(ContainedObjectType); - public Attempt> CreateContainer(int parentId, string name, int userId = Cms.Core.Constants.Security.SuperUserId) + public Attempt> CreateContainer(int parentId, Guid key, string name, int userId = Cms.Core.Constants.Security.SuperUserId) { EventMessages eventMessages = EventMessagesFactory.Get(); using (IScope scope = ScopeProvider.CreateScope()) @@ -843,7 +843,8 @@ namespace Umbraco.Cms.Core.Services.Implement { Name = name, ParentId = parentId, - CreatorId = userId + CreatorId = userId, + Key = key }; var savingNotification = new EntityContainerSavingNotification(container, eventMessages); diff --git a/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs b/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs index 50caca0ec8..d63335ba33 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs @@ -59,7 +59,7 @@ namespace Umbraco.Cms.Core.Services.Implement #region Containers - public Attempt> CreateContainer(int parentId, string name, int userId = Cms.Core.Constants.Security.SuperUserId) + public Attempt> CreateContainer(int parentId, Guid key, string name, int userId = Cms.Core.Constants.Security.SuperUserId) { var evtMsgs = EventMessagesFactory.Get(); using (var scope = ScopeProvider.CreateScope()) @@ -70,7 +70,8 @@ namespace Umbraco.Cms.Core.Services.Implement { Name = name, ParentId = parentId, - CreatorId = userId + CreatorId = userId, + Key = key }; var savingEntityContainerNotification = new EntityContainerSavingNotification(container, evtMsgs); diff --git a/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs b/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs index d2c3189447..06b279cdac 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs @@ -191,18 +191,23 @@ namespace Umbraco.Cms.Core.Services.Implement xml.Add(new XAttribute("Configuration", _configurationEditorJsonSerializer.Serialize(dataType.Configuration))); var folderNames = string.Empty; + var folderKeys = string.Empty; if (dataType.Level != 1) { //get URL encoded folder names var folders = _dataTypeService.GetContainers(dataType) - .OrderBy(x => x.Level) - .Select(x => WebUtility.UrlEncode(x.Name)); + .OrderBy(x => x.Level); - folderNames = string.Join("/", folders.ToArray()); + folderNames = string.Join("/", folders.Select(x => WebUtility.UrlEncode(x.Name)).ToArray()); + folderKeys = string.Join("/", folders.Select(x => x.Key).ToArray()); } if (string.IsNullOrWhiteSpace(folderNames) == false) + { xml.Add(new XAttribute("Folders", folderNames)); + xml.Add(new XAttribute("FolderKeys", folderKeys)); + } + return xml; } @@ -490,19 +495,24 @@ namespace Umbraco.Cms.Core.Services.Implement tabs); var folderNames = string.Empty; + var folderKeys = string.Empty; //don't add folders if this is a child doc type if (contentType.Level != 1 && masterContentType == null) { //get URL encoded folder names var folders = _contentTypeService.GetContainers(contentType) - .OrderBy(x => x.Level) - .Select(x => WebUtility.UrlEncode(x.Name)); + .OrderBy(x => x.Level); - folderNames = string.Join("/", folders.ToArray()); + folderNames = string.Join("/", folders.Select(x => WebUtility.UrlEncode(x.Name)).ToArray()); + folderKeys = string.Join("/", folders.Select(x => x.Key).ToArray()); } if (string.IsNullOrWhiteSpace(folderNames) == false) + { xml.Add(new XAttribute("Folders", folderNames)); + xml.Add(new XAttribute("FolderKeys", folderKeys)); + } + return xml; } diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs index ad9ec91353..b78ac1fdfd 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs @@ -292,7 +292,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IActionResult PostCreateContainer(int parentId, string name) { - var result = _contentTypeService.CreateContainer(parentId, name, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + var result = _contentTypeService.CreateContainer(parentId, Guid.NewGuid(), name, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); if (result.Success) return Ok(result.Result); //return the id diff --git a/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs index d68c3f06f5..19ee05d4e9 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs @@ -260,7 +260,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IActionResult PostCreateContainer(int parentId, string name) { var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser; - var result = _dataTypeService.CreateContainer(parentId, name, currentUser.Id); + var result = _dataTypeService.CreateContainer(parentId, Guid.NewGuid(), name, currentUser.Id); if (result.Success) return Ok(result.Result); //return the id diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs index 3233679ad9..cd1e9037ec 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs @@ -261,7 +261,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public IActionResult PostCreateContainer(int parentId, string name) { - var result = _mediaTypeService.CreateContainer(parentId, name, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + var result = _mediaTypeService.CreateContainer(parentId, Guid.NewGuid(), name, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); if (result.Success) return Ok(result.Result); //return the id