Entrar
PT
games/xencenter-xenadmin-xcp-jumphp / src / Feature / Organize / OrganizeTree.php · commit fb1b7c9
<?phpnamespace App\Feature\Organize;use App\Gui\Icons;use App\Model\Cache;use App\Model\Connection;use App\Model\Session;use App\View\Pages;/** * The nodes of the Folders and Tags views (TreeBuilder::folders() / tags() put them under their root): * - Folders: 'folder:/Production' > 'folder:/Production/Web' > the objects in it (their XAPI refs: an object is in one folder); * - Tags: 'tag:linux' > the objects having it, as 'tag:linux|<ref>' (an object with two tags is shown twice: ids stay unique). * Folders of every connected pool are merged, as XenCenter does. */final class OrganizeTree{    public const FOLDER = 'folder:';    public const TAG = 'tag:';    /** @return list<array<string, mixed>> */    public static function folders(Session $session): array    {        $objects = [];        // folder => list of nodes        foreach ($session->connections as $c) {            if ($c->isConnected()) {                foreach (Organizer::objects($c->cache) as $ref => $class) {                    $folder = Organizer::folderOf($c->cache, $ref);                    if ($folder !== '') {                        $objects[$folder][] = self::objectNode($c->cache, $ref, $class, $ref);                    }                }            }        }        $paths = Organizer::folders($session);        $build = function (string $parent) use (&$build, $paths, $objects): array {            $out = [];            foreach ($paths as $path) {                if (Organizer::parent($path) === $parent) {                    $children = [...$build($path), ...self::sorted($objects[$path] ?? [])];                    $out[] = ['id' => self::FOLDER . $path, 'text' => Organizer::leaf($path), 'icon' => 'folder', 'children' => $children, 'open' => true];                }            }            return $out;        };        return $build('');    }    /** @return list<array<string, mixed>> */    public static function tags(Session $session): array    {        $byTag = [];        foreach ($session->connections as $c) {            if ($c->isConnected()) {                foreach (Organizer::objects($c->cache) as $ref => $class) {                    foreach (Organizer::tagsOf($c->cache, $ref) as $tag) {                        $byTag[$tag][] = self::objectNode($c->cache, $ref, $class, self::TAG . $tag . '|' . $ref);                    }                }            }        }        uksort($byTag, 'strnatcasecmp');        $out = [];        foreach ($byTag as $tag => $nodes) {            $out[] = ['id' => self::TAG . $tag, 'text' => (string)$tag, 'suffix' => ' (' . count($nodes) . ')', 'icon' => 'tag', 'children' => self::sorted($nodes), 'open' => true];        }        return $out;    }    /** @return array<string, mixed> */    private static function objectNode(Cache $cache, string $ref, string $class, string $id): array    {        $r = $cache->get($ref) ?? [];        return ['id' => $id, 'text' => $cache->name($ref), 'icon' => Icons::of($class, $r, $cache)];    }    /** @param list<array<string, mixed>> $nodes @return list<array<string, mixed>> */    private static function sorted(array $nodes): array    {        usort($nodes, fn ($a, $b) => strnatcasecmp((string)$a['text'], (string)$b['text']));        return $nodes;    }    /** The object a node of these views stands for: 'tag:linux|OpaqueRef:…' -> 'OpaqueRef:…'; any other id as it is. */    public static function refOf(string $id): string    {        if (str_starts_with($id, self::TAG) && ($at = strrpos($id, '|')) !== false) {            return substr($id, $at + 1);        }        return $id;    }    public static function isFolder(string $id): bool    {        return str_starts_with($id, self::FOLDER);    }    public static function isTag(string $id): bool    {        return str_starts_with($id, self::TAG) && !str_contains($id, '|');    }    /** '/Production/Web' of 'folder:/Production/Web', the tag of 'tag:linux'. */    public static function nameOf(string $id): string    {        return substr($id, strlen(self::isFolder($id) ? self::FOLDER : self::TAG));    }    /**     * MainForm::selection() for the ids of these views, null for the others: a folder or a tag node is a 'group' (its Search tab)     * on the first connected server; an object under a tag is that object.     * @return array{0: ?Connection, 1: string, 2: string}|null     */    public static function selection(Session $session, string $id): ?array    {        if (self::isFolder($id) || self::isTag($id)) {            foreach ($session->connections as $c) {                if ($c->isConnected()) {                    return [$c, $id, 'group'];                }            }            return [null, $id, 'group'];        }        if (str_starts_with($id, self::TAG)) {            $ref = self::refOf($id);            $c = $session->connectionOf($ref);            return [$c, $ref, Pages::kind($c, $ref)];        }        return null;    }}