From 5e9003156d5883c0c6e8afeafc57ef7091069eff Mon Sep 17 00:00:00 2001
From: Georg von Kries <gvk@creativbox.net>
Date: Mon, 17 Jun 2024 19:07:34 +0200
Subject: [PATCH] Adds an overload to RegisterBeforeDispose() (#16322)

Co-authored-by: Hisham Bin Ateya <hishamco_2007@yahoo.com>
---
 .../Shell/Scope/ShellScope.cs                 | 19 +++++++++++++++++--
 .../Shell/Scope/ShellScopeExtensions.cs       | 19 +++++++++++++++----
 2 files changed, 32 insertions(+), 6 deletions(-)

diff --git a/src/OrchardCore/OrchardCore.Abstractions/Shell/Scope/ShellScope.cs b/src/OrchardCore/OrchardCore.Abstractions/Shell/Scope/ShellScope.cs
index cf2889fd8a7..1455015082a 100644
--- a/src/OrchardCore/OrchardCore.Abstractions/Shell/Scope/ShellScope.cs
+++ b/src/OrchardCore/OrchardCore.Abstractions/Shell/Scope/ShellScope.cs
@@ -376,7 +376,22 @@ internal async Task ActivateShellInternalAsync()
         /// <summary>
         /// Registers a delegate to be invoked when 'BeforeDisposeAsync()' is called on this scope.
         /// </summary>
-        internal void BeforeDispose(Func<ShellScope, Task> callback) => (_beforeDispose ??= []).Insert(0, callback);
+        /// <param name="callback">The delegate to be invoked before disposal. This delegate takes a <see cref="ShellScope"/> parameter and returns a <see cref="Task"/>.</param>
+        /// <param name="last">A boolean value indicating whether the delegate should be invoked last. 
+        /// If true, the delegate is added to the end of the invocation list; otherwise, it is added to the beginning.</param>
+        internal void BeforeDispose(Func<ShellScope, Task> callback, bool last)
+        {
+            var list = _beforeDispose ??= [];
+
+            if (last)
+            {
+                list.Add(callback);
+            }
+            else
+            {
+                list.Insert(0, callback);
+            }
+        }
 
         /// <summary>
         /// Adds a Signal (if not already present) to be sent just after 'BeforeDisposeAsync()'.
@@ -396,7 +411,7 @@ internal async Task ActivateShellInternalAsync()
         /// <summary>
         /// Registers a delegate to be invoked before the current shell scope will be disposed.
         /// </summary>
-        public static void RegisterBeforeDispose(Func<ShellScope, Task> callback) => Current?.BeforeDispose(callback);
+        public static void RegisterBeforeDispose(Func<ShellScope, Task> callback, bool last = false) => Current?.BeforeDispose(callback, last);
 
         /// <summary>
         /// Adds a Signal (if not already present) to be sent just before the current shell scope will be disposed.
diff --git a/src/OrchardCore/OrchardCore.Abstractions/Shell/Scope/ShellScopeExtensions.cs b/src/OrchardCore/OrchardCore.Abstractions/Shell/Scope/ShellScopeExtensions.cs
index 4c91df15f42..a2625ebb192 100644
--- a/src/OrchardCore/OrchardCore.Abstractions/Shell/Scope/ShellScopeExtensions.cs
+++ b/src/OrchardCore/OrchardCore.Abstractions/Shell/Scope/ShellScopeExtensions.cs
@@ -8,22 +8,33 @@ public static class ShellScopeExtensions
         /// <summary>
         /// Registers a delegate task to be invoked before this shell scope will be disposed.
         /// </summary>
-        public static ShellScope RegisterBeforeDispose(this ShellScope scope, Func<ShellScope, Task> callback)
+        /// <param name="scope">The <see cref="ShellScope"/> instance on which to register the delegate.</param>
+        /// <param name="callback">The delegate task to be invoked before disposal. This delegate takes a <see cref="ShellScope"/> parameter and returns a <see cref="Task"/>.</param>
+        /// <param name="last">A boolean value indicating whether the delegate should be invoked last. 
+        /// If true, the delegate is added to the end of the invocation list; otherwise, it is added to the beginning. The default value is false.</param>
+        /// <returns>The <see cref="ShellScope"/> instance for chaining further calls.</returns>
+        public static ShellScope RegisterBeforeDispose(this ShellScope scope, Func<ShellScope, Task> callback, bool last = false)
         {
-            scope?.BeforeDispose(callback);
+            scope?.BeforeDispose(callback, last);
+
             return scope;
         }
 
         /// <summary>
         /// Registers a delegate action to be invoked before this shell scope will be disposed.
         /// </summary>
-        public static ShellScope RegisterBeforeDispose(this ShellScope scope, Action<ShellScope> callback)
+        /// <param name="scope">The <see cref="ShellScope"/> instance on which to register the delegate.</param>
+        /// <param name="callback">The delegate action to be invoked before disposal. This delegate takes a <see cref="ShellScope"/> parameter.</param>
+        /// <param name="last">A boolean value indicating whether the delegate should be invoked last. 
+        /// If true, the delegate is added to the end of the invocation list; otherwise, it is added to the beginning. The default value is false.</param>
+        /// <returns>The <see cref="ShellScope"/> instance for chaining further calls.</returns>
+        public static ShellScope RegisterBeforeDispose(this ShellScope scope, Action<ShellScope> callback, bool last = false)
         {
             scope?.BeforeDispose(scope =>
             {
                 callback(scope);
                 return Task.CompletedTask;
-            });
+            }, last);
 
             return scope;
         }