$menuId]); if ($menu !== false) return new IPxeMenu($menu); if (!$emptyFallback) return null; return new EmptyIPxeMenu(); } /** * IPxeMenu constructor. * * @param array $menu array for according menu row */ public function __construct(array $menu) { $this->menuid = (int)$menu['menuid']; $this->timeoutMs = (int)$menu['timeoutms']; $this->title = (string)$menu['title']; $defaultEntryId = $menu['defaultentryid']; $res = Database::simpleQuery("SELECT e.menuentryid, e.entryid, e.refmenuid, e.hotkey, e.title, e.hidden, e.sortval, e.md5pass, b.module, b.data AS bootentry, b.title AS betitle FROM serversetup_menuentry e LEFT JOIN serversetup_bootentry b USING (entryid) WHERE e.menuid = :menuid ORDER BY e.sortval ASC, e.title ASC", ['menuid' => $menu['menuid']]); foreach ($res as $row) { $this->items[] = new MenuEntry($row); } // Make sure we have a default entry if the menu isn't empty if ($defaultEntryId === null && !empty($this->items)) { $defaultEntryId = $this->items[0]->menuEntryId(); } $this->defaultEntryId = (int)$defaultEntryId; } public function title(): string { return $this->title; } public function timeoutMs(): int { return $this->timeoutMs; } /** * @return int Number of items in this menu */ public function itemCount(): int { return count($this->items); } /** * @return MenuEntry|null Return preselected menu entry */ public function defaultEntry(): ?MenuEntry { foreach ($this->items as $item) { if ($item->menuEntryId() === $this->defaultEntryId) return $item; } return null; } private function maybeOverrideDefault(string $uuid) { $e = $this->defaultEntry(); // Shortcut - is already bwlp and timeout is reasonable (1-15s), do nothing $defIsMl = $e !== null && substr($e->internalId(), 0, 3) === 'ml-'; $timeoutOk = $this->timeoutMs > 0 && $this->timeoutMs <= 15000; if ($timeoutOk && $defIsMl) return; // No runmode module anyways if (!Module::isAvailable('runmode')) return; $rm = RunMode::getRunMode($uuid); // No runmode for this client, cannot be PVSmgr if ($rm === false) return; // Is not pvsmgr if ($rm['module'] !== 'roomplanner') return; // See if it's a dedicated station, if so make sure it boots into bwLehrpool $data = json_decode($rm['modedata'], true); if ($data['dedicatedmgr'] ?? false) { if (!$defIsMl) { $this->overrideDefaultToMinilinux(); } if (!$timeoutOk) { $this->timeoutMs = 5000; } } } /** * Patch the menu to make sure bwLehrpool/"MiniLinux" is the default * boot option, and set timeout to something reasonable. This is used * for dedicated PVS managers, as they might not have a keyboard * connected. */ private function overrideDefaultToMinilinux() { foreach ($this->items as $item) { if (substr($item->internalId(), 0, 3) === 'ml-') { $this->defaultEntryId = $item->menuEntryId(); return; } } } /* * */ public static function forLocation(int $locationId): IPxeMenu { $chain = null; if (Module::isAvailable('locations')) { $chain = Location::getLocationRootChain($locationId); } if (!empty($chain)) { $res = Database::simpleQuery("SELECT m.menuid, m.timeoutms, m.title, IFNULL(ml.defaultentryid, m.defaultentryid) AS defaultentryid, ml.locationid FROM serversetup_menu m INNER JOIN serversetup_menu_location ml USING (menuid) WHERE ml.locationid IN (:chain)", ['chain' => $chain]); if ($res->rowCount() > 0) { // Make the location id key, preserving order (closest location is first) $chain = array_flip($chain); foreach ($res as $row) { // Overwrite the value (numeric ascending values, useless) with menu array of according location $chain[(int)$row['locationid']] = $row; } // Use first one that was found foreach ($chain as $menu) { if (is_array($menu)) { return new IPxeMenu($menu); } } // Should never end up here, but we'd just fall through and use the default } } // We're here, no specific menu, use default $menu = Database::queryFirst("SELECT menuid, timeoutms, title, defaultentryid FROM serversetup_menu ORDER BY isdefault DESC LIMIT 1"); if ($menu === false) { return new EmptyIPxeMenu; } return new IPxeMenu($menu); } public static function forClient(string $ip, ?string $uuid): IPxeMenu { $locationId = 0; if (Module::isAvailable('locations')) { $locationId = Location::getFromIpAndUuid($ip, $uuid); } $menu = self::forLocation($locationId); if ($uuid !== null) { // Super specialcase hackery: If this is a dedicated PVS, force the default to // be bwlp/"minilinux" $menu->maybeOverrideDefault($uuid); } return $menu; } } class EmptyIPxeMenu extends IPxeMenu { public function __construct() { parent::__construct([ 'menuid' => -1, 'timeoutms' => 120, 'defaultentryid' => null, 'title' => 'No menu defined', ]); $this->items[] = new MenuEntry([ 'title' => 'Please create a menu in Server-Setup first' ]); $this->items[] = new MenuEntry([ 'title' => 'Bitte erstellen Sie zunächst ein Menü' ]); } }