using Sunfish.Auth;
using Sunfish.Config;
namespace Sunfish.WinForms;
/// <summary>Usuarios y contrasenas (sobre una copia de la configuracion), compacto como la ventana principal.</summary>
internal sealed class UsersDialog : Form
{
private readonly ListBox _list = new() { Dock = DockStyle.Fill, IntegralHeight = false, Margin = new Padding(0, 0, 0, 6) };
private readonly ToolTip _tips = new();
public SunfishConfig Config { get; }
public bool Changed { get; private set; }
public UsersDialog(SunfishConfig config)
{
Config = config;
SuspendLayout();
AutoScaleDimensions = new SizeF(96F, 96F);
AutoScaleMode = AutoScaleMode.Dpi;
Text = Texts.UsersTitle;
FormBorderStyle = FormBorderStyle.SizableToolWindow;
ShowInTaskbar = false;
StartPosition = FormStartPosition.CenterParent;
ClientSize = new Size(210, 200);
MinimumSize = new Size(200, 180);
Padding = new Padding(6);
var add = UiImages.IconButton("plus", Texts.Add, _tips);
var delete = UiImages.IconButton("minus", Texts.Delete, _tips);
var password = UiImages.IconButton("pencil", Texts.Password, _tips);
var close = new Button { Text = Texts.Close, Size = new Size(70, 26), DialogResult = DialogResult.OK, UseVisualStyleBackColor = true };
AcceptButton = close;
CancelButton = close;
// Lista arriba; abajo los botones de imagen a la izquierda y Cerrar a la derecha.
var grid = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 5, RowCount = 2, Margin = new Padding(0) };
for (int i = 0; i < 3; i++)
grid.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
grid.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
grid.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
grid.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
grid.RowStyles.Add(new RowStyle(SizeType.AutoSize));
grid.Controls.Add(_list, 0, 0);
grid.SetColumnSpan(_list, 5);
add.Margin = delete.Margin = password.Margin = new Padding(0, 0, 3, 0);
close.Margin = new Padding(0);
grid.Controls.Add(add, 0, 1);
grid.Controls.Add(delete, 1, 1);
grid.Controls.Add(password, 2, 1);
grid.Controls.Add(close, 4, 1);
Controls.Add(grid);
ResumeLayout(false);
PerformLayout();
add.Click += (_, _) => AddUser();
password.Click += (_, _) => ChangePassword();
delete.Click += (_, _) => DeleteUser();
_list.DoubleClick += (_, _) => ChangePassword();
Fill();
}
private void Fill()
{
_list.Items.Clear();
foreach (var user in Config.Users)
_list.Items.Add(user.Name);
}
private void AddUser()
{
using var dialog = new PasswordDialog(Texts.NewUser, askName: true);
if (dialog.ShowDialog(this) != DialogResult.OK)
return;
string name = dialog.UserName;
if (!ShareSettings.IsValidName(name))
{
MessageBox.Show(this, Strings.ErrorBadName(name), Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
if (Config.Users.Any(u => u.Name.Equals(name, StringComparison.OrdinalIgnoreCase)))
{
MessageBox.Show(this, Strings.ErrorNameInUse(name), Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
Config.Users.Add(new UserConfig { Name = name, PasswordHash = Passwords.Hash(dialog.Password) });
Changed = true;
Fill();
}
private void ChangePassword()
{
if (_list.SelectedItem is not string name)
return;
using var dialog = new PasswordDialog(Texts.PasswordFor(name), askName: false);
if (dialog.ShowDialog(this) != DialogResult.OK)
return;
Config.Users.First(u => u.Name == name).PasswordHash = Passwords.Hash(dialog.Password);
Changed = true;
}
private void DeleteUser()
{
if (_list.SelectedItem is not string name)
return;
if (MessageBox.Show(this, Strings.AskDeleteUser(name), Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
return;
Config.Users.RemoveAll(u => u.Name == name);
Changed = true;
Fill();
}
}
/// <summary>Pide (opcionalmente un nombre y) una contrasena dos veces; etiquetas encima, como en la 2.0.</summary>
internal sealed class PasswordDialog : Form
{
private readonly TextBox _name = new() { Width = 200, BackColor = Color.FromArgb(255, 252, 220) };
private readonly TextBox _password = new() { Width = 200, UseSystemPasswordChar = true };
private readonly TextBox _repeat = new() { Width = 200, UseSystemPasswordChar = true };
private readonly bool _askName;
public string UserName => _name.Text.Trim();
public string Password => _password.Text;
public PasswordDialog(string title, bool askName, bool askRepeat = true)
{
_askName = askName;
SuspendLayout();
AutoScaleDimensions = new SizeF(96F, 96F);
AutoScaleMode = AutoScaleMode.Dpi;
Text = title;
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = MinimizeBox = false;
ShowInTaskbar = false;
StartPosition = FormStartPosition.CenterParent;
AutoSize = true;
AutoSizeMode = AutoSizeMode.GrowAndShrink;
Padding = new Padding(10, 6, 10, 10);
var main = new FlowLayoutPanel { FlowDirection = FlowDirection.TopDown, AutoSize = true, WrapContents = false };
if (askName)
{
main.Controls.Add(Caption(Texts.UserName));
main.Controls.Add(_name);
}
main.Controls.Add(Caption(Texts.PasswordLabel));
main.Controls.Add(_password);
if (askRepeat)
{
main.Controls.Add(Caption(Texts.RepeatLabel));
main.Controls.Add(_repeat);
}
var ok = new Button { Text = Texts.Ok, Size = new Size(70, 25), UseVisualStyleBackColor = true };
ok.Click += (_, _) => Accept();
var cancel = new Button { Text = Texts.Cancel, Size = new Size(70, 25), DialogResult = DialogResult.Cancel, UseVisualStyleBackColor = true };
var buttons = new FlowLayoutPanel { FlowDirection = FlowDirection.RightToLeft, Width = 206, Height = 32, Margin = new Padding(0, 10, 0, 0) };
buttons.Controls.AddRange([cancel, ok]);
main.Controls.Add(buttons);
AcceptButton = ok;
CancelButton = cancel;
Controls.Add(main);
ResumeLayout(false);
PerformLayout();
}
private static Label Caption(string text) =>
new() { Text = text, AutoSize = true, Margin = new Padding(3, 6, 3, 1) };
private void Accept()
{
if (_askName && UserName.Length == 0)
return;
if (_password.Text.Length == 0)
{
MessageBox.Show(this, Strings.ErrorPasswordEmpty, Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
if (_repeat.Parent is not null && _password.Text != _repeat.Text)
{
MessageBox.Show(this, Texts.PasswordMismatch, Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
DialogResult = DialogResult.OK;
}
}