Add report logos and configurable folder scan depth
Report branding (top-left MSP logo, top-right client logo): - Add MspLogo to AppSettings; client logo already on TenantProfile - IUserSessionService.CurrentBranding composes MSP + active profile logo - New reusable LogoUpload component (InputFile -> base64 LogoData, 512KB cap) - MSP logo upload in Settings; optional client logo in profile create/edit - Wire ReportBranding into all 6 HTML export pages - Fix EditProfile dropping ClientLogo on edit Storage metrics: expose folder scan depth (0-20) in scan options UI, passed to existing StorageScanOptions.FolderDepth recursion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -111,5 +111,5 @@
|
||||
|
||||
private void Cancel() => _cts?.Cancel();
|
||||
private async Task ExportCsv() { await WebExport.DownloadCsvAsync(CsvExport.BuildCsv(_results), $"duplicates_{DateTime.Now:yyyyMMdd_HHmmss}.csv"); }
|
||||
private async Task ExportHtml() { await WebExport.DownloadHtmlAsync(HtmlExport.BuildHtml(_results), $"duplicates_{DateTime.Now:yyyyMMdd_HHmmss}.html"); }
|
||||
private async Task ExportHtml() { await WebExport.DownloadHtmlAsync(HtmlExport.BuildHtml(_results, Session.CurrentBranding), $"duplicates_{DateTime.Now:yyyyMMdd_HHmmss}.html"); }
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
|
||||
private async Task ExportHtml()
|
||||
{
|
||||
var html = HtmlExport.BuildHtml(_results);
|
||||
var html = HtmlExport.BuildHtml(_results, Session.CurrentBranding);
|
||||
await WebExport.DownloadHtmlAsync(html, $"permissions_{DateTime.Now:yyyyMMdd_HHmmss}.html");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,12 @@
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Client logo (optional)</label>
|
||||
<small class="text-muted d-block" style="margin-bottom:6px">Shown top-right on exported reports for this client.</small>
|
||||
<LogoUpload Value="_form.ClientLogo" ValueChanged="(LogoData? l) => _form.ClientLogo = l" />
|
||||
</div>
|
||||
|
||||
<div class="flex-row mt-8">
|
||||
<button class="btn btn-primary" @onclick="SaveProfile">Save</button>
|
||||
<button class="btn btn-secondary" @onclick="CancelForm">Cancel</button>
|
||||
@@ -214,7 +220,7 @@
|
||||
private void EditProfile(TenantProfile p)
|
||||
{
|
||||
_editing = p;
|
||||
_form = new TenantProfile { Id = p.Id, Name = p.Name, TenantUrl = p.TenantUrl, TenantId = p.TenantId, ClientId = p.ClientId };
|
||||
_form = new TenantProfile { Id = p.Id, Name = p.Name, TenantUrl = p.TenantUrl, TenantId = p.TenantId, ClientId = p.ClientId, ClientLogo = p.ClientLogo };
|
||||
_showForm = true;
|
||||
_formError = _pageError = string.Empty;
|
||||
}
|
||||
|
||||
@@ -124,5 +124,5 @@
|
||||
|
||||
private void Cancel() => _cts?.Cancel();
|
||||
private async Task ExportCsv() { await WebExport.DownloadCsvAsync(CsvExport.BuildCsv(_results), $"search_{DateTime.Now:yyyyMMdd_HHmmss}.csv"); }
|
||||
private async Task ExportHtml() { await WebExport.DownloadHtmlAsync(HtmlExport.BuildHtml(_results), $"search_{DateTime.Now:yyyyMMdd_HHmmss}.html"); }
|
||||
private async Task ExportHtml() { await WebExport.DownloadHtmlAsync(HtmlExport.BuildHtml(_results, Session.CurrentBranding), $"search_{DateTime.Now:yyyyMMdd_HHmmss}.html"); }
|
||||
}
|
||||
|
||||
@@ -35,11 +35,21 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">Report Branding</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">MSP logo</label>
|
||||
<p class="text-muted" style="margin-top:0">Shown top-left on exported HTML reports. The client's logo (top-right) is set per profile.</p>
|
||||
<LogoUpload Value="_mspLogo" ValueChanged="OnMspLogoChanged" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (_saved) { <div class="alert alert-success">Settings saved.</div> }
|
||||
|
||||
@code {
|
||||
private string _lang = "en", _theme = "System";
|
||||
private bool _autoTakeOwnership, _saved;
|
||||
private LogoData? _mspLogo;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
@@ -47,11 +57,18 @@
|
||||
_lang = s.Lang;
|
||||
_theme = s.Theme is "System" or "Light" ? s.Theme : "System";
|
||||
_autoTakeOwnership = s.AutoTakeOwnership;
|
||||
_mspLogo = s.MspLogo;
|
||||
}
|
||||
|
||||
private async Task OnMspLogoChanged(LogoData? logo)
|
||||
{
|
||||
_mspLogo = logo;
|
||||
await Save();
|
||||
}
|
||||
|
||||
private async Task Save()
|
||||
{
|
||||
Session.UpdateSettings(new AppSettings { Lang = _lang, Theme = _theme, AutoTakeOwnership = _autoTakeOwnership });
|
||||
Session.UpdateSettings(new AppSettings { Lang = _lang, Theme = _theme, AutoTakeOwnership = _autoTakeOwnership, MspLogo = _mspLogo });
|
||||
SharepointToolbox.Web.Localization.TranslationSource.Instance.SetCulture(_lang);
|
||||
await JS.InvokeVoidAsync("sptb.setTheme", _theme);
|
||||
_saved = true;
|
||||
|
||||
@@ -21,6 +21,13 @@
|
||||
<input class="form-input" @bind="_siteUrl" placeholder="@Session.CurrentProfile!.TenantUrl" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group" style="max-width:220px">
|
||||
<label class="form-label">Folder scan depth</label>
|
||||
<input class="form-input" type="number" min="0" max="20" @bind="_folderDepth" />
|
||||
<small class="text-muted">0 = libraries only. 1+ = drill into subfolders that many levels deep.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group" style="display:flex;align-items:center;gap:16px;padding-top:20px">
|
||||
<label><input type="checkbox" @bind="_includeSubsites" /> Include subsites</label>
|
||||
@@ -81,6 +88,7 @@
|
||||
@code {
|
||||
private string _siteUrl = string.Empty;
|
||||
private bool _includeSubsites, _includeHidden = true, _includeRecycleBin = true;
|
||||
private int _folderDepth;
|
||||
private bool _running; private string _status = string.Empty, _error = string.Empty;
|
||||
private int _current, _total;
|
||||
private List<StorageNode> _results = new();
|
||||
@@ -94,7 +102,7 @@
|
||||
var progress = new Progress<OperationProgress>(p => { _status = p.Message; _current = p.Current; _total = p.Total; InvokeAsync(StateHasChanged); });
|
||||
try
|
||||
{
|
||||
var opts = new StorageScanOptions(IncludeSubsites: _includeSubsites, IncludeHiddenLibraries: _includeHidden, IncludeRecycleBin: _includeRecycleBin);
|
||||
var opts = new StorageScanOptions(IncludeSubsites: _includeSubsites, FolderDepth: Math.Clamp(_folderDepth, 0, 20), IncludeHiddenLibraries: _includeHidden, IncludeRecycleBin: _includeRecycleBin);
|
||||
_results = (await Elevation.RunAsync(async c =>
|
||||
{
|
||||
var ctx = await SessionMgr.GetOrCreateContextAsync(siteUrl, Session.CurrentProfile!, c);
|
||||
@@ -116,7 +124,7 @@
|
||||
}
|
||||
private async Task ExportHtml()
|
||||
{
|
||||
var html = HtmlExport.BuildHtml(_results);
|
||||
var html = HtmlExport.BuildHtml(_results, Session.CurrentBranding);
|
||||
await WebExport.DownloadHtmlAsync(html, $"storage_{DateTime.Now:yyyyMMdd_HHmmss}.html");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,5 +103,5 @@
|
||||
|
||||
private void Cancel() => _cts?.Cancel();
|
||||
private async Task ExportCsv() { await WebExport.DownloadCsvAsync(CsvExport.BuildCsv(_results.FirstOrDefault()?.UserDisplayName ?? "Users", _results.FirstOrDefault()?.UserLogin ?? "", _results), $"user_audit_{DateTime.Now:yyyyMMdd_HHmmss}.csv"); }
|
||||
private async Task ExportHtml() { await WebExport.DownloadHtmlAsync(HtmlExport.BuildHtml(_results), $"user_audit_{DateTime.Now:yyyyMMdd_HHmmss}.html"); }
|
||||
private async Task ExportHtml() { await WebExport.DownloadHtmlAsync(HtmlExport.BuildHtml(_results, branding: Session.CurrentBranding), $"user_audit_{DateTime.Now:yyyyMMdd_HHmmss}.html"); }
|
||||
}
|
||||
|
||||
@@ -144,5 +144,5 @@
|
||||
}
|
||||
|
||||
private void Cancel() => _cts?.Cancel();
|
||||
private async Task ExportHtml() { await WebExport.DownloadHtmlAsync(HtmlExport.BuildHtml(_results), $"versions_{DateTime.Now:yyyyMMdd_HHmmss}.html"); }
|
||||
private async Task ExportHtml() { await WebExport.DownloadHtmlAsync(HtmlExport.BuildHtml(_results, Session.CurrentBranding), $"versions_{DateTime.Now:yyyyMMdd_HHmmss}.html"); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user