<?phpnamespace App\Form;use App\Feature\Pools\PoolCommands;use App\Model\Connection;use App\Model\Session;use JumPHP\Gui\Button;use JumPHP\Gui\CheckBox;use JumPHP\Gui\ComboBox;use JumPHP\Gui\Container;use JumPHP\Gui\Form;use JumPHP\Gui\Label;use JumPHP\Gui\TextBox;/** * New Pool (mode 'new': a name, a master among the connected standalone servers, the others that join it) and Add Server (mode * 'add': servers that join the selected pool), as XenCenter's New Pool wizard and Add Server menu. Closes with * ['newPool' => [name, description, master, members]] or ['joinPool' => [pool, members]] (servers by their profile key); * Feature\Pools\PoolActions makes the XAPI calls. * * @property Label $heading * @property Label $where * @property Label $nameLabel * @property TextBox $name * @property Label $descriptionLabel * @property TextBox $description * @property Label $masterLabel * @property ComboBox $master * @property Label $membersLabel * @property Container $servers * @property Label $note * @property Label $error * @property Button $ok * @property Button $cancel */final class NewPoolForm extends Form{ public function __construct(private Session $session) { parent::__construct(); } /** XAPI's answers, events and the console go on while this dialog is open. */ protected function tick(float $dt): void { $this->session->tick($dt); } private array $params = []; /** @var array<string, CheckBox> server key => its box */ public array $boxes = []; /** @return list<array{key: string, name: string, address: string, reason: string}> */ private static function servers(Session $session, ?Connection $except = null): array { return array_map(fn (Connection $c) => ['key' => $c->profile->key(), 'name' => $c->name(), 'address' => $c->profile->host, 'reason' => PoolCommands::cannotJoin($c)], PoolCommands::standalone($session, $except)); } /** @return array<string, mixed> */ public static function paramsForNew(Session $session): array { return ['mode' => 'new', 'servers' => self::servers($session)]; } /** @return array<string, mixed> */ public static function paramsForAdd(Session $session, Connection $pool): array { return ['mode' => 'add', 'pool' => $pool->profile->key(), 'poolName' => $pool->name(), 'servers' => self::servers($session, $pool)]; } protected function loaded(array $params = []): void { $this->params = $params; $new = ($params['mode'] ?? 'new') === 'new'; $this->heading->text = $new ? 'New Pool' : "Add Server to pool '" . ($params['poolName'] ?? '') . "'"; $this->where->text = $new ? 'A pool shares storage and networks between servers; VMs move between them.' : 'The servers below are connected and in no pool.'; foreach ([$this->nameLabel, $this->name, $this->descriptionLabel, $this->description, $this->masterLabel, $this->master] as $w) { $w->visible = $new; } $this->name->text = ''; $this->description->text = ''; $this->error->text = ''; $this->ok->text = $new ? 'Create Pool' : 'Add'; $servers = (array)($params['servers'] ?? []); $this->master->items = array_map(fn (array $s) => $s['name'] . ' (' . $s['address'] . ')', $servers); $this->master->selected = $servers === [] ? -1 : 0; $this->membersLabel->text = $new ? 'Other servers that join the pool:' : 'Servers that join the pool:'; $this->boxes(); $this->setFocus($new ? $this->name : null); } /** The master's server key (mode 'new'). */ private function masterKey(): ?string { $servers = (array)($this->params['servers'] ?? []); return ($this->params['mode'] ?? 'new') === 'new' ? ($servers[$this->master->selected]['key'] ?? null) : null; } /** One box per server that may join (not the master); a server that cannot says why. */ private function boxes(): void { $checked = array_map(fn (CheckBox $b) => (bool)$b->checked, $this->boxes); $this->servers->clear(); $this->boxes = []; $master = $this->masterKey(); foreach ((array)($this->params['servers'] ?? []) as $s) { if ($s['key'] === $master) { continue; } $box = new CheckBox(['text' => $s['name'] . ' (' . $s['address'] . ')' . ($s['reason'] !== '' ? ' — cannot join: ' . $s['reason'] : ''), 'checked' => $s['reason'] === '' && ($checked[$s['key']] ?? ($this->params['mode'] ?? 'new') === 'add'), 'enabled' => $s['reason'] === '']); $this->boxes[$s['key']] = $box; $this->servers->add($box); } if ($this->boxes === []) { $this->servers->add(new Label(['text' => ($this->params['mode'] ?? 'new') === 'new' ? 'No other server: the pool has the master alone (add servers later).' : 'No connected server is in no pool: add one first (File > Add New Server…).', 'style' => 'muted', 'wrap' => true])); } } public function onMasterChange(ComboBox $sender, int $index, string $text): void { $this->boxes(); } /** @return list<string> the keys of the ticked servers */ public function members(): array { return array_keys(array_filter($this->boxes, fn (CheckBox $b) => (bool)$b->checked && $b->isEnabled())); } public function onOkClick(Button $sender): void { if (($this->params['mode'] ?? 'new') === 'add') { if ($this->members() === []) { $this->error->text = 'Tick at least one server.'; return; } $this->close(['joinPool' => ['pool' => (string)$this->params['pool'], 'members' => $this->members()]]); return; } if (trim($this->name->text) === '') { $this->error->text = 'Enter a name for the pool.'; return; } if ($this->masterKey() === null) { $this->error->text = 'Choose the master.'; return; } $this->close(['newPool' => ['name' => trim($this->name->text), 'description' => trim($this->description->text), 'master' => $this->masterKey(), 'members' => $this->members()]]); } public function onCancelClick(Button $sender): void { $this->close(); }}