Initial commit

This commit is contained in:
2026-06-02 10:51:14 +02:00
committed by kawa
commit d19092c84e
182 changed files with 13757 additions and 0 deletions
+107
View File
@@ -0,0 +1,107 @@
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using CsvHelper;
using CsvHelper.Configuration;
using SharepointToolbox.Web.Core.Models;
namespace SharepointToolbox.Web.Services;
public class CsvValidationService : ICsvValidationService
{
private static readonly Regex EmailRegex = new(@"^[^@\s]+@[^@\s]+\.[^@\s]+$", RegexOptions.Compiled);
public List<CsvValidationRow<T>> ParseAndValidate<T>(Stream csvStream) where T : class
{
using var reader = new StreamReader(csvStream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
using var csv = new CsvReader(reader, new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = true,
MissingFieldFound = null,
HeaderValidated = null,
DetectDelimiter = true,
TrimOptions = TrimOptions.Trim,
});
var rows = new List<CsvValidationRow<T>>();
csv.Read(); csv.ReadHeader();
while (csv.Read())
{
try
{
var record = csv.GetRecord<T>();
if (record == null)
{
rows.Add(CsvValidationRow<T>.ParseError(csv.Context.Parser?.RawRecord, "Failed to parse row"));
continue;
}
rows.Add(new CsvValidationRow<T>(record, new List<string>()));
}
catch (Exception ex)
{
rows.Add(CsvValidationRow<T>.ParseError(csv.Context.Parser?.RawRecord, ex.Message));
}
}
return rows;
}
public List<CsvValidationRow<BulkMemberRow>> ParseAndValidateMembers(Stream csvStream)
{
var rows = ParseAndValidate<BulkMemberRow>(csvStream);
foreach (var row in rows.Where(r => r.IsValid && r.Record != null))
row.Errors.AddRange(ValidateMemberRow(row.Record!));
return rows;
}
public List<CsvValidationRow<BulkSiteRow>> ParseAndValidateSites(Stream csvStream)
{
var rows = ParseAndValidate<BulkSiteRow>(csvStream);
foreach (var row in rows.Where(r => r.IsValid && r.Record != null))
row.Errors.AddRange(ValidateSiteRow(row.Record!));
return rows;
}
public List<CsvValidationRow<FolderStructureRow>> ParseAndValidateFolders(Stream csvStream)
{
var rows = ParseAndValidate<FolderStructureRow>(csvStream);
foreach (var row in rows.Where(r => r.IsValid && r.Record != null))
row.Errors.AddRange(ValidateFolderRow(row.Record!));
return rows;
}
private static List<string> ValidateMemberRow(BulkMemberRow row)
{
var e = new List<string>();
if (string.IsNullOrWhiteSpace(row.Email)) e.Add("Email is required");
else if (!EmailRegex.IsMatch(row.Email.Trim())) e.Add($"Invalid email: {row.Email}");
if (string.IsNullOrWhiteSpace(row.GroupName) && string.IsNullOrWhiteSpace(row.GroupUrl))
e.Add("GroupName or GroupUrl is required");
if (!string.IsNullOrWhiteSpace(row.Role) &&
!row.Role.Equals("Member", StringComparison.OrdinalIgnoreCase) &&
!row.Role.Equals("Owner", StringComparison.OrdinalIgnoreCase))
e.Add($"Role must be 'Member' or 'Owner', got: {row.Role}");
return e;
}
private static List<string> ValidateSiteRow(BulkSiteRow row)
{
var e = new List<string>();
if (string.IsNullOrWhiteSpace(row.Name)) e.Add("Name is required");
if (string.IsNullOrWhiteSpace(row.Type)) e.Add("Type is required");
else if (!row.Type.Equals("Team", StringComparison.OrdinalIgnoreCase) &&
!row.Type.Equals("Communication", StringComparison.OrdinalIgnoreCase))
e.Add($"Type must be 'Team' or 'Communication', got: {row.Type}");
if (row.Type.Equals("Team", StringComparison.OrdinalIgnoreCase) && string.IsNullOrWhiteSpace(row.Owners))
e.Add("Team sites require at least one owner");
if (row.Type.Equals("Team", StringComparison.OrdinalIgnoreCase) && string.IsNullOrWhiteSpace(row.Alias))
e.Add("Team sites require an alias");
return e;
}
private static List<string> ValidateFolderRow(FolderStructureRow row)
{
var e = new List<string>();
if (string.IsNullOrWhiteSpace(row.Level1)) e.Add("Level1 is required");
return e;
}
}