---
phase: 03
plan: 03
title: Storage Export Services — CSV and Collapsible-Tree HTML
status: pending
wave: 2
depends_on:
- 03-02
files_modified:
- SharepointToolbox/Services/Export/StorageCsvExportService.cs
- SharepointToolbox/Services/Export/StorageHtmlExportService.cs
autonomous: true
requirements:
- STOR-04
- STOR-05
must_haves:
truths:
- "StorageCsvExportService.BuildCsv produces a UTF-8 BOM CSV with header: Library, Site, Files, Total Size (MB), Version Size (MB), Last Modified"
- "StorageCsvExportService.BuildCsv includes one row per StorageNode (flattened, respects IndentLevel for Library name prefix)"
- "StorageHtmlExportService.BuildHtml produces a self-contained HTML file with inline CSS and JS — no external dependencies"
- "StorageHtmlExportService.BuildHtml includes toggle(i) JS and collapsible subfolder rows (sf-{i} IDs)"
- "StorageCsvExportServiceTests: all 3 tests pass"
- "StorageHtmlExportServiceTests: all 3 tests pass"
artifacts:
- path: "SharepointToolbox/Services/Export/StorageCsvExportService.cs"
provides: "CSV exporter for StorageNode list (STOR-04)"
exports: ["StorageCsvExportService"]
- path: "SharepointToolbox/Services/Export/StorageHtmlExportService.cs"
provides: "Collapsible-tree HTML exporter for StorageNode list (STOR-05)"
exports: ["StorageHtmlExportService"]
key_links:
- from: "StorageCsvExportService.cs"
to: "StorageNode.VersionSizeBytes"
via: "computed property"
pattern: "VersionSizeBytes"
- from: "StorageHtmlExportService.cs"
to: "toggle(i) JS"
via: "inline script"
pattern: "toggle\\("
---
# Plan 03-03: Storage Export Services — CSV and Collapsible-Tree HTML
## Goal
Replace the stub implementations in `StorageCsvExportService` and `StorageHtmlExportService` with real implementations. The CSV export produces a flat UTF-8 BOM CSV compatible with Excel. The HTML export ports the PowerShell `Export-StorageToHTML` function (PS lines 1621-1780), producing a self-contained HTML file with a collapsible tree view driven by an inline `toggle(i)` JavaScript function.
## Context
Plan 03-01 created stub `BuildCsv`/`BuildHtml` methods returning `string.Empty`. This plan fills them in. The test files `StorageCsvExportServiceTests.cs` and `StorageHtmlExportServiceTests.cs` already exist and define the expected output — they currently fail because of the stubs.
Pattern reference: Phase 2 `CsvExportService` uses UTF-8 BOM + RFC 4180 quoting. The same `Csv()` helper pattern is applied here. `StorageHtmlExportService` uses a `_togIdx` counter reset at the start of each `BuildHtml` call (per the PS pattern) to generate unique IDs for collapsible rows.
## Tasks
### Task 1: Implement StorageCsvExportService
**File:** `SharepointToolbox/Services/Export/StorageCsvExportService.cs`
**Action:** Modify (replace stub with full implementation)
**Why:** STOR-04 — user can export storage metrics to CSV.
```csharp
using SharepointToolbox.Core.Models;
using System.Text;
namespace SharepointToolbox.Services.Export;
///
/// Exports a flat list of StorageNode objects to a UTF-8 BOM CSV.
/// Compatible with Microsoft Excel (BOM signals UTF-8 encoding).
///
public class StorageCsvExportService
{
public string BuildCsv(IReadOnlyList nodes)
{
var sb = new StringBuilder();
// Header
sb.AppendLine("Library,Site,Files,Total Size (MB),Version Size (MB),Last Modified");
foreach (var node in nodes)
{
sb.AppendLine(string.Join(",",
Csv(node.Name),
Csv(node.SiteTitle),
node.TotalFileCount.ToString(),
FormatMb(node.TotalSizeBytes),
FormatMb(node.VersionSizeBytes),
node.LastModified.HasValue
? Csv(node.LastModified.Value.ToString("yyyy-MM-dd"))
: string.Empty));
}
return sb.ToString();
}
public async Task WriteAsync(IReadOnlyList nodes, string filePath, CancellationToken ct)
{
var csv = BuildCsv(nodes);
// UTF-8 with BOM for Excel compatibility
await File.WriteAllTextAsync(filePath, csv, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true), ct);
}
// ── Helpers ───────────────────────────────────────────────────────────────
private static string FormatMb(long bytes)
=> (bytes / (1024.0 * 1024.0)).ToString("F2");
/// RFC 4180 CSV field quoting.
private static string Csv(string value)
{
if (string.IsNullOrEmpty(value)) return string.Empty;
if (value.Contains(',') || value.Contains('"') || value.Contains('\n'))
return $"\"{value.Replace("\"", "\"\"")}\"";
return value;
}
}
```
**Verification:**
```bash
dotnet test C:/Users/dev/Documents/projets/Sharepoint/SharepointToolbox.Tests/SharepointToolbox.Tests.csproj --filter "FullyQualifiedName~StorageCsvExportServiceTests" -x
```
Expected: 3 tests pass
### Task 2: Implement StorageHtmlExportService
**File:** `SharepointToolbox/Services/Export/StorageHtmlExportService.cs`
**Action:** Modify (replace stub with full implementation)
**Why:** STOR-05 — user can export storage metrics to interactive HTML with collapsible tree view.
```csharp
using SharepointToolbox.Core.Models;
using System.Text;
namespace SharepointToolbox.Services.Export;
///
/// Exports StorageNode tree to a self-contained HTML file with collapsible subfolder rows.
/// Port of PS Export-StorageToHTML (PS lines 1621-1780).
/// Uses a toggle(i) JS pattern where each collapsible row has id="sf-{i}".
///
public class StorageHtmlExportService
{
private int _togIdx;
public string BuildHtml(IReadOnlyList nodes)
{
_togIdx = 0;
var sb = new StringBuilder();
sb.AppendLine("""
SharePoint Storage Metrics