JA
<?phpnamespace App\Feature\Storage;use App\Form\ConfirmForm;use App\Form\CrossPoolMigrateForm;use App\Form\DiskForm;use App\Form\MainForm;use App\Form\MoveDiskForm;use App\Form\NetworkPropertiesForm;use App\Form\NewNetworkForm;use App\Form\NewSrForm;use App\Form\StrongConfirmForm;use App\Model\Connection;use App\Model\ServerProfile;use App\Xapi\FakeXapi;use JumPHP\Async\Task;use JumPHP\Kernel\Engine;/** * The commands of these features, run by MainForm::command() (one call at its start) and the results of their dialogs, given to * MainForm::resume() (one call too). Commands: sr.new / detach / reattach / forget / destroy / setDefault, network.new / properties / * remove, disk.add / attach / move / detach / delete, vm.crossPoolMigrate, server.demo2. */final class Router{    public const SECOND_DEMO = 'xcp-lab.local';    private const IDS = ['sr.new', 'sr.detach', 'sr.reattach', 'sr.forget', 'sr.destroy', 'sr.setDefault', 'network.new', 'network.properties', 'network.remove',        'disk.add', 'disk.attach', 'disk.move', 'disk.detach', 'disk.delete', 'vm.crossPoolMigrate'];    /** @return bool true when the command was one of these (done, or a dialog opened) */    public static function command(MainForm $form, string $id, ?Connection $c, string $target, bool $confirmed): bool    {        if ($id === 'server.demo2') {            self::openSecondDemo($form);            return true;        }        if ($id === 'object.properties' && $c?->cache->classOf($target) === 'network') {            $id = 'network.properties';        }        if (!in_array($id, self::IDS, true)) {            return false;        }        if ($c === null || !$c->isConnected()) {            return true;        }        $cache = $c->cache;        $scenes = Engine::get()->scenes;        [, $selected] = $form->selection();        switch ($id) {            case 'sr.new':                $scenes->push(NewSrForm::class, NewSrForm::paramsFor($c, $cache->classOf($target) === 'host' ? $target : null));                return true;            case 'network.new':                $scenes->push(NewNetworkForm::class, NewNetworkForm::paramsFor($c));                return true;            case 'network.properties':                if ($cache->classOf($target) === 'network') {                    $scenes->push(NetworkPropertiesForm::class, NetworkPropertiesForm::paramsFor($c, $target));                }                return true;            case 'disk.add':            case 'disk.attach':                if ($cache->classOf($target) === 'vm') {                    $scenes->push(DiskForm::class, DiskForm::paramsFor($c, $target, $id === 'disk.add' ? 'add' : 'attach'));                }                return true;            case 'disk.move':                $vdi = $cache->classOf($target) === 'vbd' ? (string)$cache->field($target, 'VDI', '') : $target;                if ($cache->classOf($vdi) === 'vdi') {                    $scenes->push(MoveDiskForm::class, MoveDiskForm::paramsFor($c, $vdi));                }                return true;            case 'vm.crossPoolMigrate':                if (Menus::canCrossPool($c, $target)) {                    $scenes->push(CrossPoolMigrateForm::class, CrossPoolMigrateForm::paramsFor($form->session, $c, $target));                }                return true;        }        if (!$confirmed && ($ask = self::question($id, $c, $target)) !== null) {            [$message, $ok, $strong] = $ask;            $scenes->push($strong ? StrongConfirmForm::class : ConfirmForm::class, ['message' => $message, 'command' => $id, 'target' => $target, 'ok' => $ok,                'check' => $strong ? 'I understand: this cannot be undone.' : '']);            return true;        }        $name = $cache->name($target);        $task = match ($id) {            'sr.detach' => Actions::detachSr($c, $target),            'sr.reattach' => Actions::reattachSr($c, $target),            'sr.forget' => Actions::removeSr($c, $target, false),            'sr.destroy' => Actions::removeSr($c, $target, true),            'sr.setDefault' => Actions::setDefaultSr($c, $target),            'network.remove' => Actions::removeNetwork($c, $target),            'disk.detach' => Actions::detachDisk($c, $target, false),            'disk.delete' => Actions::detachDisk($c, $target, true),        };        $gone = in_array($id, ['sr.forget', 'sr.destroy', 'network.remove'], true) && $selected === $target;        $task->then(function () use ($form, $c, $gone): void {            if ($gone) {                // the object shown is no more: its pool                $form->navigate((string)($c->cache->isRealPool() ? $c->cache->poolRef() : $c->cache->master()));            }        }, fn () => null);        if ($name !== '') {            $form->refresh();        }        return true;    }    /** @return array{0: string, 1: string, 2: bool}|null the question, the button, a strong confirmation (a box to tick) */    private static function question(string $id, Connection $c, string $target): ?array    {        $name = $c->cache->name($target);        $disk = $c->cache->name((string)$c->cache->field($target, 'VDI', ''));        return match ($id) {            'sr.detach' => ["Detach the storage repository '$name'?\n\nIts virtual disks cannot be used until it is reattached. The VMs that use them must be shut down first.", 'Detach', false],            'sr.forget' => ["Forget the storage repository '$name'?\n\nXenCenter removes it from the pool: the data stays on the storage, but the VMs lose their disks on it. It can be introduced again later.", 'Forget', true],            'sr.destroy' => ["Destroy the storage repository '$name'?\n\nEvery virtual disk on it is DELETED, for good: the VMs lose them and the data cannot be recovered.", 'Destroy', true],            'network.remove' => ["Remove the network '$name'?\n\nIts VLANs and bonds are removed on every server.", 'Remove', false],            'disk.detach' => ["Detach the virtual disk '$disk' from VM '" . $c->cache->name((string)$c->cache->field($target, 'VM', '')) . "'?\n\nThe disk stays on its storage repository.", 'Detach', false],            'disk.delete' => ["Delete the virtual disk '$disk'?\n\nIts data is lost. This cannot be undone.", 'Delete', false],            default => null,        };    }    /** What the dialogs of these features answered. @param array<string, mixed> $result */    public static function resume(MainForm $form, array $result): bool    {        $session = $form->session;        $task = match (true) {            isset($result['newSr']) => Actions::createSr($session, (array)$result['newSr']),            isset($result['newNetwork']) => Actions::createNetwork($session, (array)$result['newNetwork']),            isset($result['networkProperties']) => Actions::networkProperties($session, (array)$result['networkProperties']),            isset($result['moveDisk']) => Actions::moveDisk($session, (string)$result['moveDisk']['vdi'], (string)$result['moveDisk']['sr']),            isset($result['addDisk']) => Actions::addDisk($session, (array)$result['addDisk']),            isset($result['attachDisk']) => Actions::attachDisk($session, (string)$result['attachDisk']['vm'], (string)$result['attachDisk']['vdi']),            isset($result['crossPool']) => Actions::crossPoolMigrate($session, (array)$result['crossPool']),            default => null,        };        if (!$task instanceof Task) {            return false;        }        $task->then(fn () => null, fn () => null);        $form->refresh(true);        return true;    }    /** File > Open the Second Demo Pool: another FakeXapi ("Demo pool 2"), to try cross-pool migration. */    public static function openSecondDemo(MainForm $form): Connection    {        foreach ($form->session->connections as $c) {            if ($c->profile->method === 'demo' && strtolower($c->profile->host) === self::SECOND_DEMO) {                if (!$c->isConnected() && $c->state !== Connection::CONNECTING) {                    $form->connect($c);                }                return $c;            }        }        $c = $form->session->add(new ServerProfile(self::SECOND_DEMO, FakeXapi::USER, FakeXapi::PASSWORD, 'demo', 0, true, 'root', 22, '', 'Demo pool 2'));        $form->connect($c);        return $c;    }}