From 54d2f4698aa09dca8430e76de97fbe6232fb1fdc Mon Sep 17 00:00:00 2001 From: "Antti K. Koskela" Date: Sun, 16 Oct 2022 20:49:49 +0300 Subject: [PATCH 1/4] Implement paging for recycle bin items --- src/Commands/RecycleBin/GetRecycleBinItem.cs | 45 ++++--------- src/Commands/Utilities/RecycleBinUtility.cs | 71 ++++++++++++++++++++ 2 files changed, 85 insertions(+), 31 deletions(-) create mode 100644 src/Commands/Utilities/RecycleBinUtility.cs diff --git a/src/Commands/RecycleBin/GetRecycleBinItem.cs b/src/Commands/RecycleBin/GetRecycleBinItem.cs index 8b0b4361a..405487e02 100644 --- a/src/Commands/RecycleBin/GetRecycleBinItem.cs +++ b/src/Commands/RecycleBin/GetRecycleBinItem.cs @@ -3,7 +3,11 @@ using System.Linq; using System.Linq.Expressions; using System.Management.Automation; +using System.Net; +using System.Web; +using Microsoft.AspNetCore.Http.Extensions; using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Utilities; using PnP.PowerShell.Commands.Base.PipeBinds; @@ -44,7 +48,6 @@ protected override void ExecuteCmdlet() } else { - if (ParameterSpecified(nameof(RowLimit))) { RecycleBinItemState recycleBinStage; @@ -61,51 +64,31 @@ protected override void ExecuteCmdlet() break; } - if (FirstStage.IsPresent || SecondStage.IsPresent) - { - RecycleBinItemCollection items = ClientContext.Site.GetRecycleBinItems(null, RowLimit, false, RecycleBinOrderBy.DeletedDate, recycleBinStage); - ClientContext.Load(items); - ClientContext.ExecuteQueryRetry(); - - List recycleBinItemList = items.ToList(); - WriteObject(recycleBinItemList, true); - } - else - { - ClientContext.Site.Context.Load(ClientContext.Site.RecycleBin, r => r.IncludeWithDefaultProperties(RetrievalExpressions)); - ClientContext.Site.Context.ExecuteQueryRetry(); - - List recycleBinItemList = ClientContext.Site.RecycleBin.ToList(); - recycleBinItemList = recycleBinItemList.Take(RowLimit).ToList(); - WriteObject(recycleBinItemList, true); - } - - + List recycleBinItemList = RecycleBinUtility.GetRecycleBinItems(ClientContext, RowLimit, recycleBinStage); + WriteObject(recycleBinItemList, true); } else { - ClientContext.Site.Context.Load(ClientContext.Site.RecycleBin, r => r.IncludeWithDefaultProperties(RetrievalExpressions)); - ClientContext.Site.Context.ExecuteQueryRetry(); - - List recycleBinItemList = ClientContext.Site.RecycleBin.ToList(); - + List recycleBinItemList; switch (ParameterSetName) { case ParameterSet_FIRSTSTAGE: - WriteObject( - recycleBinItemList.Where(i => i.ItemState == RecycleBinItemState.FirstStageRecycleBin), true); + recycleBinItemList = RecycleBinUtility.GetRecycleBinItems(ClientContext, RowLimit, RecycleBinItemState.FirstStageRecycleBin); + WriteObject(recycleBinItemList, true); break; case ParameterSet_SECONDSTAGE: - WriteObject( - recycleBinItemList.Where(i => i.ItemState == RecycleBinItemState.SecondStageRecycleBin), - true); + recycleBinItemList = RecycleBinUtility.GetRecycleBinItems(ClientContext, RowLimit, RecycleBinItemState.SecondStageRecycleBin); + WriteObject(recycleBinItemList, true); break; default: + recycleBinItemList = RecycleBinUtility.GetRecycleBinItems(ClientContext, RowLimit); WriteObject(recycleBinItemList, true); break; } } } } + + } } diff --git a/src/Commands/Utilities/RecycleBinUtility.cs b/src/Commands/Utilities/RecycleBinUtility.cs new file mode 100644 index 000000000..480ca706b --- /dev/null +++ b/src/Commands/Utilities/RecycleBinUtility.cs @@ -0,0 +1,71 @@ +using Microsoft.SharePoint.Client; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; + +namespace PnP.PowerShell.Commands.Utilities +{ + internal static class RecycleBinUtility + { + internal static List GetRecycleBinItems(ClientContext ctx, int? rowLimit = null, RecycleBinItemState? recycleBinStage = null) + { + var recycleBinItems = new List(); + string pagingInfo = null; + RecycleBinItemCollection items; + ctx.Load(ctx.Site.RecycleBin); + ctx.ExecuteQueryRetry(); + var totalRecyclebinContentsCount = ctx.Site.RecycleBin.Count; + + do + { + // We don't actually know what the List View Threshold for the Recycle Bin is, so we'll use the safe number (5000) and implement paging. + int iterationRowLimit; + if (rowLimit.HasValue && rowLimit.Value >= 5000) + { + // Subtract this page's count from the rowLimit (we don't want duplicates or go out of bounds) + if (rowLimit.HasValue) rowLimit -= 5000; + + iterationRowLimit = 5000; + } + else if (rowLimit.HasValue && rowLimit.Value > 0 && rowLimit.Value < 5000) + { + iterationRowLimit = rowLimit.Value; + } + else // rowLimit was not set, just fetch a "whole page" + { + iterationRowLimit = 5000; + } + + if (recycleBinStage.HasValue) + { + items = ctx.Site.GetRecycleBinItems(pagingInfo, iterationRowLimit, false, RecycleBinOrderBy.DefaultOrderBy, recycleBinStage.Value); + ctx.Load(items); + ctx.ExecuteQueryRetry(); + recycleBinItems.AddRange(items.ToList()); + } + else + { + items = ctx.Site.GetRecycleBinItems(pagingInfo, iterationRowLimit, false, RecycleBinOrderBy.DefaultOrderBy, RecycleBinItemState.None); + ctx.Load(items); + ctx.ExecuteQueryRetry(); + recycleBinItems.AddRange(items.ToList()); + } + + // Paging magic (if needed) + if (items.Count > 0) + { + var nextId = items.Last().Id; + //var nextTitle = items.Last().Title; + var nextTitle = WebUtility.UrlEncode(items.Last().Title); + var deletionTime = items.Last().DeletedDate; + pagingInfo = $"id={nextId}&title={nextTitle}"; // &searchValue=${deletionTime} + } + } + while (items?.Count == 5000); // if items had 5000 items, there might be more since that's the page size we're using + + return recycleBinItems; + } + } +} \ No newline at end of file From f7f39bdc0f545e82b15eb789c7b74b22eb6dec6b Mon Sep 17 00:00:00 2001 From: "Antti K. Koskela" Date: Sun, 16 Oct 2022 20:54:26 +0300 Subject: [PATCH 2/4] Disable useful debugging code --- src/Commands/Utilities/RecycleBinUtility.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Commands/Utilities/RecycleBinUtility.cs b/src/Commands/Utilities/RecycleBinUtility.cs index 480ca706b..ba01727b6 100644 --- a/src/Commands/Utilities/RecycleBinUtility.cs +++ b/src/Commands/Utilities/RecycleBinUtility.cs @@ -14,9 +14,11 @@ internal static List GetRecycleBinItems(ClientContext ctx, int? var recycleBinItems = new List(); string pagingInfo = null; RecycleBinItemCollection items; - ctx.Load(ctx.Site.RecycleBin); - ctx.ExecuteQueryRetry(); - var totalRecyclebinContentsCount = ctx.Site.RecycleBin.Count; + + // This part is only here to make debugging easier if you ever run into issues with this code :) + //ctx.Load(ctx.Site.RecycleBin); + //ctx.ExecuteQueryRetry(); + //var totalRecyclebinContentsCount = ctx.Site.RecycleBin.Count; do { @@ -54,12 +56,14 @@ internal static List GetRecycleBinItems(ClientContext ctx, int? } // Paging magic (if needed) + // Based on this work our good friends at Portiva did ❤ + // https://www.portiva.nl/portiblog/blogs-cat/paging-through-sharepoint-recycle-bin if (items.Count > 0) { var nextId = items.Last().Id; //var nextTitle = items.Last().Title; var nextTitle = WebUtility.UrlEncode(items.Last().Title); - var deletionTime = items.Last().DeletedDate; + //var deletionTime = items.Last().DeletedDate; pagingInfo = $"id={nextId}&title={nextTitle}"; // &searchValue=${deletionTime} } } From 1662aa0296ec12af0c7eb849b43d6e3666f985bd Mon Sep 17 00:00:00 2001 From: "Antti K. Koskela" Date: Sun, 16 Oct 2022 21:15:33 +0300 Subject: [PATCH 3/4] Remove unnecessary using statements (should fix the build, too) --- src/Commands/RecycleBin/GetRecycleBinItem.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Commands/RecycleBin/GetRecycleBinItem.cs b/src/Commands/RecycleBin/GetRecycleBinItem.cs index 405487e02..877441023 100644 --- a/src/Commands/RecycleBin/GetRecycleBinItem.cs +++ b/src/Commands/RecycleBin/GetRecycleBinItem.cs @@ -1,15 +1,9 @@ -using System; +using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Utilities; +using System; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using System.Management.Automation; -using System.Net; -using System.Web; -using Microsoft.AspNetCore.Http.Extensions; -using Microsoft.SharePoint.Client; -using PnP.PowerShell.Commands.Utilities; - -using PnP.PowerShell.Commands.Base.PipeBinds; namespace PnP.PowerShell.Commands.RecycleBin { From 7d2b66a8dabbffe79f2b7fb8d30983aea9d868ad Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 17 Oct 2022 21:53:22 +0300 Subject: [PATCH 4/4] Update RecycleBinUtility.cs Improved conditional check --- src/Commands/Utilities/RecycleBinUtility.cs | 26 +++++++-------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/Commands/Utilities/RecycleBinUtility.cs b/src/Commands/Utilities/RecycleBinUtility.cs index ba01727b6..0b5587ef0 100644 --- a/src/Commands/Utilities/RecycleBinUtility.cs +++ b/src/Commands/Utilities/RecycleBinUtility.cs @@ -1,4 +1,4 @@ -using Microsoft.SharePoint.Client; +using Microsoft.SharePoint.Client; using System; using System.Collections.Generic; using System.Linq; @@ -9,7 +9,7 @@ namespace PnP.PowerShell.Commands.Utilities { internal static class RecycleBinUtility { - internal static List GetRecycleBinItems(ClientContext ctx, int? rowLimit = null, RecycleBinItemState? recycleBinStage = null) + internal static List GetRecycleBinItems(ClientContext ctx, int? rowLimit = null, RecycleBinItemState recycleBinStage = RecycleBinItemState.None) { var recycleBinItems = new List(); string pagingInfo = null; @@ -39,21 +39,11 @@ internal static List GetRecycleBinItems(ClientContext ctx, int? { iterationRowLimit = 5000; } - - if (recycleBinStage.HasValue) - { - items = ctx.Site.GetRecycleBinItems(pagingInfo, iterationRowLimit, false, RecycleBinOrderBy.DefaultOrderBy, recycleBinStage.Value); - ctx.Load(items); - ctx.ExecuteQueryRetry(); - recycleBinItems.AddRange(items.ToList()); - } - else - { - items = ctx.Site.GetRecycleBinItems(pagingInfo, iterationRowLimit, false, RecycleBinOrderBy.DefaultOrderBy, RecycleBinItemState.None); - ctx.Load(items); - ctx.ExecuteQueryRetry(); - recycleBinItems.AddRange(items.ToList()); - } + + items = ctx.Site.GetRecycleBinItems(pagingInfo, iterationRowLimit, false, RecycleBinOrderBy.DefaultOrderBy, recycleBinStage); + ctx.Load(items); + ctx.ExecuteQueryRetry(); + recycleBinItems.AddRange(items.ToList()); // Paging magic (if needed) // Based on this work our good friends at Portiva did ❤ @@ -72,4 +62,4 @@ internal static List GetRecycleBinItems(ClientContext ctx, int? return recycleBinItems; } } -} \ No newline at end of file +}