From 3263e496aa3d0ec0ecac0e9c9971ed0321071b80 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Aug 2017 00:02:27 +1000 Subject: [PATCH] U4-10201 Exception when deleting a user's start node - adds FKs, fixes delete scripts, adds migrations for FKs --- src/Umbraco.Core/Models/Rdbms/UserGroupDto.cs | 2 + .../AddUserGroupTables.cs | 39 +++++++++++++++++++ .../Repositories/ContentRepository.cs | 4 +- .../Repositories/MediaRepository.cs | 2 + .../Repositories/RecycleBinRepository.cs | 20 +++++++++- .../Services/ContentServiceTests.cs | 9 +++++ 6 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Models/Rdbms/UserGroupDto.cs b/src/Umbraco.Core/Models/Rdbms/UserGroupDto.cs index fdad78bfcd..f6bb09066d 100644 --- a/src/Umbraco.Core/Models/Rdbms/UserGroupDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/UserGroupDto.cs @@ -51,10 +51,12 @@ namespace Umbraco.Core.Models.Rdbms [Column("startContentId")] [NullSetting(NullSetting = NullSettings.Null)] + [ForeignKey(typeof(NodeDto), Name = "FK_startContentId_umbracoNode_id")] public int? StartContentId { get; set; } [Column("startMediaId")] [NullSetting(NullSetting = NullSettings.Null)] + [ForeignKey(typeof(NodeDto), Name = "FK_startMediaId_umbracoNode_id")] public int? StartMediaId { get; set; } [ResultColumn] diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSevenZero/AddUserGroupTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSevenZero/AddUserGroupTables.cs index d1cf0a40d9..36c45615a3 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSevenZero/AddUserGroupTables.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSevenZero/AddUserGroupTables.cs @@ -28,6 +28,45 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZe DeleteOldTables(tables, constraints); SetDefaultIcons(); } + else + { + //if we aren't adding the tables, make sure that the umbracoUserGroup table has the correct FKs - these + //were added after the beta release so we need to do some cleanup + //if the FK doesn't exist + if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUserGroup") + && x.Item2.InvariantEquals("startContentId") + && x.Item3.InvariantEquals("FK_startContentId_umbracoNode_id")) == false) + { + //before we add any foreign key we need to make sure there's no stale data in there which would have happened in the beta + //release if a start node was assigned and then that start node was deleted. + Execute.Sql(@"UPDATE umbracoUserGroup SET startContentId = NULL WHERE startContentId NOT IN (SELECT id FROM umbracoNode)"); + + Create.ForeignKey("FK_startContentId_umbracoNode_id") + .FromTable("umbracoUserGroup") + .ForeignColumn("startContentId") + .ToTable("umbracoNode") + .PrimaryColumn("id") + .OnDelete(Rule.None) + .OnUpdate(Rule.None); + } + + if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUserGroup") + && x.Item2.InvariantEquals("startMediaId") + && x.Item3.InvariantEquals("FK_startMediaId_umbracoNode_id")) == false) + { + //before we add any foreign key we need to make sure there's no stale data in there which would have happened in the beta + //release if a start node was assigned and then that start node was deleted. + Execute.Sql(@"UPDATE umbracoUserGroup SET startMediaId = NULL WHERE startMediaId NOT IN (SELECT id FROM umbracoNode)"); + + Create.ForeignKey("FK_startMediaId_umbracoNode_id") + .FromTable("umbracoUserGroup") + .ForeignColumn("startMediaId") + .ToTable("umbracoNode") + .PrimaryColumn("id") + .OnDelete(Rule.None) + .OnUpdate(Rule.None); + } + } } private void SetDefaultIcons() diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index addd0bc925..daa8140670 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -183,6 +183,8 @@ namespace Umbraco.Core.Persistence.Repositories "DELETE FROM cmsTask WHERE nodeId = @Id", "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id", "DELETE FROM umbracoUserGroup2NodePermission WHERE nodeId = @Id", + "DELETE FROM umbracoUserStartNode WHERE startNode = @Id", + "UPDATE umbracoUserGroup SET startContentId = NULL WHERE startContentId = @Id", "DELETE FROM umbracoRelation WHERE parentId = @Id", "DELETE FROM umbracoRelation WHERE childId = @Id", "DELETE FROM cmsTagRelationship WHERE nodeId = @Id", @@ -194,7 +196,7 @@ namespace Umbraco.Core.Persistence.Repositories "DELETE FROM cmsContentXml WHERE nodeId = @Id", "DELETE FROM cmsContent WHERE nodeId = @Id", "DELETE FROM umbracoAccess WHERE nodeId = @Id", - "DELETE FROM umbracoNode WHERE id = @Id" + "DELETE FROM umbracoNode WHERE id = @Id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index ee413c2a59..be96d80f5a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -118,6 +118,8 @@ namespace Umbraco.Core.Persistence.Repositories "DELETE FROM cmsTask WHERE nodeId = @Id", "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id", "DELETE FROM umbracoUserGroup2NodePermission WHERE nodeId = @Id", + "DELETE FROM umbracoUserStartNode WHERE startNode = @Id", + "UPDATE umbracoUserGroup SET startMediaId = NULL WHERE startMediaId = @Id", "DELETE FROM umbracoRelation WHERE parentId = @Id", "DELETE FROM umbracoRelation WHERE childId = @Id", "DELETE FROM cmsTagRelationship WHERE nodeId = @Id", diff --git a/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs index efb7487fdf..efb93bcff3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs @@ -53,7 +53,10 @@ namespace Umbraco.Core.Persistence.Repositories @"DELETE FROM umbracoRedirectUrl WHERE umbracoRedirectUrl.id IN( SELECT TB1.id FROM umbracoRedirectUrl as TB1 INNER JOIN umbracoNode as TB2 ON TB1.contentKey = TB2.uniqueId - WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)", + WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)", + FormatDeleteStatement("umbracoUserStartNode", "startNode"), + FormatUpdateStatement("umbracoUserGroup", "startContentId"), + FormatUpdateStatement("umbracoUserGroup", "startMediaId"), FormatDeleteStatement("umbracoRelation", "parentId"), FormatDeleteStatement("umbracoRelation", "childId"), FormatDeleteStatement("cmsTagRelationship", "nodeId"), @@ -108,6 +111,21 @@ namespace Umbraco.Core.Persistence.Repositories tableName, keyName); } + /// + /// An update statement that will update a value to NULL in the table specified where its PK (keyName) is found in the + /// list of umbracoNode.id that have trashed flag set + /// + /// + /// + /// + private string FormatUpdateStatement(string tableName, string keyName) + { + return + string.Format( + "UPDATE {0} SET {0}.{1} = NULL WHERE {0}.{1} IN (SELECT id FROM umbracoNode WHERE trashed = '1' AND nodeObjectType = @NodeObjectType)", + tableName, keyName); + } + /// /// Gets a list of files, which are referenced on items in the Recycle Bin. /// The list is generated by the convention that a file is referenced by diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 5a2b9f23e7..c9daeb0022 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1583,6 +1583,14 @@ namespace Umbraco.Tests.Services ServiceContext.ContentService.Save(content2, 0); Assert.IsTrue(ServiceContext.ContentService.PublishWithStatus(content2, 0).Success); + var editorGroup = ServiceContext.UserService.GetUserGroupByAlias("editor"); + editorGroup.StartContentId = content1.Id; + ServiceContext.UserService.Save(editorGroup); + + var admin = ServiceContext.UserService.GetUserById(0); + admin.StartContentIds = new[] {content1.Id}; + ServiceContext.UserService.Save(admin); + ServiceContext.RelationService.Save(new RelationType(Constants.ObjectTypes.DocumentGuid, Constants.ObjectTypes.DocumentGuid, "test")); Assert.IsNotNull(ServiceContext.RelationService.Relate(content1, content2, "test")); @@ -1608,6 +1616,7 @@ namespace Umbraco.Tests.Services }).Success); // Act + ServiceContext.ContentService.MoveToRecycleBin(content1); ServiceContext.ContentService.EmptyRecycleBin(); var contents = ServiceContext.ContentService.GetContentInRecycleBin();