-
Notifications
You must be signed in to change notification settings - Fork 367
/
Copy pathRecycleBinUtility.cs
165 lines (147 loc) · 7.55 KB
/
RecycleBinUtility.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
using Microsoft.SharePoint.Client;
using Newtonsoft.Json;
using PnP.PowerShell.Commands.Model.Mail;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Net;
using System.Net.Http;
using System.Xml.Linq;
namespace PnP.PowerShell.Commands.Utilities
{
internal static class RecycleBinUtility
{
internal static List<RecycleBinItem> GetRecycleBinItems(ClientContext ctx, int? rowLimit = null, RecycleBinItemState recycleBinStage = RecycleBinItemState.None)
{
var recycleBinItems = new List<RecycleBinItem>();
string pagingInfo = null;
RecycleBinItemCollection items;
// 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
{
// 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;
}
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 ❤
// 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;
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;
}
internal static List<RecycleBinItemCollection> GetRecycleBinItemCollection(ClientContext ctx, int? rowLimit = null, RecycleBinItemState recycleBinItemState = RecycleBinItemState.None)
{
string pagingInfo = null;
RecycleBinItemCollection items;
var recycleBinItems = new List<RecycleBinItemCollection>();
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
{
iterationRowLimit = 5000;
}
items = ctx.Site.GetRecycleBinItems(pagingInfo, iterationRowLimit, false, RecycleBinOrderBy.DefaultOrderBy, recycleBinItemState);
ctx.Load(items);
ctx.ExecuteQueryRetry();
recycleBinItems.Add(items);
if (items.Count > 0)
{
var nextId = items.Last().Id;
var nextTitle = WebUtility.UrlEncode(items.Last().Title);
pagingInfo = $"id={nextId}&title={nextTitle}";
}
}
while (items?.Count == 5000);
return recycleBinItems;
}
internal static void RestoreRecycleBinItemInBulk(HttpClient httpClient, ClientContext ctx, string[] idsList, RecycleBin.RestoreRecycleBinItem restoreRecycleBinItem)
{
//restoreRecycleBinItem provides us the reference to the instance of RestoreRecycleBinItem object. We use this object to log key information as verbose
Uri currentContextUri = new Uri(ctx.Url);
string apiCall = $"{currentContextUri}/_api/site/RecycleBin/RestoreByIds";
string idsString = string.Join("','", idsList); // Convert array to a comma-separated string
try
{
string requestBody = $"{{'ids':['{idsString}']}}";
REST.RestHelper.Post(httpClient, apiCall, ctx, requestBody, "application/json", "application/json");
restoreRecycleBinItem.WriteVerbose("Whole batch restored successfuly.");
}
catch (Exception ex)
{
{
//fall back logic
//Unable to process as batch because of an error in restoring one of the ids in batch, processing individually
restoreRecycleBinItem.WriteVerbose($"Unable to process as batch because of an error in restoring one of the ids in batch. Error:{ex.Message}");
restoreRecycleBinItem.WriteVerbose($"Switching to individul restore of items ...");
foreach (string id in idsList)
{
try
{
string requestBody = $"{{'ids':['{id}']}}";
REST.RestHelper.Post(httpClient, apiCall, ctx, requestBody, "application/json", "application/json");
restoreRecycleBinItem.WriteVerbose($"Item - {id} restored successfuly.");
}
catch (Exception e)
{
var odataError = e.Message;
if (odataError != null)
{
if (odataError.Contains("Value does not fall within the expected range."))
{
restoreRecycleBinItem.WriteVerbose($"Item - {id} already restored.");
}
else
{
//Most common reason is that an item with the same name already exists. To restore the item, rename the existing item and try again
restoreRecycleBinItem.WriteVerbose($"Item - {id} restore failed. Error:{odataError}");
}
}
//Digest errors because we cannot do anything
}
}
}
}
}
}
}