diff --git a/PlotLine/Data/Repositories.cs b/PlotLine/Data/Repositories.cs index 9cb5c50..5746f23 100644 --- a/PlotLine/Data/Repositories.cs +++ b/PlotLine/Data/Repositories.cs @@ -435,7 +435,7 @@ public interface IArchiveRepository Task> ListProjectFiltersForUserAsync(int userId); Task ArchiveAsync(string entityType, int entityId, string? reason); Task RestoreAsync(string entityType, int entityId); - Task DeleteForeverAsync(string entityType, int entityId, string confirmationText); + Task DeleteForeverAsync(string entityType, int entityId, string confirmationText, int userId); } public interface IAcceptanceRepository @@ -1873,12 +1873,12 @@ public sealed class ArchiveRepository(ISqlConnectionFactory connectionFactory) : commandType: CommandType.StoredProcedure); } - public async Task DeleteForeverAsync(string entityType, int entityId, string confirmationText) + public async Task DeleteForeverAsync(string entityType, int entityId, string confirmationText, int userId) { using var connection = connectionFactory.CreateConnection(); return await connection.QuerySingleAsync( "dbo.Archive_DeleteForever", - new { EntityType = entityType, EntityID = entityId, ConfirmationText = confirmationText }, + new { EntityType = entityType, EntityID = entityId, ConfirmationText = confirmationText, UserID = userId }, commandType: CommandType.StoredProcedure); } } diff --git a/PlotLine/Services/CoreServices.cs b/PlotLine/Services/CoreServices.cs index ff10dbb..b7734c6 100644 --- a/PlotLine/Services/CoreServices.cs +++ b/PlotLine/Services/CoreServices.cs @@ -7216,6 +7216,16 @@ public sealed class ArchiveService( public async Task DeleteForeverAsync(string entityType, int entityId, string confirmationText) { + if (!currentUser.UserId.HasValue) + { + return new ArchiveDeleteResult + { + Succeeded = false, + Message = "This archived item could not be found or you do not have permission to delete it." + }; + } + + var userId = currentUser.UserId.Value; var item = await FindArchivedItemContextAsync(entityType, entityId); if (item is null) { @@ -7229,7 +7239,7 @@ public sealed class ArchiveService( var bookCoverPaths = string.Equals(entityType, "Book", StringComparison.OrdinalIgnoreCase) ? await books.GetCoverPathsAsync(entityId) : null; - var result = await archives.DeleteForeverAsync(entityType, entityId, confirmationText); + var result = await archives.DeleteForeverAsync(entityType, entityId, confirmationText, userId); if (result.Succeeded && item?.ProjectID.HasValue == true) { await activity.RecordAsync(item.ProjectID.Value, "Deleted", entityType, entityId, item.DisplayName); diff --git a/PlotLine/Sql/134_Phase20AV_ArchivedBookProjectPermanentDeletion.sql b/PlotLine/Sql/134_Phase20AV_ArchivedBookProjectPermanentDeletion.sql new file mode 100644 index 0000000..66a047f --- /dev/null +++ b/PlotLine/Sql/134_Phase20AV_ArchivedBookProjectPermanentDeletion.sql @@ -0,0 +1,400 @@ +SET ANSI_NULLS ON; +GO +SET QUOTED_IDENTIFIER ON; +GO + +IF EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_StoryIntelligenceBookPipelines_LastRun') + ALTER TABLE dbo.StoryIntelligenceBookPipelines DROP CONSTRAINT FK_StoryIntelligenceBookPipelines_LastRun; +GO + +ALTER TABLE dbo.StoryIntelligenceBookPipelines + ADD CONSTRAINT FK_StoryIntelligenceBookPipelines_LastRun + FOREIGN KEY (LastRunID) REFERENCES dbo.StoryIntelligenceRuns(StoryIntelligenceRunID) + ON DELETE SET NULL; +GO + +IF EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_StoryIntelligenceBookPipelines_Books') + ALTER TABLE dbo.StoryIntelligenceBookPipelines DROP CONSTRAINT FK_StoryIntelligenceBookPipelines_Books; +GO + +ALTER TABLE dbo.StoryIntelligenceBookPipelines + ADD CONSTRAINT FK_StoryIntelligenceBookPipelines_Books + FOREIGN KEY (BookID) REFERENCES dbo.Books(BookID) + ON DELETE CASCADE; +GO + +CREATE OR ALTER PROCEDURE dbo.BookHardDelete_ForOwner + @BookID int, + @UserID int +AS +BEGIN + SET NOCOUNT ON; + SET XACT_ABORT ON; + + DECLARE @ProjectID int; + + SELECT @ProjectID = b.ProjectID + FROM dbo.Books b + INNER JOIN dbo.ProjectUserAccess pua ON pua.ProjectID = b.ProjectID + WHERE b.BookID = @BookID + AND pua.UserID = @UserID + AND pua.AccessRole = N'Owner' + AND pua.IsActive = 1; + + IF @ProjectID IS NULL + THROW 52201, 'Book was not found for this owner.', 1; + + IF EXISTS (SELECT 1 FROM dbo.Books WHERE BookID = @BookID AND IsArchived = 0) + THROW 52202, 'Only archived books can be permanently deleted.', 1; + + BEGIN TRY + BEGIN TRANSACTION; + + DECLARE @SceneIDs TABLE (SceneID int NOT NULL PRIMARY KEY); + DECLARE @ChapterIDs TABLE (ChapterID int NOT NULL PRIMARY KEY); + DECLARE @StoryIntelligenceRunIDs TABLE (StoryIntelligenceRunID int NOT NULL PRIMARY KEY); + DECLARE @StoryIntelligenceChapterResultIDs TABLE (ChapterResultID int NOT NULL PRIMARY KEY); + + INSERT @ChapterIDs (ChapterID) + SELECT ChapterID FROM dbo.Chapters WHERE BookID = @BookID; + + INSERT @SceneIDs (SceneID) + SELECT s.SceneID + FROM dbo.Scenes s + INNER JOIN @ChapterIDs c ON c.ChapterID = s.ChapterID; + + INSERT @StoryIntelligenceRunIDs (StoryIntelligenceRunID) + SELECT DISTINCT r.StoryIntelligenceRunID + FROM dbo.StoryIntelligenceRuns r + WHERE r.BookID = @BookID + OR EXISTS (SELECT 1 FROM @ChapterIDs c WHERE c.ChapterID = r.ChapterID) + OR EXISTS (SELECT 1 FROM @SceneIDs s INNER JOIN dbo.Scenes scene ON scene.SceneID = s.SceneID WHERE scene.ImportRunID = r.StoryIntelligenceRunID); + + INSERT @StoryIntelligenceChapterResultIDs (ChapterResultID) + SELECT DISTINCT cr.ChapterResultID + FROM dbo.StoryIntelligenceChapterResults cr + WHERE cr.BookID = @BookID + OR EXISTS (SELECT 1 FROM @StoryIntelligenceRunIDs r WHERE r.StoryIntelligenceRunID = cr.StoryIntelligenceRunID) + OR EXISTS (SELECT 1 FROM @ChapterIDs c WHERE c.ChapterID = cr.ChapterID); + + DELETE FROM dbo.StoryIntelligenceBookPipelines WHERE BookID = @BookID; + + DELETE sr + FROM dbo.StoryIntelligenceSceneResults sr + WHERE sr.BookID = @BookID + OR EXISTS (SELECT 1 FROM @StoryIntelligenceRunIDs r WHERE r.StoryIntelligenceRunID = sr.StoryIntelligenceRunID) + OR EXISTS (SELECT 1 FROM @StoryIntelligenceChapterResultIDs cr WHERE cr.ChapterResultID = sr.ChapterResultID) + OR EXISTS (SELECT 1 FROM @ChapterIDs c WHERE c.ChapterID = sr.ChapterID) + OR EXISTS (SELECT 1 FROM @SceneIDs s WHERE s.SceneID = sr.SceneID); + + DELETE cr + FROM dbo.StoryIntelligenceChapterResults cr + WHERE cr.BookID = @BookID + OR EXISTS (SELECT 1 FROM @StoryIntelligenceRunIDs r WHERE r.StoryIntelligenceRunID = cr.StoryIntelligenceRunID) + OR EXISTS (SELECT 1 FROM @StoryIntelligenceChapterResultIDs result WHERE result.ChapterResultID = cr.ChapterResultID) + OR EXISTS (SELECT 1 FROM @ChapterIDs c WHERE c.ChapterID = cr.ChapterID); + + DELETE commitRows + FROM dbo.StoryIntelligenceImportCommits commitRows + WHERE commitRows.BookID = @BookID + OR EXISTS (SELECT 1 FROM @StoryIntelligenceRunIDs r WHERE r.StoryIntelligenceRunID = commitRows.StoryIntelligenceRunID) + OR EXISTS (SELECT 1 FROM @ChapterIDs c WHERE c.ChapterID = commitRows.ChapterID); + + UPDATE scene + SET ImportRunID = NULL + FROM dbo.Scenes scene + INNER JOIN @SceneIDs s ON s.SceneID = scene.SceneID + WHERE scene.ImportRunID IS NOT NULL; + + DELETE r + FROM dbo.StoryIntelligenceRuns r + WHERE EXISTS (SELECT 1 FROM @StoryIntelligenceRunIDs runIDs WHERE runIDs.StoryIntelligenceRunID = r.StoryIntelligenceRunID); + + DELETE job + FROM dbo.StoryIntelligenceJobs job + LEFT JOIN dbo.StoryIntelligenceConsents consent ON consent.StoryIntelligenceConsentID = job.StoryIntelligenceConsentID + WHERE job.BookID = @BookID + OR consent.BookID = @BookID; + + DELETE FROM dbo.StoryIntelligenceConsents WHERE BookID = @BookID; + + DELETE petpl + FROM dbo.PlotEventTargetPlotLines petpl + INNER JOIN dbo.ThreadEvents te ON te.ThreadEventID = petpl.PlotEventID + INNER JOIN @SceneIDs scene ON scene.SceneID = te.SceneID; + + DELETE acec + FROM dbo.AssetCustodyEventCharacters acec + INNER JOIN dbo.AssetCustodyEvents ace ON ace.AssetCustodyEventID = acec.AssetCustodyEventID + INNER JOIN @SceneIDs scene ON scene.SceneID = ace.SceneID; + + DELETE cwa FROM dbo.ContinuityWarningAcknowledgements cwa INNER JOIN @SceneIDs scene ON scene.SceneID = cwa.SceneID; + DELETE wcsa FROM dbo.WordCompanionSceneAssets wcsa INNER JOIN @SceneIDs scene ON scene.SceneID = wcsa.SceneID; + DELETE wcsl FROM dbo.WordCompanionSceneLocations wcsl INNER JOIN @SceneIDs scene ON scene.SceneID = wcsl.SceneID; + DELETE scs FROM dbo.SceneCharacterSuggestions scs INNER JOIN @SceneIDs scene ON scene.SceneID = scs.SceneID; + DELETE sas FROM dbo.SceneAssetSuggestions sas INNER JOIN @SceneIDs scene ON scene.SceneID = sas.SceneID; + DELETE sls FROM dbo.SceneLocationSuggestions sls INNER JOIN @SceneIDs scene ON scene.SceneID = sls.SceneID; + DELETE ombsm FROM dbo.OnboardingManuscriptBuildSceneMap ombsm INNER JOIN @SceneIDs scene ON scene.SceneID = ombsm.SceneID; + DELETE ombcm FROM dbo.OnboardingManuscriptBuildChapterMap ombcm INNER JOIN @ChapterIDs c ON c.ChapterID = ombcm.ChapterID; + DELETE sso FROM dbo.ScenarioSceneOrder sso WHERE EXISTS (SELECT 1 FROM @SceneIDs scene WHERE scene.SceneID = sso.SceneID) OR EXISTS (SELECT 1 FROM @ChapterIDs c WHERE c.ChapterID = sso.ProposedChapterID); + DELETE sw FROM dbo.ScenarioWarnings sw INNER JOIN @SceneIDs scene ON scene.SceneID = sw.SceneID; + DELETE wsi FROM dbo.WritingScheduleItems wsi INNER JOIN @SceneIDs scene ON scene.SceneID = wsi.SceneID; + DELETE sa FROM dbo.SceneAttachments sa INNER JOIN @SceneIDs scene ON scene.SceneID = sa.SceneID; + DELETE sci FROM dbo.SceneChecklistItems sci INNER JOIN @SceneIDs scene ON scene.SceneID = sci.SceneID; + DELETE swf FROM dbo.SceneWorkflow swf INNER JOIN @SceneIDs scene ON scene.SceneID = swf.SceneID; + DELETE sn FROM dbo.SceneNotes sn INNER JOIN @SceneIDs scene ON scene.SceneID = sn.SceneID; + DELETE sfpo FROM dbo.SceneFloorPlanOccupancy sfpo INNER JOIN @SceneIDs scene ON scene.SceneID = sfpo.SceneID; + DELETE sd FROM dbo.SceneDependencies sd WHERE EXISTS (SELECT 1 FROM @SceneIDs scene WHERE scene.SceneID = sd.SourceSceneID OR scene.SceneID = sd.TargetSceneID); + DELETE re FROM dbo.RelationshipEvents re INNER JOIN @SceneIDs scene ON scene.SceneID = re.SceneID; + DELETE ck FROM dbo.CharacterKnowledge ck INNER JOIN @SceneIDs scene ON scene.SceneID = ck.SceneID; + DELETE cae FROM dbo.CharacterAttributeEvents cae INNER JOIN @SceneIDs scene ON scene.SceneID = cae.SceneID; + DELETE sc FROM dbo.SceneCharacters sc INNER JOIN @SceneIDs scene ON scene.SceneID = sc.SceneID; + DELETE ace FROM dbo.AssetCustodyEvents ace INNER JOIN @SceneIDs scene ON scene.SceneID = ace.SceneID; + DELETE ae FROM dbo.AssetEvents ae INNER JOIN @SceneIDs scene ON scene.SceneID = ae.SceneID; + DELETE te FROM dbo.ThreadEvents te INNER JOIN @SceneIDs scene ON scene.SceneID = te.SceneID; + DELETE cw FROM dbo.ContinuityWarnings cw WHERE cw.BookID = @BookID OR EXISTS (SELECT 1 FROM @ChapterIDs c WHERE c.ChapterID = cw.ChapterID) OR EXISTS (SELECT 1 FROM @SceneIDs s WHERE s.SceneID = cw.SceneID); + DELETE sal FROM dbo.SceneAssetLocations sal INNER JOIN @SceneIDs scene ON scene.SceneID = sal.SceneID; + DELETE smv FROM dbo.SceneMetricValues smv INNER JOIN @SceneIDs scene ON scene.SceneID = smv.SceneID; + DELETE sp FROM dbo.ScenePurposes sp INNER JOIN @SceneIDs scene ON scene.SceneID = sp.SceneID; + DELETE so FROM dbo.SceneOutcomes so INNER JOIN @SceneIDs scene ON scene.SceneID = so.SceneID; + DELETE cg FROM dbo.ChapterGoals cg INNER JOIN @ChapterIDs c ON c.ChapterID = cg.ChapterID; + + UPDATE ch SET AgeReferenceSceneID = NULL FROM dbo.Characters ch INNER JOIN @SceneIDs scene ON scene.SceneID = ch.AgeReferenceSceneID; + UPDATE cr SET StartSceneID = NULL FROM dbo.CharacterRelationships cr INNER JOIN @SceneIDs scene ON scene.SceneID = cr.StartSceneID; + UPDATE cr SET EndSceneID = NULL FROM dbo.CharacterRelationships cr INNER JOIN @SceneIDs scene ON scene.SceneID = cr.EndSceneID; + UPDATE pt SET IntroducedSceneID = NULL FROM dbo.PlotThreads pt INNER JOIN @SceneIDs scene ON scene.SceneID = pt.IntroducedSceneID; + UPDATE pt SET PlannedResolutionSceneID = NULL FROM dbo.PlotThreads pt INNER JOIN @SceneIDs scene ON scene.SceneID = pt.PlannedResolutionSceneID; + UPDATE pt SET ActualResolutionSceneID = NULL FROM dbo.PlotThreads pt INNER JOIN @SceneIDs scene ON scene.SceneID = pt.ActualResolutionSceneID; + UPDATE cr SET InitialBookID = NULL FROM dbo.CharacterRelationships cr WHERE cr.InitialBookID = @BookID; + UPDATE pl SET BookID = NULL FROM dbo.PlotLines pl WHERE pl.BookID = @BookID; + UPDATE sc SET BookID = NULL FROM dbo.Scenarios sc WHERE sc.BookID = @BookID; + UPDATE tvp SET BookID = NULL FROM dbo.TimelineViewPresets tvp WHERE tvp.BookID = @BookID; + UPDATE vr SET BookID = NULL FROM dbo.ValidationRuns vr WHERE vr.BookID = @BookID; + UPDATE wp SET BookID = NULL FROM dbo.WritingPlans wp WHERE wp.BookID = @BookID; + UPDATE uos SET BookID = NULL FROM dbo.UserOnboardingState uos WHERE uos.BookID = @BookID; + UPDATE s SET ManuscriptDocumentID = NULL FROM dbo.Scenes s INNER JOIN @SceneIDs scene ON scene.SceneID = s.SceneID; + UPDATE c SET ManuscriptDocumentID = NULL FROM dbo.Chapters c INNER JOIN @ChapterIDs chapter ON chapter.ChapterID = c.ChapterID; + + DELETE FROM dbo.OnboardingManuscriptBuilds WHERE BookID = @BookID; + DELETE FROM dbo.ManuscriptDocuments WHERE BookID = @BookID; + DELETE s FROM dbo.Scenes s INNER JOIN @SceneIDs scene ON scene.SceneID = s.SceneID; + DELETE c FROM dbo.Chapters c INNER JOIN @ChapterIDs chapter ON chapter.ChapterID = c.ChapterID; + DELETE FROM dbo.Books WHERE BookID = @BookID AND IsArchived = 1; + + COMMIT TRANSACTION; + END TRY + BEGIN CATCH + IF @@TRANCOUNT > 0 + ROLLBACK TRANSACTION; + + THROW; + END CATCH +END; +GO + +CREATE OR ALTER PROCEDURE dbo.Archive_DeleteForever + @EntityType nvarchar(80), + @EntityID int, + @ConfirmationText nvarchar(20), + @UserID int = NULL +AS +BEGIN + SET NOCOUNT ON; + + IF UPPER(LTRIM(RTRIM(ISNULL(@ConfirmationText, N'')))) <> N'DELETE' + BEGIN + SELECT CAST(0 AS bit) AS Succeeded, N'Type DELETE to permanently delete an archived item.' AS Message; + RETURN; + END; + + IF @EntityType = N'Book' + BEGIN + BEGIN TRY + EXEC dbo.BookHardDelete_ForOwner @BookID = @EntityID, @UserID = @UserID; + SELECT CAST(1 AS bit) AS Succeeded, N'Archived book permanently deleted.' AS Message; + END TRY + BEGIN CATCH + SELECT CAST(0 AS bit) AS Succeeded, CONCAT(N'Archived book could not be permanently deleted: ', ERROR_MESSAGE()) AS Message; + END CATCH; + RETURN; + END; + + IF @EntityType = N'Project' + BEGIN + BEGIN TRY + EXEC dbo.ProjectHardDelete_ForOwner @ProjectID = @EntityID, @UserID = @UserID; + SELECT CAST(1 AS bit) AS Succeeded, N'Archived project permanently deleted.' AS Message; + END TRY + BEGIN CATCH + SELECT CAST(0 AS bit) AS Succeeded, CONCAT(N'Archived project could not be permanently deleted: ', ERROR_MESSAGE()) AS Message; + END CATCH; + RETURN; + END; + + DECLARE @HasLinks bit = 0; + DECLARE @IsArchived bit = 0; + + IF @EntityType = N'Scene' + BEGIN + SELECT @IsArchived = IsArchived FROM dbo.Scenes WHERE SceneID = @EntityID; + IF ISNULL(@IsArchived, 0) = 0 + BEGIN + SELECT CAST(0 AS bit) AS Succeeded, N'Only archived items can be permanently deleted.' AS Message; + RETURN; + END; + + BEGIN TRY + BEGIN TRANSACTION; + + DELETE petpl + FROM dbo.PlotEventTargetPlotLines petpl + INNER JOIN dbo.ThreadEvents te ON te.ThreadEventID = petpl.PlotEventID + WHERE te.SceneID = @EntityID; + + DELETE acec + FROM dbo.AssetCustodyEventCharacters acec + INNER JOIN dbo.AssetCustodyEvents ace ON ace.AssetCustodyEventID = acec.AssetCustodyEventID + WHERE ace.SceneID = @EntityID; + + DELETE FROM dbo.ContinuityWarningAcknowledgements WHERE SceneID = @EntityID; + DELETE FROM dbo.WordCompanionSceneAssets WHERE SceneID = @EntityID; + DELETE FROM dbo.WordCompanionSceneLocations WHERE SceneID = @EntityID; + DELETE FROM dbo.SceneCharacterSuggestions WHERE SceneID = @EntityID; + DELETE FROM dbo.SceneAssetSuggestions WHERE SceneID = @EntityID; + DELETE FROM dbo.SceneLocationSuggestions WHERE SceneID = @EntityID; + DELETE FROM dbo.OnboardingManuscriptBuildSceneMap WHERE SceneID = @EntityID; + DELETE FROM dbo.ScenarioSceneOrder WHERE SceneID = @EntityID; + DELETE FROM dbo.ScenarioWarnings WHERE SceneID = @EntityID; + DELETE FROM dbo.WritingScheduleItems WHERE SceneID = @EntityID; + DELETE FROM dbo.SceneAttachments WHERE SceneID = @EntityID; + DELETE FROM dbo.SceneChecklistItems WHERE SceneID = @EntityID; + DELETE FROM dbo.SceneWorkflow WHERE SceneID = @EntityID; + DELETE FROM dbo.SceneNotes WHERE SceneID = @EntityID; + DELETE FROM dbo.SceneFloorPlanOccupancy WHERE SceneID = @EntityID; + DELETE FROM dbo.SceneDependencies WHERE SourceSceneID = @EntityID OR TargetSceneID = @EntityID; + DELETE FROM dbo.RelationshipEvents WHERE SceneID = @EntityID; + DELETE FROM dbo.CharacterKnowledge WHERE SceneID = @EntityID; + DELETE FROM dbo.CharacterAttributeEvents WHERE SceneID = @EntityID; + DELETE FROM dbo.SceneCharacters WHERE SceneID = @EntityID; + DELETE FROM dbo.AssetCustodyEvents WHERE SceneID = @EntityID; + DELETE FROM dbo.AssetEvents WHERE SceneID = @EntityID; + DELETE FROM dbo.ThreadEvents WHERE SceneID = @EntityID; + DELETE FROM dbo.ContinuityWarnings WHERE SceneID = @EntityID; + DELETE FROM dbo.SceneAssetLocations WHERE SceneID = @EntityID; + DELETE FROM dbo.SceneMetricValues WHERE SceneID = @EntityID; + DELETE FROM dbo.ScenePurposes WHERE SceneID = @EntityID; + DELETE FROM dbo.SceneOutcomes WHERE SceneID = @EntityID; + + UPDATE dbo.Characters SET AgeReferenceSceneID = NULL WHERE AgeReferenceSceneID = @EntityID; + UPDATE dbo.CharacterRelationships SET StartSceneID = NULL WHERE StartSceneID = @EntityID; + UPDATE dbo.CharacterRelationships SET EndSceneID = NULL WHERE EndSceneID = @EntityID; + UPDATE dbo.PlotThreads SET IntroducedSceneID = NULL WHERE IntroducedSceneID = @EntityID; + UPDATE dbo.PlotThreads SET PlannedResolutionSceneID = NULL WHERE PlannedResolutionSceneID = @EntityID; + UPDATE dbo.PlotThreads SET ActualResolutionSceneID = NULL WHERE ActualResolutionSceneID = @EntityID; + + DELETE FROM dbo.Scenes WHERE SceneID = @EntityID AND IsArchived = 1; + + COMMIT TRANSACTION; + SELECT CAST(1 AS bit) AS Succeeded, N'Archived scene permanently deleted.' AS Message; + RETURN; + END TRY + BEGIN CATCH + IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; + SELECT CAST(0 AS bit) AS Succeeded, CONCAT(N'Archived scene could not be permanently deleted: ', ERROR_MESSAGE()) AS Message; + RETURN; + END CATCH; + END; + + IF @EntityType = N'Chapter' + BEGIN + SELECT @IsArchived = IsArchived FROM dbo.Chapters WHERE ChapterID = @EntityID; + IF EXISTS (SELECT 1 FROM dbo.Scenes WHERE ChapterID = @EntityID) SET @HasLinks = 1; + IF @IsArchived = 1 AND @HasLinks = 0 DELETE FROM dbo.Chapters WHERE ChapterID = @EntityID; + END + ELSE IF @EntityType = N'PlotLine' + BEGIN + SELECT @IsArchived = IsArchived FROM dbo.PlotLines WHERE PlotLineID = @EntityID; + IF EXISTS (SELECT 1 FROM dbo.PlotThreads WHERE PlotLineID = @EntityID) SET @HasLinks = 1; + IF @IsArchived = 1 AND @HasLinks = 0 DELETE FROM dbo.PlotLines WHERE PlotLineID = @EntityID; + END + ELSE IF @EntityType = N'PlotThread' + BEGIN + SELECT @IsArchived = IsArchived FROM dbo.PlotThreads WHERE PlotThreadID = @EntityID; + IF EXISTS ( + SELECT 1 FROM dbo.ThreadEvents WHERE PlotThreadID = @EntityID + UNION ALL SELECT 1 FROM dbo.CharacterKnowledge WHERE PlotThreadID = @EntityID + ) SET @HasLinks = 1; + IF @IsArchived = 1 AND @HasLinks = 0 DELETE FROM dbo.PlotThreads WHERE PlotThreadID = @EntityID; + END + ELSE IF @EntityType = N'StoryAsset' + BEGIN + SELECT @IsArchived = IsArchived FROM dbo.StoryAssets WHERE StoryAssetID = @EntityID; + IF EXISTS ( + SELECT 1 FROM dbo.AssetEvents WHERE StoryAssetID = @EntityID + UNION ALL SELECT 1 FROM dbo.AssetDependencies WHERE SourceAssetID = @EntityID OR TargetAssetID = @EntityID + UNION ALL SELECT 1 FROM dbo.AssetCustodyEvents WHERE StoryAssetID = @EntityID + UNION ALL SELECT 1 FROM dbo.CharacterKnowledge WHERE StoryAssetID = @EntityID + UNION ALL SELECT 1 FROM dbo.SceneAssetLocations WHERE StoryAssetID = @EntityID + ) SET @HasLinks = 1; + IF @IsArchived = 1 AND @HasLinks = 0 DELETE FROM dbo.StoryAssets WHERE StoryAssetID = @EntityID; + END + ELSE IF @EntityType = N'Character' + BEGIN + SELECT @IsArchived = IsArchived FROM dbo.Characters WHERE CharacterID = @EntityID; + IF EXISTS ( + SELECT 1 FROM dbo.SceneCharacters WHERE CharacterID = @EntityID + UNION ALL SELECT 1 FROM dbo.CharacterAttributeEvents WHERE CharacterID = @EntityID + UNION ALL SELECT 1 FROM dbo.CharacterKnowledge WHERE CharacterID = @EntityID + UNION ALL SELECT 1 FROM dbo.CharacterRelationships WHERE CharacterAID = @EntityID OR CharacterBID = @EntityID + ) SET @HasLinks = 1; + IF @IsArchived = 1 AND @HasLinks = 0 DELETE FROM dbo.Characters WHERE CharacterID = @EntityID; + END + ELSE IF @EntityType = N'Location' + BEGIN + SELECT @IsArchived = IsArchived FROM dbo.Locations WHERE LocationID = @EntityID; + IF EXISTS ( + SELECT 1 FROM dbo.Locations WHERE ParentLocationID = @EntityID + UNION ALL SELECT 1 FROM dbo.Scenes WHERE PrimaryLocationID = @EntityID + UNION ALL SELECT 1 FROM dbo.SceneCharacters WHERE LocationID = @EntityID OR EntryLocationID = @EntityID OR ExitLocationID = @EntityID + UNION ALL SELECT 1 FROM dbo.StoryAssets WHERE CurrentLocationID = @EntityID + UNION ALL SELECT 1 FROM dbo.SceneAssetLocations WHERE LocationID = @EntityID + UNION ALL SELECT 1 FROM dbo.LocationRelationships WHERE FromLocationID = @EntityID OR ToLocationID = @EntityID + ) SET @HasLinks = 1; + IF @IsArchived = 1 AND @HasLinks = 0 DELETE FROM dbo.Locations WHERE LocationID = @EntityID; + END + ELSE IF @EntityType = N'Scenario' + BEGIN + SELECT @IsArchived = IsArchived FROM dbo.Scenarios WHERE ScenarioID = @EntityID; + IF EXISTS ( + SELECT 1 FROM dbo.ScenarioSceneOrder WHERE ScenarioID = @EntityID + UNION ALL SELECT 1 FROM dbo.ScenarioWarnings WHERE ScenarioID = @EntityID + ) SET @HasLinks = 1; + IF @IsArchived = 1 AND @HasLinks = 0 DELETE FROM dbo.Scenarios WHERE ScenarioID = @EntityID; + END + ELSE IF @EntityType = N'Relationship' + BEGIN + SELECT @IsArchived = IsArchived FROM dbo.CharacterRelationships WHERE CharacterRelationshipID = @EntityID; + IF EXISTS (SELECT 1 FROM dbo.RelationshipEvents WHERE CharacterRelationshipID = @EntityID) SET @HasLinks = 1; + IF @IsArchived = 1 AND @HasLinks = 0 DELETE FROM dbo.CharacterRelationships WHERE CharacterRelationshipID = @EntityID; + END + ELSE + BEGIN + SELECT CAST(0 AS bit) AS Succeeded, N'This archived item type is not supported for permanent delete.' AS Message; + RETURN; + END; + + IF ISNULL(@IsArchived, 0) = 0 + BEGIN + SELECT CAST(0 AS bit) AS Succeeded, N'Only archived items can be permanently deleted.' AS Message; + RETURN; + END; + + IF @HasLinks = 1 + BEGIN + SELECT CAST(0 AS bit) AS Succeeded, N'This item has linked story data and cannot yet be permanently deleted safely. Leave it archived instead.' AS Message; + RETURN; + END; + + SELECT CAST(1 AS bit) AS Succeeded, N'Archived item permanently deleted.' AS Message; +END; +GO