Anmelden
DE
games/xencenter-xenadmin-xcp-jumphp / src / Feature / Organize / OrganizeCommands.php · commit fb1b7c9
<?phpnamespace App\Feature\Organize;use App\Form\ConfirmForm;use App\Form\FolderForm;use App\Form\MainForm;use App\Form\TagsForm;use App\Gui\Icons;use App\Model\Connection;use App\Model\Session;use App\View\Commands;use App\View\Page;use JumPHP\Kernel\Engine;/** * The commands of the Organization views ("organize.*"): Move to Folder…, Edit Tags… on an object; New Folder, Rename, Delete on a * folder; Rename / Delete on a tag. MainForm hands them over here (command(), resume(), the right click's menu, the page shown). */final class OrganizeCommands{    /** The kinds of the tree that can be put in a folder and tagged. */    public const KINDS = ['pool', 'host', 'vm', 'template', 'sr', 'network'];    /**     * The right click's menu: a folder's or a tag's own, else the object's ($items, from Commands::context) with Move to Folder… and     * Edit Tags… before its Properties.     * @param list<array<string, mixed>|string> $items @return list<array<string, mixed>|string>     */    public static function context(string $ref, string $kind, array $items): array    {        if (OrganizeTree::isFolder($ref)) {            return [Commands::item('organize.newFolder', 'New Folder…', true, 'folder'), Commands::item('organize.renameFolder', 'Rename Folder…'),                '-', Commands::item('organize.deleteFolder', 'Delete Folder…')];        }        if (OrganizeTree::isTag($ref)) {            return [Commands::item('organize.renameTag', 'Rename Tag…'), Commands::item('organize.deleteTag', 'Delete Tag…')];        }        if ($ref === \App\View\TreeBuilder::ROOT && $kind === 'root') {            return [...$items, '-', Commands::item('organize.newFolder', 'New Folder…', true, 'folder')];        }        if (!in_array($kind, self::KINDS, true)) {            return $items;        }        $mine = [Commands::item('organize.move', 'Move to Folder…', true, 'folder'), Commands::item('organize.tags', 'Edit Tags…', true, 'tag')];        $at = count($items);        if ($at > 0 && is_array($items[$at - 1]) && ($items[$at - 1]['id'] ?? '') === 'object.properties') {            $at--;        }        return [...array_slice($items, 0, $at), ...$mine, '-', ...array_slice($items, $at)];    }    /** Runs a command of a menu: opens its dialog (or a confirmation first). */    public static function run(MainForm $form, string $id, ?Connection $c, string $target, bool $confirmed): void    {        $session = $form->session;        $name = OrganizeTree::nameOf($target);        switch ($id) {            case 'organize.move':                if ($c !== null && $c->cache->get($target) !== null) {                    Engine::get()->scenes->push(FolderForm::class, ['mode' => 'move', 'target' => $target, 'name' => $c->cache->name($target),                        'folder' => Organizer::folderOf($c->cache, $target), 'folders' => Organizer::folders($session)]);                }                return;            case 'organize.tags':                if ($c !== null && $c->cache->get($target) !== null) {                    Engine::get()->scenes->push(TagsForm::class, ['target' => $target, 'name' => $c->cache->name($target),                        'tags' => Organizer::tagsOf($c->cache, $target), 'known' => Organizer::allTags($session)]);                }                return;            case 'organize.newFolder':                Engine::get()->scenes->push(FolderForm::class, ['mode' => 'new', 'parent' => OrganizeTree::isFolder($target) ? $name : '']);                return;            case 'organize.renameFolder':                Engine::get()->scenes->push(FolderForm::class, ['mode' => 'rename', 'path' => $name, 'value' => Organizer::leaf($name)]);                return;            case 'organize.renameTag':                Engine::get()->scenes->push(FolderForm::class, ['mode' => 'renameTag', 'path' => $name, 'value' => $name]);                return;            case 'organize.deleteFolder':            case 'organize.deleteTag':                if (!$confirmed) {                    Engine::get()->scenes->push(ConfirmForm::class, ['command' => $id, 'target' => $target, 'ok' => 'Delete', 'message' => $id === 'organize.deleteFolder'                        ? "Delete the folder '$name'?\n\nIts objects and those of its subfolders go back to the top level: nothing is deleted on the servers."                        : "Delete the tag '$name'?\n\nIt is removed from every object that has it."]);                    return;                }                $id === 'organize.deleteFolder' ? Organizer::deleteFolder($session, $name) : Organizer::renameTag($session, $name, '');                $form->navigate(\App\View\TreeBuilder::ROOT);                return;        }    }    /** What FolderForm / TagsForm answered. @param array<string, mixed> $r */    public static function apply(Session $session, array $r): void    {        $ref = (string)($r['target'] ?? '');        $c = $ref === '' ? null : $session->connectionOf($ref);        switch ((string)($r['mode'] ?? '')) {            case 'move':                if ($c !== null) {                    Organizer::setFolder($c, $ref, (string)$r['folder'])->then(fn () => null, fn () => null);                }                return;            case 'tags':                if ($c !== null) {                    Organizer::setTags($c, $ref, array_values(array_map('strval', (array)$r['tags'])))->then(fn () => null, fn () => null);                }                return;            case 'new':                foreach ($session->connections as $conn) {                    if ($conn->isConnected()) {                        Organizer::newFolder($conn, (string)$r['folder'])->then(fn () => null, fn () => null);    // the first pool keeps it                        return;                    }                }                return;            case 'rename':                Organizer::renameFolder($session, (string)$r['path'], (string)$r['folder']);                return;            case 'renameTag':                Organizer::renameTag($session, (string)$r['path'], (string)$r['folder']);                return;        }    }    /** The page of a folder or a tag: what is in it, with its commands. Null for any other node. */    public static function page(Session $session, string $id): ?Page    {        $folder = OrganizeTree::isFolder($id);        if (!$folder && !OrganizeTree::isTag($id)) {            return null;        }        $name = OrganizeTree::nameOf($id);        $rows = [];        foreach ($session->connections as $c) {            if (!$c->isConnected()) {                continue;            }            foreach (Organizer::objects($c->cache) as $ref => $class) {                $in = $folder ? Organizer::inside(Organizer::folderOf($c->cache, $ref), $name) : in_array($name, Organizer::tagsOf($c->cache, $ref), true);                if ($in) {                    $r = $c->cache->get($ref) ?? [];                    $rows[] = ['id' => $ref, 'icon' => Icons::of($class, $r, $c->cache), 'cells' => [$c->cache->name($ref), self::label($class, $r),                        Organizer::folderOf($c->cache, $ref) ?: '(top level)', implode(', ', Organizer::tagsOf($c->cache, $ref)), $c->name()]];                }            }        }        usort($rows, fn ($a, $b) => strnatcasecmp((string)$a['cells'][0], (string)$b['cells'][0]));        $actions = $folder            ? [['id' => 'organize.newFolder', 'text' => 'New Folder…', 'style' => 'primary'], ['id' => 'organize.renameFolder', 'text' => 'Rename…'], ['id' => 'organize.deleteFolder', 'text' => 'Delete…']]            : [['id' => 'organize.renameTag', 'text' => 'Rename Tag…'], ['id' => 'organize.deleteTag', 'text' => 'Delete Tag…']];        return Page::grid([['title' => 'Name', 'width' => 2.2], ['title' => 'Type', 'width' => 1.1], ['title' => 'Folder', 'width' => 1.8], ['title' => 'Tags', 'width' => 1.8],            ['title' => 'Server', 'width' => 1.2]], $rows, $actions, $folder ? 'This folder is empty: right click an object, Move to Folder…' : 'No object has this tag.');    }    /** @param array<string, mixed> $r */    private static function label(string $class, array $r): string    {        return match ($class) {            'pool' => 'Pool', 'host' => 'Server', 'sr' => 'Storage', 'network' => 'Network',            default => !empty($r['is_a_template']) ? 'Template' : 'VM',        };    }}