Initial commit
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
using Microsoft.SharePoint.Client;
|
||||
using PnP.Framework.Sites;
|
||||
using Serilog;
|
||||
using SharepointToolbox.Web.Core.Helpers;
|
||||
using SharepointToolbox.Web.Core.Models;
|
||||
using SharepointToolbox.Web.Services.Audit;
|
||||
|
||||
namespace SharepointToolbox.Web.Services;
|
||||
|
||||
public class BulkSiteService : IBulkSiteService
|
||||
{
|
||||
private readonly IAuditService _audit;
|
||||
|
||||
public BulkSiteService(IAuditService audit) { _audit = audit; }
|
||||
|
||||
public async Task<BulkOperationSummary<BulkSiteRow>> CreateSitesAsync(
|
||||
ClientContext adminCtx, IReadOnlyList<BulkSiteRow> rows,
|
||||
IProgress<OperationProgress> progress, CancellationToken ct)
|
||||
{
|
||||
var createdUrls = new System.Collections.Concurrent.ConcurrentBag<string>();
|
||||
var result = await BulkOperationRunner.RunAsync(rows,
|
||||
async (row, idx, token) =>
|
||||
{
|
||||
var siteUrl = await CreateSingleSiteAsync(adminCtx, row, progress, token);
|
||||
createdUrls.Add(siteUrl);
|
||||
Log.Information("Created site: {Name} ({Type}) at {Url}", row.Name, row.Type, siteUrl);
|
||||
},
|
||||
progress, ct);
|
||||
var tenantHost = Uri.TryCreate(adminCtx.Url, UriKind.Absolute, out var u) ? u.Host : adminCtx.Url;
|
||||
await _audit.LogAsync("BulkCreateSites", tenantHost, createdUrls, $"{result.SuccessCount} created, {(result.TotalCount - result.SuccessCount)} failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
private static async Task<string> CreateSingleSiteAsync(ClientContext adminCtx, BulkSiteRow row, IProgress<OperationProgress> progress, CancellationToken ct) =>
|
||||
row.Type.Equals("Team", StringComparison.OrdinalIgnoreCase) ? await CreateTeamSiteAsync(adminCtx, row, progress, ct)
|
||||
: row.Type.Equals("Communication", StringComparison.OrdinalIgnoreCase) ? await CreateCommunicationSiteAsync(adminCtx, row, progress, ct)
|
||||
: throw new InvalidOperationException($"Unknown site type: {row.Type}");
|
||||
|
||||
private static async Task<string> CreateTeamSiteAsync(ClientContext adminCtx, BulkSiteRow row, IProgress<OperationProgress> progress, CancellationToken ct)
|
||||
{
|
||||
var owners = ParseEmails(row.Owners);
|
||||
if (owners.Count == 0) throw new InvalidOperationException($"Team site '{row.Name}' requires at least one owner.");
|
||||
var creationInfo = new TeamSiteCollectionCreationInformation { DisplayName = row.Name, Alias = row.Alias, Description = string.Empty, IsPublic = false, Owners = owners.ToArray() };
|
||||
progress.Report(new OperationProgress(0, 0, $"Creating Team site: {row.Name}..."));
|
||||
using var siteCtx = await adminCtx.CreateSiteAsync(creationInfo);
|
||||
siteCtx.Load(siteCtx.Web, w => w.Url);
|
||||
await ExecuteQueryRetryHelper.ExecuteQueryRetryAsync(siteCtx, progress, ct);
|
||||
var siteUrl = siteCtx.Web.Url;
|
||||
foreach (var memberEmail in ParseEmails(row.Members))
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
try { var user = siteCtx.Web.EnsureUser(memberEmail); siteCtx.Load(user); await ExecuteQueryRetryHelper.ExecuteQueryRetryAsync(siteCtx, progress, ct); siteCtx.Web.AssociatedMemberGroup.Users.AddUser(user); await ExecuteQueryRetryHelper.ExecuteQueryRetryAsync(siteCtx, progress, ct); }
|
||||
catch (OperationCanceledException) { throw; }
|
||||
catch (Exception ex) { Log.Warning("Failed to add member {Email} to {Site}: {Error}", memberEmail, row.Name, ex.Message); }
|
||||
}
|
||||
return siteUrl;
|
||||
}
|
||||
|
||||
private static async Task<string> CreateCommunicationSiteAsync(ClientContext adminCtx, BulkSiteRow row, IProgress<OperationProgress> progress, CancellationToken ct)
|
||||
{
|
||||
var alias = !string.IsNullOrWhiteSpace(row.Alias) ? row.Alias : SanitizeAlias(row.Name);
|
||||
var tenantUrl = new Uri(adminCtx.Url);
|
||||
var creationInfo = new CommunicationSiteCollectionCreationInformation { Title = row.Name, Url = $"https://{tenantUrl.Host}/sites/{alias}", Description = string.Empty };
|
||||
progress.Report(new OperationProgress(0, 0, $"Creating Communication site: {row.Name}..."));
|
||||
using var siteCtx = await adminCtx.CreateSiteAsync(creationInfo);
|
||||
siteCtx.Load(siteCtx.Web, w => w.Url);
|
||||
await ExecuteQueryRetryHelper.ExecuteQueryRetryAsync(siteCtx, progress, ct);
|
||||
var createdUrl = siteCtx.Web.Url;
|
||||
foreach (var ownerEmail in ParseEmails(row.Owners))
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
try { var user = siteCtx.Web.EnsureUser(ownerEmail); siteCtx.Load(user); await ExecuteQueryRetryHelper.ExecuteQueryRetryAsync(siteCtx, progress, ct); siteCtx.Web.AssociatedOwnerGroup.Users.AddUser(user); await ExecuteQueryRetryHelper.ExecuteQueryRetryAsync(siteCtx, progress, ct); }
|
||||
catch (OperationCanceledException) { throw; }
|
||||
catch (Exception ex) { Log.Warning("Failed to add owner {Email}: {Error}", ownerEmail, ex.Message); }
|
||||
}
|
||||
return createdUrl;
|
||||
}
|
||||
|
||||
private static List<string> ParseEmails(string commaSeparated) =>
|
||||
string.IsNullOrWhiteSpace(commaSeparated) ? new List<string>() :
|
||||
commaSeparated.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Where(e => !string.IsNullOrWhiteSpace(e)).ToList();
|
||||
|
||||
private static string SanitizeAlias(string name) =>
|
||||
new string(name.Where(c => char.IsLetterOrDigit(c) || c == ' ' || c == '-').ToArray()).Replace(' ', '-').ToLowerInvariant();
|
||||
}
|
||||
Reference in New Issue
Block a user