Configure CertificateCheckUrl per container for curl-like TLS checks, classify Sonic permission errors, and extend setup wizard for container management. Co-authored-by: Cursor <cursoragent@cursor.com>
1524 lines
44 KiB
C#
1524 lines
44 KiB
C#
using ZA.CoreService.ESBCertificateManager.Models;
|
||
using ZA.CoreService.ESBCertificateManager.Services;
|
||
|
||
namespace ZA.CoreService.ESBCertificateManager.Setup;
|
||
|
||
public sealed class SetupWizardForm : Form
|
||
{
|
||
private readonly SetupCoordinator _coordinator;
|
||
private readonly bool _isSettingsMode;
|
||
|
||
private Panel _sidebar = null!;
|
||
private Panel _contentHost = null!;
|
||
private Panel _panelStatus = null!;
|
||
private Panel _panelSystems = null!;
|
||
private Panel _panelContainers = null!;
|
||
private Panel _panelPaths = null!;
|
||
private Panel _panelProfiles = null!;
|
||
|
||
private readonly List<Button> _navButtons = [];
|
||
private Button _activeNav = null!;
|
||
|
||
private Label _lblRuntime = null!;
|
||
private Label _lblCertificate = null!;
|
||
private Label _lblDatabase = null!;
|
||
private Label _lblSelection = null!;
|
||
|
||
private ListView _lvSystems = null!;
|
||
private ListView _lvContainers = null!;
|
||
private ListView _lvPaths = null!;
|
||
|
||
private ComboBox _cmbSystem = null!;
|
||
private ComboBox _cmbCredential = null!;
|
||
private Label _lblSystemDetails = null!;
|
||
|
||
private Button _btnImportCertificate = null!;
|
||
private Button _btnSaveSelection = null!;
|
||
private Button _btnFinish = null!;
|
||
private Button _btnAddCredential = null!;
|
||
private Button _btnEditCredential = null!;
|
||
|
||
private IReadOnlyList<SonicSystemOption> _systems = [];
|
||
private IReadOnlyList<SonicCredentialProfile> _profiles = [];
|
||
private bool _running;
|
||
|
||
public SetupWizardForm(
|
||
SetupCoordinator coordinator,
|
||
bool isSettingsMode = false)
|
||
{
|
||
_coordinator = coordinator;
|
||
_isSettingsMode = isSettingsMode;
|
||
|
||
Text = "Einstellungen";
|
||
StartPosition = FormStartPosition.CenterScreen;
|
||
Size = new Size(1180, 760);
|
||
MinimumSize = new Size(1080, 700);
|
||
BackColor = SettingsUi.Background;
|
||
ForeColor = SettingsUi.Text;
|
||
Font = new Font("Segoe UI", 10f);
|
||
DoubleBuffered = true;
|
||
|
||
BuildShell();
|
||
BuildStatusPanel();
|
||
BuildSystemsPanel();
|
||
BuildContainersPanel();
|
||
BuildPathsPanel();
|
||
BuildProfilesPanel();
|
||
ShowPanel(_panelStatus, _navButtons[0]);
|
||
|
||
Shown += async (_, _) => await RefreshAllAsync();
|
||
|
||
FormClosing += (_, e) =>
|
||
{
|
||
// X-Button: in Einstellungen trotzdem als „geschlossen mit Änderungen möglich“
|
||
if (_isSettingsMode && DialogResult == DialogResult.None)
|
||
{
|
||
DialogResult = DialogResult.OK;
|
||
}
|
||
};
|
||
}
|
||
|
||
private void BuildShell()
|
||
{
|
||
_sidebar = new Panel
|
||
{
|
||
Dock = DockStyle.Left,
|
||
Width = 230,
|
||
BackColor = SettingsUi.Sidebar
|
||
};
|
||
|
||
Label brand = new()
|
||
{
|
||
Text = "ESB Certificate",
|
||
Location = new Point(22, 28),
|
||
AutoSize = true,
|
||
ForeColor = SettingsUi.Gold,
|
||
Font = new Font("Segoe UI Semibold", 11f, FontStyle.Bold)
|
||
};
|
||
|
||
Label brandSub = new()
|
||
{
|
||
Text = "Einstellungen",
|
||
Location = new Point(22, 52),
|
||
AutoSize = true,
|
||
ForeColor = SettingsUi.Text,
|
||
Font = new Font("Segoe UI Semibold", 20f, FontStyle.Bold)
|
||
};
|
||
|
||
_sidebar.Controls.Add(brand);
|
||
_sidebar.Controls.Add(brandSub);
|
||
|
||
string[] navItems =
|
||
[
|
||
"Prüfung",
|
||
"Systeme",
|
||
"Container",
|
||
"Pfade",
|
||
"Profile"
|
||
];
|
||
|
||
int y = 120;
|
||
|
||
foreach (string item in navItems)
|
||
{
|
||
Button nav = CreateNavButton(item);
|
||
nav.Location = new Point(14, y);
|
||
y += 48;
|
||
_navButtons.Add(nav);
|
||
_sidebar.Controls.Add(nav);
|
||
}
|
||
|
||
_navButtons[0].Click += (_, _) =>
|
||
ShowPanel(_panelStatus, _navButtons[0]);
|
||
_navButtons[1].Click += async (_, _) =>
|
||
{
|
||
ShowPanel(_panelSystems, _navButtons[1]);
|
||
await LoadSystemsListAsync();
|
||
};
|
||
_navButtons[2].Click += async (_, _) =>
|
||
{
|
||
ShowPanel(_panelContainers, _navButtons[2]);
|
||
await LoadContainersListAsync();
|
||
};
|
||
_navButtons[3].Click += async (_, _) =>
|
||
{
|
||
ShowPanel(_panelPaths, _navButtons[3]);
|
||
await LoadPathsListAsync();
|
||
};
|
||
_navButtons[4].Click += async (_, _) =>
|
||
{
|
||
ShowPanel(_panelProfiles, _navButtons[4]);
|
||
await LoadSystemsAsync();
|
||
};
|
||
|
||
Panel footer = new()
|
||
{
|
||
Dock = DockStyle.Bottom,
|
||
Height = 72,
|
||
BackColor = SettingsUi.Background
|
||
};
|
||
|
||
Button closeButton = SettingsUi.CreateGhostButton(
|
||
_isSettingsMode ? "Schließen" : "Abbrechen");
|
||
closeButton.Location = new Point(Width - 340, 16);
|
||
closeButton.Size = new Size(120, 40);
|
||
closeButton.Anchor = AnchorStyles.Top | AnchorStyles.Right;
|
||
closeButton.Click += async (_, _) => await CloseAsync();
|
||
|
||
_btnFinish = SettingsUi.CreatePrimaryButton(
|
||
_isSettingsMode ? "Übernehmen" : "Fertig");
|
||
_btnFinish.Location = new Point(Width - 200, 16);
|
||
_btnFinish.Size = new Size(160, 40);
|
||
_btnFinish.Anchor = AnchorStyles.Top | AnchorStyles.Right;
|
||
_btnFinish.Enabled = false;
|
||
_btnFinish.Click += (_, _) =>
|
||
{
|
||
DialogResult = DialogResult.OK;
|
||
Close();
|
||
};
|
||
|
||
Button refresh = SettingsUi.CreateGhostButton("Aktualisieren");
|
||
refresh.Location = new Point(250, 16);
|
||
refresh.Size = new Size(130, 40);
|
||
refresh.Click += async (_, _) => await RefreshAllAsync();
|
||
|
||
footer.Controls.Add(refresh);
|
||
footer.Controls.Add(closeButton);
|
||
footer.Controls.Add(_btnFinish);
|
||
|
||
_contentHost = new Panel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
BackColor = SettingsUi.Background,
|
||
Padding = new Padding(28, 24, 28, 12)
|
||
};
|
||
|
||
Controls.Add(_contentHost);
|
||
Controls.Add(footer);
|
||
Controls.Add(_sidebar);
|
||
|
||
Resize += (_, _) =>
|
||
{
|
||
closeButton.Left = footer.Width - 340;
|
||
_btnFinish.Left = footer.Width - 200;
|
||
};
|
||
}
|
||
|
||
private Button CreateNavButton(string text)
|
||
{
|
||
Button button = new()
|
||
{
|
||
Text = " " + text,
|
||
Size = new Size(200, 42),
|
||
FlatStyle = FlatStyle.Flat,
|
||
TextAlign = ContentAlignment.MiddleLeft,
|
||
BackColor = SettingsUi.Sidebar,
|
||
ForeColor = SettingsUi.Muted,
|
||
Font = new Font("Segoe UI Semibold", 10.5f, FontStyle.Bold),
|
||
Cursor = Cursors.Hand
|
||
};
|
||
|
||
button.FlatAppearance.BorderSize = 0;
|
||
button.FlatAppearance.MouseOverBackColor = SettingsUi.CardHover;
|
||
return button;
|
||
}
|
||
|
||
private void ShowPanel(Panel panel, Button nav)
|
||
{
|
||
foreach (Control control in _contentHost.Controls)
|
||
{
|
||
control.Visible = false;
|
||
}
|
||
|
||
panel.Visible = true;
|
||
panel.BringToFront();
|
||
|
||
foreach (Button button in _navButtons)
|
||
{
|
||
button.BackColor = SettingsUi.Sidebar;
|
||
button.ForeColor = SettingsUi.Muted;
|
||
}
|
||
|
||
nav.BackColor = SettingsUi.Card;
|
||
nav.ForeColor = SettingsUi.Gold;
|
||
_activeNav = nav;
|
||
}
|
||
|
||
private Panel CreateContentPanel(string title, string subtitle)
|
||
{
|
||
Panel panel = new()
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
BackColor = SettingsUi.Background,
|
||
Visible = false,
|
||
Padding = new Padding(4)
|
||
};
|
||
|
||
// Reihenfolge für Dock: Fill zuerst, dann Toolbar (Top), Header zuletzt (Top außen).
|
||
Panel header = CreateHeaderPanel(title, subtitle);
|
||
panel.Tag = header;
|
||
|
||
_contentHost.Controls.Add(panel);
|
||
return panel;
|
||
}
|
||
|
||
private static Panel CreateHeaderPanel(string title, string subtitle)
|
||
{
|
||
Panel header = new()
|
||
{
|
||
Dock = DockStyle.Top,
|
||
Height = 72,
|
||
BackColor = SettingsUi.Background
|
||
};
|
||
|
||
Label heading = new()
|
||
{
|
||
Text = title,
|
||
AutoSize = true,
|
||
Location = new Point(4, 2),
|
||
ForeColor = SettingsUi.Text,
|
||
Font = new Font("Segoe UI Semibold", 22f, FontStyle.Bold)
|
||
};
|
||
|
||
Label sub = new()
|
||
{
|
||
Text = subtitle,
|
||
AutoSize = true,
|
||
Location = new Point(8, 40),
|
||
ForeColor = SettingsUi.Muted,
|
||
Font = new Font("Segoe UI", 10f)
|
||
};
|
||
|
||
header.Controls.Add(heading);
|
||
header.Controls.Add(sub);
|
||
return header;
|
||
}
|
||
|
||
private static void FinishPanelLayout(Panel panel, Control list, Panel toolbar)
|
||
{
|
||
Panel header = (Panel)panel.Tag!;
|
||
|
||
// 1) Fill 2) Toolbar Top 3) Header Top (außen)
|
||
panel.Controls.Add(list);
|
||
panel.Controls.Add(toolbar);
|
||
panel.Controls.Add(header);
|
||
}
|
||
|
||
private void BuildStatusPanel()
|
||
{
|
||
_panelStatus = CreateContentPanel(
|
||
"Systemprüfung",
|
||
"Runtime, Zertifikat, Datenbank und aktuelle Auswahl.");
|
||
|
||
Panel card = SettingsUi.CreateCard();
|
||
card.Dock = DockStyle.Fill;
|
||
card.Padding = new Padding(12);
|
||
card.Paint += SettingsUi.PaintCardBorder;
|
||
|
||
_lblRuntime = CreateStatusLabel(20, 24);
|
||
_lblCertificate = CreateStatusLabel(20, 78);
|
||
_lblDatabase = CreateStatusLabel(20, 132);
|
||
_lblSelection = CreateStatusLabel(20, 186);
|
||
|
||
_btnImportCertificate = SettingsUi.CreatePrimaryButton("PFX importieren");
|
||
_btnImportCertificate.Location = new Point(640, 72);
|
||
_btnImportCertificate.Size = new Size(180, 38);
|
||
_btnImportCertificate.Anchor = AnchorStyles.Top | AnchorStyles.Right;
|
||
_btnImportCertificate.Click += async (_, _) =>
|
||
await ImportCertificateAsync();
|
||
|
||
Label tip = new()
|
||
{
|
||
Text =
|
||
"Tipp: Unter „Systeme“, „Container“ und „Pfade“ kannst du " +
|
||
"die Katalogdaten für die aktuelle Umgebung pflegen.",
|
||
Location = new Point(20, 235),
|
||
Size = new Size(780, 28),
|
||
ForeColor = SettingsUi.Muted
|
||
};
|
||
|
||
card.Controls.Add(_lblRuntime);
|
||
card.Controls.Add(_lblCertificate);
|
||
card.Controls.Add(_lblDatabase);
|
||
card.Controls.Add(_lblSelection);
|
||
card.Controls.Add(_btnImportCertificate);
|
||
card.Controls.Add(tip);
|
||
|
||
Panel header = (Panel)_panelStatus.Tag!;
|
||
_panelStatus.Controls.Add(card);
|
||
_panelStatus.Controls.Add(header);
|
||
}
|
||
|
||
private void BuildSystemsPanel()
|
||
{
|
||
_panelSystems = CreateContentPanel(
|
||
"Sonic-Systeme",
|
||
"Management-Verbindungen der aktuellen Umgebung.");
|
||
|
||
Panel toolbar = CreateToolbar(
|
||
("+ System hinzufügen", async () => await AddSystemAsync(), true, 210),
|
||
("Bearbeiten", async () => await EditSelectedSystemAsync(), false, 130),
|
||
("Löschen", async () => await DeleteSelectedSystemAsync(), false, 120));
|
||
|
||
_lvSystems = SettingsUi.CreateListView();
|
||
_lvSystems.Dock = DockStyle.Fill;
|
||
_lvSystems.Columns.Add("Name", 160);
|
||
_lvSystems.Columns.Add("Host", 180);
|
||
_lvSystems.Columns.Add("Port", 70);
|
||
_lvSystems.Columns.Add("Domain", 180);
|
||
_lvSystems.Columns.Add("Protokoll", 80);
|
||
_lvSystems.Columns.Add("URL", 180);
|
||
_lvSystems.DoubleClick += async (_, _) => await EditSelectedSystemAsync();
|
||
|
||
FinishPanelLayout(_panelSystems, _lvSystems, toolbar);
|
||
}
|
||
|
||
private void BuildContainersPanel()
|
||
{
|
||
_panelContainers = CreateContentPanel(
|
||
"Container",
|
||
"Sonic-Container je Company und System.");
|
||
|
||
Panel toolbar = CreateToolbar(
|
||
("+ Container hinzufügen", async () => await AddContainerAsync(), true, 220),
|
||
("Bearbeiten", async () => await EditSelectedContainerAsync(), false, 130),
|
||
("Löschen", async () => await DeleteSelectedContainerAsync(), false, 120));
|
||
|
||
_lvContainers = SettingsUi.CreateListView();
|
||
_lvContainers.Dock = DockStyle.Fill;
|
||
_lvContainers.Columns.Add("Company", 70);
|
||
_lvContainers.Columns.Add("Container", 150);
|
||
_lvContainers.Columns.Add("Anzeige", 120);
|
||
_lvContainers.Columns.Add("System", 120);
|
||
_lvContainers.Columns.Add("Prüf-URL", 180);
|
||
_lvContainers.Columns.Add("Timeout", 80);
|
||
_lvContainers.DoubleClick += async (_, _) => await EditSelectedContainerAsync();
|
||
|
||
FinishPanelLayout(_panelContainers, _lvContainers, toolbar);
|
||
}
|
||
|
||
private void BuildPathsPanel()
|
||
{
|
||
_panelPaths = CreateContentPanel(
|
||
"Zertifikat-Pfade",
|
||
"Dateipfade (UNC/lokal) zum Austausch. Container = nur Neustart-Ziel.");
|
||
|
||
Panel toolbar = CreateToolbar(
|
||
("+ Pfad hinzufügen", async () => await AddPathAsync(), true, 190),
|
||
("Bearbeiten", async () => await EditSelectedPathAsync(), false, 120),
|
||
("Pfad prüfen", async () => await ProbeSelectedPathAsync(), false, 130),
|
||
("Löschen", async () => await DeleteSelectedPathAsync(), false, 110));
|
||
|
||
_lvPaths = SettingsUi.CreateListView();
|
||
_lvPaths.Dock = DockStyle.Fill;
|
||
_lvPaths.Columns.Add("Company", 70);
|
||
_lvPaths.Columns.Add("Neustart-Container", 140);
|
||
_lvPaths.Columns.Add("Zertifikat-Pfad", 360);
|
||
_lvPaths.Columns.Add("Ablauf", 140);
|
||
_lvPaths.Columns.Add("Backup", 80);
|
||
_lvPaths.Columns.Add("System", 120);
|
||
_lvPaths.DoubleClick += async (_, _) => await EditSelectedPathAsync();
|
||
|
||
FinishPanelLayout(_panelPaths, _lvPaths, toolbar);
|
||
}
|
||
|
||
private static Panel CreateToolbar(
|
||
params (string Text, Func<Task> Action, bool Primary, int Width)[] buttons)
|
||
{
|
||
Panel toolbar = new()
|
||
{
|
||
Dock = DockStyle.Top,
|
||
Height = 56,
|
||
BackColor = SettingsUi.Background,
|
||
Padding = new Padding(8, 8, 8, 8)
|
||
};
|
||
|
||
// Dock Left: zuletzt hinzugefügt erscheint ganz links
|
||
for (int index = buttons.Length - 1; index >= 0; index--)
|
||
{
|
||
(string text, Func<Task> action, bool primary, int width) =
|
||
buttons[index];
|
||
|
||
Button button = primary
|
||
? SettingsUi.CreatePrimaryButton(text)
|
||
: SettingsUi.CreateGhostButton(text);
|
||
|
||
button.Dock = DockStyle.Left;
|
||
button.Width = width;
|
||
button.Margin = new Padding(8, 0, 0, 0);
|
||
button.Click += async (_, _) => await action();
|
||
toolbar.Controls.Add(button);
|
||
}
|
||
|
||
return toolbar;
|
||
}
|
||
|
||
private void BuildProfilesPanel()
|
||
{
|
||
_panelProfiles = CreateContentPanel(
|
||
"Profil & Auswahl",
|
||
"Aktives Sonic-System und Benutzerprofil für Neustarts.");
|
||
|
||
Panel card = SettingsUi.CreateCard();
|
||
card.Dock = DockStyle.Fill;
|
||
card.Padding = new Padding(12);
|
||
card.Paint += SettingsUi.PaintCardBorder;
|
||
|
||
Label systemLabel = SettingsUi.CreateFieldLabel("Sonic-System");
|
||
systemLabel.Location = new Point(24, 24);
|
||
|
||
_cmbSystem = SettingsUi.CreateComboBox();
|
||
_cmbSystem.Location = new Point(24, 48);
|
||
_cmbSystem.Width = 340;
|
||
_cmbSystem.SelectedIndexChanged += async (_, _) =>
|
||
await SystemChangedAsync();
|
||
|
||
Label credentialLabel = SettingsUi.CreateFieldLabel("Benutzerprofil");
|
||
credentialLabel.Location = new Point(24, 100);
|
||
|
||
_cmbCredential = SettingsUi.CreateComboBox();
|
||
_cmbCredential.Location = new Point(24, 124);
|
||
_cmbCredential.Width = 340;
|
||
|
||
_btnAddCredential = SettingsUi.CreateGhostButton("Profil hinzufügen");
|
||
_btnAddCredential.Location = new Point(380, 122);
|
||
_btnAddCredential.Size = new Size(160, 34);
|
||
_btnAddCredential.Click += async (_, _) =>
|
||
await AddCredentialAsync();
|
||
|
||
_btnEditCredential = SettingsUi.CreateGhostButton("Profil bearbeiten");
|
||
_btnEditCredential.Location = new Point(550, 122);
|
||
_btnEditCredential.Size = new Size(160, 34);
|
||
_btnEditCredential.Click += async (_, _) =>
|
||
await EditCredentialAsync();
|
||
|
||
_lblSystemDetails = new Label
|
||
{
|
||
Location = new Point(24, 180),
|
||
Size = new Size(780, 50),
|
||
ForeColor = SettingsUi.Muted
|
||
};
|
||
|
||
_btnSaveSelection = SettingsUi.CreatePrimaryButton("Auswahl übernehmen");
|
||
_btnSaveSelection.Location = new Point(600, 220);
|
||
_btnSaveSelection.Size = new Size(220, 40);
|
||
_btnSaveSelection.Click += async (_, _) =>
|
||
await SaveSelectionAsync();
|
||
|
||
card.Controls.Add(systemLabel);
|
||
card.Controls.Add(_cmbSystem);
|
||
card.Controls.Add(credentialLabel);
|
||
card.Controls.Add(_cmbCredential);
|
||
card.Controls.Add(_btnAddCredential);
|
||
card.Controls.Add(_btnEditCredential);
|
||
card.Controls.Add(_lblSystemDetails);
|
||
card.Controls.Add(_btnSaveSelection);
|
||
|
||
Panel header = (Panel)_panelProfiles.Tag!;
|
||
_panelProfiles.Controls.Add(card);
|
||
_panelProfiles.Controls.Add(header);
|
||
}
|
||
|
||
private static Label CreateStatusLabel(int x, int y)
|
||
{
|
||
return new Label
|
||
{
|
||
Location = new Point(x, y),
|
||
Size = new Size(600, 40),
|
||
ForeColor = SettingsUi.Muted,
|
||
Font = new Font("Segoe UI", 10.5f)
|
||
};
|
||
}
|
||
|
||
private async Task RefreshAllAsync()
|
||
{
|
||
if (_running)
|
||
{
|
||
return;
|
||
}
|
||
|
||
SetRunning(true);
|
||
|
||
try
|
||
{
|
||
SetupCheckResult result =
|
||
await _coordinator.CheckAsync();
|
||
|
||
SetStatus(
|
||
_lblRuntime,
|
||
"Runtime",
|
||
result.RuntimeReady,
|
||
result.RuntimeMessage);
|
||
|
||
SetStatus(
|
||
_lblCertificate,
|
||
"Always Encrypted",
|
||
result.CertificateReady,
|
||
result.CertificateMessage);
|
||
|
||
SetStatus(
|
||
_lblDatabase,
|
||
"Datenbank",
|
||
result.DatabaseReady,
|
||
result.DatabaseMessage);
|
||
|
||
SetStatus(
|
||
_lblSelection,
|
||
"Auswahl",
|
||
result.CredentialsReady,
|
||
result.CredentialsMessage);
|
||
|
||
_btnImportCertificate.Visible = !result.CertificateReady;
|
||
_btnFinish.Enabled = result.IsReady;
|
||
|
||
if (result.CertificateReady)
|
||
{
|
||
await LoadSystemsAsync();
|
||
await LoadSystemsListAsync();
|
||
await LoadContainersListAsync();
|
||
await LoadPathsListAsync();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(
|
||
this,
|
||
ex.Message,
|
||
"Einstellungen",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Error);
|
||
}
|
||
finally
|
||
{
|
||
SetRunning(false);
|
||
}
|
||
}
|
||
|
||
private async Task LoadSystemsListAsync()
|
||
{
|
||
try
|
||
{
|
||
_systems = await _coordinator.LoadSystemsAsync();
|
||
_lvSystems.Items.Clear();
|
||
|
||
foreach (SonicSystemOption system in _systems)
|
||
{
|
||
ListViewItem item = new(system.ConnectionName);
|
||
item.SubItems.Add(system.ManagementHost);
|
||
item.SubItems.Add(system.ManagementPort.ToString());
|
||
item.SubItems.Add(system.DomainName);
|
||
item.SubItems.Add(system.ConnectionProtocol);
|
||
item.SubItems.Add(system.ConnectionUrl);
|
||
item.Tag = system;
|
||
_lvSystems.Items.Add(item);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ShowError(ex.Message);
|
||
}
|
||
}
|
||
|
||
private async Task LoadContainersListAsync()
|
||
{
|
||
try
|
||
{
|
||
IReadOnlyList<ContainerOption> containers =
|
||
await _coordinator.LoadContainersAsync();
|
||
|
||
_lvContainers.Items.Clear();
|
||
|
||
foreach (ContainerOption container in containers)
|
||
{
|
||
ListViewItem item = new(container.CompanyCode);
|
||
item.SubItems.Add(container.ContainerName);
|
||
item.SubItems.Add(container.ContainerDisplayName ?? "—");
|
||
item.SubItems.Add(container.ConnectionName);
|
||
item.SubItems.Add(
|
||
string.IsNullOrWhiteSpace(container.CertificateCheckUrl)
|
||
? "—"
|
||
: container.CertificateCheckUrl);
|
||
item.SubItems.Add(container.RestartTimeoutSeconds + " s");
|
||
item.Tag = container;
|
||
_lvContainers.Items.Add(item);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ShowError(ex.Message);
|
||
}
|
||
}
|
||
|
||
private async Task LoadPathsListAsync()
|
||
{
|
||
try
|
||
{
|
||
IReadOnlyList<CertificateTargetOption> targets =
|
||
await _coordinator.LoadCertificateTargetsAsync();
|
||
|
||
_lvPaths.Items.Clear();
|
||
CertificateProbeService probe = new();
|
||
|
||
foreach (CertificateTargetOption target in targets)
|
||
{
|
||
ListViewItem item = new(target.CompanyCode);
|
||
item.SubItems.Add(target.ContainerName);
|
||
item.SubItems.Add(target.FullTargetPath);
|
||
item.SubItems.Add("…");
|
||
item.SubItems.Add(
|
||
target.BackupEnabled
|
||
? target.BackupDirectoryName
|
||
: "aus");
|
||
item.SubItems.Add(target.ConnectionName);
|
||
item.Tag = target;
|
||
_lvPaths.Items.Add(item);
|
||
|
||
CertificateProbeResult result = await Task.Run(
|
||
() => probe.Probe(target.FullTargetPath));
|
||
|
||
item.SubItems[3].Text = FormatProbeExpiry(result);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ShowError(ex.Message);
|
||
}
|
||
}
|
||
|
||
private async Task ProbeSelectedPathAsync()
|
||
{
|
||
if (_lvPaths.SelectedItems.Count == 0
|
||
|| _lvPaths.SelectedItems[0].Tag is not CertificateTargetOption target)
|
||
{
|
||
MessageBox.Show(
|
||
this,
|
||
"Bitte auswählen.",
|
||
"Pfad prüfen",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
return;
|
||
}
|
||
|
||
CertificateProbeService probe = new();
|
||
CertificateProbeResult result = probe.Probe(
|
||
target.FullTargetPath,
|
||
() => PromptCertificatePassword(this));
|
||
|
||
_lvPaths.SelectedItems[0].SubItems[3].Text =
|
||
FormatProbeExpiry(result);
|
||
|
||
MessageBoxIcon icon = result.Success && result.Certificate is not null
|
||
? (result.Certificate.IsCurrentlyValid
|
||
? MessageBoxIcon.Information
|
||
: MessageBoxIcon.Warning)
|
||
: MessageBoxIcon.Warning;
|
||
|
||
MessageBox.Show(
|
||
this,
|
||
result.Success ? "Pfad OK." : "Prüfung fehlgeschlagen.",
|
||
"Pfad prüfen",
|
||
MessageBoxButtons.OK,
|
||
icon);
|
||
}
|
||
|
||
private static string FormatProbeExpiry(CertificateProbeResult result)
|
||
{
|
||
if (result.Success && result.Certificate is not null)
|
||
{
|
||
return result.Certificate.ValidUntil
|
||
.ToLocalTime()
|
||
.ToString("dd.MM.yyyy HH:mm");
|
||
}
|
||
|
||
if (result.FileExists)
|
||
{
|
||
return "Kennwort nötig";
|
||
}
|
||
|
||
if (result.AccessDenied)
|
||
{
|
||
return CertificateProbeResult.AccessDeniedShortText;
|
||
}
|
||
|
||
return result.Success ? "fehlt" : "nicht erreichbar";
|
||
}
|
||
|
||
private static string? PromptCertificatePassword(IWin32Window owner)
|
||
{
|
||
using Form dialog = new()
|
||
{
|
||
Text = "PFX/P12-Kennwort",
|
||
FormBorderStyle = FormBorderStyle.FixedDialog,
|
||
StartPosition = FormStartPosition.CenterParent,
|
||
ClientSize = new Size(360, 140),
|
||
MaximizeBox = false,
|
||
MinimizeBox = false,
|
||
ShowInTaskbar = false,
|
||
BackColor = SettingsUi.Background,
|
||
ForeColor = SettingsUi.Text
|
||
};
|
||
|
||
Label label = new()
|
||
{
|
||
Text = "Kennwort für die Zertifikatsdatei:",
|
||
Location = new Point(16, 16),
|
||
AutoSize = true
|
||
};
|
||
|
||
TextBox txtPwd = new()
|
||
{
|
||
Location = new Point(16, 44),
|
||
Width = 320,
|
||
UseSystemPasswordChar = true
|
||
};
|
||
|
||
Button btnOk = SettingsUi.CreatePrimaryButton("OK");
|
||
btnOk.Location = new Point(196, 90);
|
||
btnOk.Size = new Size(140, 32);
|
||
btnOk.DialogResult = DialogResult.OK;
|
||
|
||
Button btnCancel = SettingsUi.CreateGhostButton("Abbrechen");
|
||
btnCancel.Location = new Point(100, 90);
|
||
btnCancel.Size = new Size(90, 32);
|
||
btnCancel.DialogResult = DialogResult.Cancel;
|
||
|
||
dialog.Controls.Add(label);
|
||
dialog.Controls.Add(txtPwd);
|
||
dialog.Controls.Add(btnOk);
|
||
dialog.Controls.Add(btnCancel);
|
||
dialog.AcceptButton = btnOk;
|
||
dialog.CancelButton = btnCancel;
|
||
|
||
return dialog.ShowDialog(owner) == DialogResult.OK
|
||
? txtPwd.Text
|
||
: null;
|
||
}
|
||
|
||
private async Task AddSystemAsync()
|
||
{
|
||
using AddSonicSystemForm dialog = new(_coordinator);
|
||
|
||
if (dialog.ShowDialog(this) != DialogResult.OK
|
||
|| dialog.CreatedSonicConnectionId is not int createdId)
|
||
{
|
||
return;
|
||
}
|
||
|
||
await AfterSystemSavedAsync(createdId);
|
||
}
|
||
|
||
private async Task EditSelectedSystemAsync()
|
||
{
|
||
if (_lvSystems.SelectedItems.Count == 0
|
||
|| _lvSystems.SelectedItems[0].Tag is not SonicSystemOption system)
|
||
{
|
||
MessageBox.Show(
|
||
this,
|
||
"Bitte auswählen.",
|
||
"System bearbeiten",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
return;
|
||
}
|
||
|
||
using AddSonicSystemForm dialog = new(_coordinator, system);
|
||
|
||
if (dialog.ShowDialog(this) != DialogResult.OK
|
||
|| dialog.CreatedSonicConnectionId is not int savedId)
|
||
{
|
||
return;
|
||
}
|
||
|
||
await AfterSystemSavedAsync(savedId);
|
||
}
|
||
|
||
private async Task DeleteSelectedSystemAsync()
|
||
{
|
||
if (_lvSystems.SelectedItems.Count == 0
|
||
|| _lvSystems.SelectedItems[0].Tag is not SonicSystemOption system)
|
||
{
|
||
MessageBox.Show(
|
||
this,
|
||
"Bitte auswählen.",
|
||
"System löschen",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
return;
|
||
}
|
||
|
||
DialogResult confirm = MessageBox.Show(
|
||
this,
|
||
"System entfernen?",
|
||
"System löschen",
|
||
MessageBoxButtons.YesNo,
|
||
MessageBoxIcon.Warning);
|
||
|
||
if (confirm != DialogResult.Yes)
|
||
{
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
await _coordinator.DeactivateSonicConnectionAsync(
|
||
system.SonicConnectionId);
|
||
|
||
await LoadSystemsListAsync();
|
||
await LoadContainersListAsync();
|
||
await LoadPathsListAsync();
|
||
await LoadSystemsAsync();
|
||
await RefreshAllAsync();
|
||
|
||
MessageBox.Show(
|
||
this,
|
||
"Entfernt.",
|
||
"System gelöscht",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ShowError(ex.Message);
|
||
}
|
||
}
|
||
|
||
private async Task AfterSystemSavedAsync(int systemId)
|
||
{
|
||
await LoadSystemsListAsync();
|
||
await LoadSystemsAsync();
|
||
await RefreshAllAsync();
|
||
|
||
bool visible = _lvSystems.Items
|
||
.Cast<ListViewItem>()
|
||
.Any(item =>
|
||
item.Tag is SonicSystemOption system
|
||
&& system.SonicConnectionId == systemId);
|
||
|
||
if (!visible)
|
||
{
|
||
ShowError("Speichern fehlgeschlagen.");
|
||
return;
|
||
}
|
||
|
||
SelectListItem(
|
||
_lvSystems,
|
||
item =>
|
||
item.Tag is SonicSystemOption system
|
||
&& system.SonicConnectionId == systemId);
|
||
|
||
MessageBox.Show(
|
||
this,
|
||
"Gespeichert.",
|
||
"OK",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
}
|
||
|
||
private async Task AddContainerAsync()
|
||
{
|
||
int? preferred = null;
|
||
|
||
if (_lvSystems.SelectedItems.Count > 0
|
||
&& _lvSystems.SelectedItems[0].Tag is SonicSystemOption system)
|
||
{
|
||
preferred = system.SonicConnectionId;
|
||
}
|
||
|
||
using AddContainerForm dialog = new(_coordinator, preferred);
|
||
|
||
if (dialog.ShowDialog(this) != DialogResult.OK
|
||
|| dialog.CreatedSonicContainerId is not int createdId)
|
||
{
|
||
return;
|
||
}
|
||
|
||
await AfterContainerSavedAsync(createdId);
|
||
}
|
||
|
||
private async Task EditSelectedContainerAsync()
|
||
{
|
||
if (_lvContainers.SelectedItems.Count == 0
|
||
|| _lvContainers.SelectedItems[0].Tag is not ContainerOption container)
|
||
{
|
||
MessageBox.Show(
|
||
this,
|
||
"Bitte auswählen.",
|
||
"Container bearbeiten",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
return;
|
||
}
|
||
|
||
using AddContainerForm dialog = new(
|
||
_coordinator,
|
||
container.SonicConnectionId,
|
||
container);
|
||
|
||
if (dialog.ShowDialog(this) != DialogResult.OK
|
||
|| dialog.CreatedSonicContainerId is not int savedId)
|
||
{
|
||
return;
|
||
}
|
||
|
||
await AfterContainerSavedAsync(savedId);
|
||
}
|
||
|
||
private async Task DeleteSelectedContainerAsync()
|
||
{
|
||
if (_lvContainers.SelectedItems.Count == 0
|
||
|| _lvContainers.SelectedItems[0].Tag is not ContainerOption container)
|
||
{
|
||
MessageBox.Show(
|
||
this,
|
||
"Bitte auswählen.",
|
||
"Container löschen",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
return;
|
||
}
|
||
|
||
DialogResult confirm = MessageBox.Show(
|
||
this,
|
||
"Container entfernen?",
|
||
"Container löschen",
|
||
MessageBoxButtons.YesNo,
|
||
MessageBoxIcon.Warning);
|
||
|
||
if (confirm != DialogResult.Yes)
|
||
{
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
await _coordinator.DeactivateSonicContainerAsync(
|
||
container.SonicContainerId);
|
||
|
||
await LoadContainersListAsync();
|
||
await LoadPathsListAsync();
|
||
await RefreshAllAsync();
|
||
|
||
MessageBox.Show(
|
||
this,
|
||
"Entfernt.",
|
||
"Container gelöscht",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ShowError(ex.Message);
|
||
}
|
||
}
|
||
|
||
private async Task AfterContainerSavedAsync(int containerId)
|
||
{
|
||
await LoadContainersListAsync();
|
||
await LoadSystemsListAsync();
|
||
await RefreshAllAsync();
|
||
|
||
bool visible = _lvContainers.Items
|
||
.Cast<ListViewItem>()
|
||
.Any(item =>
|
||
item.Tag is ContainerOption container
|
||
&& container.SonicContainerId == containerId);
|
||
|
||
if (!visible)
|
||
{
|
||
ShowError("Speichern fehlgeschlagen.");
|
||
return;
|
||
}
|
||
|
||
SelectListItem(
|
||
_lvContainers,
|
||
item =>
|
||
item.Tag is ContainerOption container
|
||
&& container.SonicContainerId == containerId);
|
||
|
||
MessageBox.Show(
|
||
this,
|
||
"Gespeichert.",
|
||
"OK",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
}
|
||
|
||
private async Task AddPathAsync()
|
||
{
|
||
int? preferred = null;
|
||
|
||
if (_lvContainers.SelectedItems.Count > 0
|
||
&& _lvContainers.SelectedItems[0].Tag is ContainerOption container)
|
||
{
|
||
preferred = container.SonicContainerId;
|
||
}
|
||
|
||
using AddTargetPathForm dialog = new(_coordinator, preferred);
|
||
|
||
if (dialog.ShowDialog(this) != DialogResult.OK
|
||
|| dialog.CreatedCertificateTargetId is not int createdId)
|
||
{
|
||
return;
|
||
}
|
||
|
||
await AfterPathSavedAsync(createdId);
|
||
}
|
||
|
||
private async Task EditSelectedPathAsync()
|
||
{
|
||
if (_lvPaths.SelectedItems.Count == 0
|
||
|| _lvPaths.SelectedItems[0].Tag is not CertificateTargetOption target)
|
||
{
|
||
MessageBox.Show(
|
||
this,
|
||
"Bitte auswählen.",
|
||
"Pfad bearbeiten",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
return;
|
||
}
|
||
|
||
using AddTargetPathForm dialog = new(
|
||
_coordinator,
|
||
target.SonicContainerId,
|
||
target);
|
||
|
||
if (dialog.ShowDialog(this) != DialogResult.OK
|
||
|| dialog.CreatedCertificateTargetId is not int savedId)
|
||
{
|
||
return;
|
||
}
|
||
|
||
await AfterPathSavedAsync(savedId);
|
||
}
|
||
|
||
private async Task AfterPathSavedAsync(int pathId)
|
||
{
|
||
await LoadPathsListAsync();
|
||
await RefreshAllAsync();
|
||
|
||
bool visible = _lvPaths.Items
|
||
.Cast<ListViewItem>()
|
||
.Any(item =>
|
||
item.Tag is CertificateTargetOption target
|
||
&& target.CertificateTargetId == pathId);
|
||
|
||
if (!visible)
|
||
{
|
||
ShowError("Speichern fehlgeschlagen.");
|
||
return;
|
||
}
|
||
|
||
SelectListItem(
|
||
_lvPaths,
|
||
item =>
|
||
item.Tag is CertificateTargetOption target
|
||
&& target.CertificateTargetId == pathId);
|
||
|
||
MessageBox.Show(
|
||
this,
|
||
"Gespeichert.",
|
||
"OK",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
}
|
||
|
||
private async Task DeleteSelectedPathAsync()
|
||
{
|
||
if (_lvPaths.SelectedItems.Count == 0
|
||
|| _lvPaths.SelectedItems[0].Tag is not CertificateTargetOption target)
|
||
{
|
||
MessageBox.Show(
|
||
this,
|
||
"Bitte auswählen.",
|
||
"Pfad löschen",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
return;
|
||
}
|
||
|
||
DialogResult confirm = MessageBox.Show(
|
||
this,
|
||
"Pfad entfernen?",
|
||
"Pfad löschen",
|
||
MessageBoxButtons.YesNo,
|
||
MessageBoxIcon.Warning);
|
||
|
||
if (confirm != DialogResult.Yes)
|
||
{
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
await _coordinator.DeactivateCertificateTargetAsync(
|
||
target.CertificateTargetId);
|
||
|
||
await LoadPathsListAsync();
|
||
await RefreshAllAsync();
|
||
|
||
MessageBox.Show(
|
||
this,
|
||
"Entfernt.",
|
||
"Pfad gelöscht",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ShowError(ex.Message);
|
||
}
|
||
}
|
||
|
||
private static void SelectListItem(
|
||
ListView listView,
|
||
Func<ListViewItem, bool> match)
|
||
{
|
||
foreach (ListViewItem item in listView.Items)
|
||
{
|
||
if (!match(item))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
item.Selected = true;
|
||
item.EnsureVisible();
|
||
listView.Focus();
|
||
return;
|
||
}
|
||
}
|
||
|
||
private async Task LoadSystemsAsync()
|
||
{
|
||
LocalSetupSelection? previousSelection =
|
||
_coordinator.LoadSelection();
|
||
|
||
_systems = await _coordinator.LoadSystemsAsync();
|
||
|
||
_cmbSystem.Items.Clear();
|
||
_cmbCredential.Items.Clear();
|
||
|
||
foreach (SonicSystemOption system in _systems)
|
||
{
|
||
_cmbSystem.Items.Add(system);
|
||
}
|
||
|
||
if (_systems.Count == 0)
|
||
{
|
||
_lblSystemDetails.Text =
|
||
"Kein System vorhanden. Lege zuerst unter „Systeme“ eines an.";
|
||
return;
|
||
}
|
||
|
||
int selectedIndex = 0;
|
||
|
||
if (previousSelection is not null)
|
||
{
|
||
for (int index = 0; index < _systems.Count; index++)
|
||
{
|
||
if (_systems[index].SonicConnectionId
|
||
== previousSelection.SonicConnectionId)
|
||
{
|
||
selectedIndex = index;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
_cmbSystem.SelectedIndex = selectedIndex;
|
||
}
|
||
|
||
private async Task AddCredentialAsync()
|
||
{
|
||
if (_cmbSystem.SelectedItem is not SonicSystemOption system)
|
||
{
|
||
ShowWarning("Bitte auswählen.");
|
||
return;
|
||
}
|
||
|
||
using AddCredentialProfileForm dialog =
|
||
new(_coordinator, system);
|
||
|
||
if (dialog.ShowDialog(this) != DialogResult.OK
|
||
|| dialog.CreatedProfile is null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
await SystemChangedAsync();
|
||
SelectCredential(dialog.CreatedProfile.SonicCredentialId);
|
||
|
||
MessageBox.Show(
|
||
this,
|
||
"Profil geladen.",
|
||
"Benutzerprofil",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
}
|
||
|
||
private async Task EditCredentialAsync()
|
||
{
|
||
if (_cmbSystem.SelectedItem is not SonicSystemOption system)
|
||
{
|
||
ShowWarning("Bitte System wählen.");
|
||
return;
|
||
}
|
||
|
||
if (_cmbCredential.SelectedItem is not SonicCredentialProfile profile)
|
||
{
|
||
ShowWarning("Bitte Profil wählen.");
|
||
return;
|
||
}
|
||
|
||
using AddCredentialProfileForm dialog =
|
||
new(_coordinator, system, profile);
|
||
|
||
if (dialog.ShowDialog(this) != DialogResult.OK
|
||
|| dialog.CreatedProfile is null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
await SystemChangedAsync();
|
||
SelectCredential(dialog.CreatedProfile.SonicCredentialId);
|
||
|
||
MessageBox.Show(
|
||
this,
|
||
"Profil aktualisiert.",
|
||
"Benutzerprofil",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
}
|
||
|
||
private void SelectCredential(int sonicCredentialId)
|
||
{
|
||
for (int index = 0; index < _cmbCredential.Items.Count; index++)
|
||
{
|
||
if (_cmbCredential.Items[index] is SonicCredentialProfile profile
|
||
&& profile.SonicCredentialId == sonicCredentialId)
|
||
{
|
||
_cmbCredential.SelectedIndex = index;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
private async Task SystemChangedAsync()
|
||
{
|
||
if (_cmbSystem.SelectedItem is not SonicSystemOption system)
|
||
{
|
||
return;
|
||
}
|
||
|
||
_lblSystemDetails.Text =
|
||
$"Domain: {system.DomainName} · {system.ConnectionUrl}";
|
||
|
||
_profiles =
|
||
await _coordinator.LoadCredentialsAsync(
|
||
system.SonicConnectionId);
|
||
|
||
_cmbCredential.Items.Clear();
|
||
|
||
foreach (SonicCredentialProfile profile in _profiles)
|
||
{
|
||
_cmbCredential.Items.Add(profile);
|
||
}
|
||
|
||
if (_profiles.Count == 0)
|
||
{
|
||
_lblSystemDetails.Text +=
|
||
Environment.NewLine
|
||
+ "Noch kein Benutzerprofil — bitte hinzufügen.";
|
||
_btnSaveSelection.Enabled = false;
|
||
_btnEditCredential.Enabled = false;
|
||
return;
|
||
}
|
||
|
||
_btnEditCredential.Enabled = true;
|
||
|
||
_btnSaveSelection.Enabled = true;
|
||
|
||
LocalSetupSelection? selection = _coordinator.LoadSelection();
|
||
int selectedIndex = 0;
|
||
|
||
if (selection is not null)
|
||
{
|
||
for (int index = 0; index < _profiles.Count; index++)
|
||
{
|
||
if (_profiles[index].SonicCredentialId
|
||
== selection.SonicCredentialId)
|
||
{
|
||
selectedIndex = index;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
selectedIndex = _profiles
|
||
.Select((profile, index) => new { profile, index })
|
||
.Where(item => item.profile.IsDefault)
|
||
.Select(item => item.index)
|
||
.FirstOrDefault();
|
||
}
|
||
|
||
_cmbCredential.SelectedIndex = selectedIndex;
|
||
}
|
||
|
||
private async Task SaveSelectionAsync()
|
||
{
|
||
if (_cmbSystem.SelectedItem is not SonicSystemOption system)
|
||
{
|
||
ShowWarning("Bitte System wählen.");
|
||
return;
|
||
}
|
||
|
||
if (_cmbCredential.SelectedItem is not SonicCredentialProfile profile)
|
||
{
|
||
ShowWarning("Bitte Profil wählen.");
|
||
return;
|
||
}
|
||
|
||
_coordinator.SaveSelection(system, profile);
|
||
|
||
MessageBox.Show(
|
||
this,
|
||
"Gespeichert.",
|
||
"Einstellungen",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
|
||
await RefreshAllAsync();
|
||
}
|
||
|
||
private async Task ImportCertificateAsync()
|
||
{
|
||
using OpenFileDialog dialog = new()
|
||
{
|
||
Title = "Always-Encrypted-PFX auswählen",
|
||
Filter = "PFX-Zertifikat (*.pfx)|*.pfx",
|
||
CheckFileExists = true
|
||
};
|
||
|
||
if (dialog.ShowDialog(this) != DialogResult.OK)
|
||
{
|
||
return;
|
||
}
|
||
|
||
string? password = PromptForPassword();
|
||
|
||
if (password is null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
_coordinator.CertificateService.ImportPfx(
|
||
dialog.FileName,
|
||
password);
|
||
|
||
await RefreshAllAsync();
|
||
}
|
||
finally
|
||
{
|
||
password = string.Empty;
|
||
}
|
||
}
|
||
|
||
private async Task CloseAsync()
|
||
{
|
||
if (_isSettingsMode)
|
||
{
|
||
// Katalog-Änderungen (Pfade/Container) sollen die Hauptansicht
|
||
// immer aktualisieren – unabhängig davon, ob das Profil schon „ready“ ist.
|
||
_ = await _coordinator.CheckAsync();
|
||
DialogResult = DialogResult.OK;
|
||
}
|
||
else
|
||
{
|
||
DialogResult = DialogResult.Cancel;
|
||
}
|
||
|
||
Close();
|
||
}
|
||
|
||
private static string? PromptForPassword()
|
||
{
|
||
using Form form = new()
|
||
{
|
||
Text = "PFX-Kennwort",
|
||
Size = new Size(390, 170),
|
||
StartPosition = FormStartPosition.CenterParent,
|
||
FormBorderStyle = FormBorderStyle.FixedDialog,
|
||
BackColor = SettingsUi.Background,
|
||
ForeColor = SettingsUi.Text
|
||
};
|
||
|
||
TextBox textBox = SettingsUi.CreateTextBox();
|
||
textBox.Location = new Point(20, 40);
|
||
textBox.Width = 330;
|
||
textBox.UseSystemPasswordChar = true;
|
||
|
||
Button ok = SettingsUi.CreatePrimaryButton("OK");
|
||
ok.Location = new Point(160, 85);
|
||
ok.Size = new Size(90, 34);
|
||
ok.DialogResult = DialogResult.OK;
|
||
|
||
Button cancel = SettingsUi.CreateGhostButton("Abbrechen");
|
||
cancel.Location = new Point(260, 85);
|
||
cancel.Size = new Size(90, 34);
|
||
cancel.DialogResult = DialogResult.Cancel;
|
||
|
||
form.Controls.Add(textBox);
|
||
form.Controls.Add(ok);
|
||
form.Controls.Add(cancel);
|
||
form.AcceptButton = ok;
|
||
form.CancelButton = cancel;
|
||
|
||
return form.ShowDialog() == DialogResult.OK
|
||
? textBox.Text
|
||
: null;
|
||
}
|
||
|
||
private void SetRunning(bool running)
|
||
{
|
||
_running = running;
|
||
UseWaitCursor = running;
|
||
}
|
||
|
||
private void ShowWarning(string message)
|
||
{
|
||
MessageBox.Show(
|
||
this,
|
||
message,
|
||
"Einstellungen",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Warning);
|
||
}
|
||
|
||
private void ShowError(string message)
|
||
{
|
||
MessageBox.Show(
|
||
this,
|
||
message,
|
||
"Einstellungen",
|
||
MessageBoxButtons.OK,
|
||
MessageBoxIcon.Error);
|
||
}
|
||
|
||
private static void SetStatus(
|
||
Label label,
|
||
string category,
|
||
bool success,
|
||
string message)
|
||
{
|
||
label.Text =
|
||
$"{(success ? "●" : "○")} {category}\n {message}";
|
||
|
||
label.ForeColor = success
|
||
? SettingsUi.Green
|
||
: SettingsUi.Red;
|
||
}
|
||
}
|