using System.IO; using System.Text; using SharepointToolbox.Core.Models; namespace SharepointToolbox.Services.Export; /// /// Exports SearchResult list to a self-contained sortable/filterable HTML report. /// Port of PS Export-SearchToHTML (PS lines 2112-2233). /// Columns are sortable by clicking the header. A filter input narrows rows by text match. /// public class SearchHtmlExportService { public string BuildHtml(IReadOnlyList results, ReportBranding? branding = null) { var sb = new StringBuilder(); sb.AppendLine(""" SharePoint File Search Results """); sb.Append(BrandingHtmlHelper.BuildBrandingHeader(branding)); sb.AppendLine("""

File Search Results

"""); sb.AppendLine(""" """); foreach (var r in results) { string fileName = System.IO.Path.GetFileName(r.Path); if (string.IsNullOrEmpty(fileName)) fileName = r.Title; sb.AppendLine($""" """); } sb.AppendLine(" \n
File Name Extension Path Created Created By Modified Modified By Size
{H(fileName)} {H(r.FileExtension)} {H(r.Path)} {(r.Created.HasValue ? r.Created.Value.ToString("yyyy-MM-dd") : string.Empty)} {H(r.Author)} {(r.LastModified.HasValue ? r.LastModified.Value.ToString("yyyy-MM-dd") : string.Empty)} {H(r.ModifiedBy)} {FormatSize(r.SizeBytes)}
"); int count = results.Count; sb.AppendLine($"

Generated: {DateTime.Now:yyyy-MM-dd HH:mm} — {count:N0} result(s)

"); sb.AppendLine($$""" """); return sb.ToString(); } public async Task WriteAsync(IReadOnlyList results, string filePath, CancellationToken ct, ReportBranding? branding = null) { var html = BuildHtml(results, branding); await System.IO.File.WriteAllTextAsync(filePath, html, Encoding.UTF8, ct); } private static string H(string value) => System.Net.WebUtility.HtmlEncode(value ?? string.Empty); private static string FormatSize(long bytes) { if (bytes >= 1_073_741_824L) return $"{bytes / 1_073_741_824.0:F2} GB"; if (bytes >= 1_048_576L) return $"{bytes / 1_048_576.0:F2} MB"; if (bytes >= 1024L) return $"{bytes / 1024.0:F2} KB"; return $"{bytes} B"; } }