From 89e6c1ce7f901a19467fb5cbc18e8a87ea901482 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 19 Mar 2019 23:52:11 +0100 Subject: [serversetup-bwlp-ipxe] Implement cascaded menus --- .../serversetup-bwlp-ipxe/api.inc.php | 7 +- .../serversetup-bwlp-ipxe/inc/bootentry.inc.php | 35 ++++++++ .../serversetup-bwlp-ipxe/inc/ipxemenu.inc.php | 4 +- .../serversetup-bwlp-ipxe/inc/menuentry.inc.php | 4 +- .../serversetup-bwlp-ipxe/install.inc.php | 15 +++- .../serversetup-bwlp-ipxe/page.inc.php | 95 ++++++++++++---------- .../serversetup-bwlp-ipxe/templates/menu-edit.html | 11 ++- 7 files changed, 121 insertions(+), 50 deletions(-) (limited to 'modules-available/serversetup-bwlp-ipxe') diff --git a/modules-available/serversetup-bwlp-ipxe/api.inc.php b/modules-available/serversetup-bwlp-ipxe/api.inc.php index 73461901..7a81f430 100644 --- a/modules-available/serversetup-bwlp-ipxe/api.inc.php +++ b/modules-available/serversetup-bwlp-ipxe/api.inc.php @@ -68,7 +68,12 @@ $ip = $_SERVER['REMOTE_ADDR']; if (substr($ip, 0, 7) === '::ffff:') { $ip = substr($ip, 7); } -$menu = IPxeMenu::forClient($ip, $uuid); +$menu = Request::get('menuid', false, 'int'); +if ($menu !== false) { + $menu = new IPxeMenu($menu); +} else { + $menu = IPxeMenu::forClient($ip, $uuid); +} // Get preferred localboot method, depending on system model diff --git a/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php b/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php index 0248e0ea..ee245e40 100644 --- a/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php +++ b/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php @@ -46,6 +46,11 @@ abstract class BootEntry return null; } + public static function forMenu($menuId) + { + return new MenuBootEntry($menuId); + } + public static function newStandardBootEntry($initData) { $ret = new StandardBootEntry($initData); @@ -276,3 +281,33 @@ class CustomBootEntry extends BootEntry return ['script' => $this->script]; } } + +class MenuBootEntry extends BootEntry +{ + protected $menuId; + + public function __construct($menuId) + { + $this->menuId = $menuId; + } + + public function supportsMode($mode) + { + return true; + } + + public function toScript($failLabel, $mode) + { + return 'chain -ar ${self}&menuid=' . $this->menuId . ' || goto ' . $failLabel . "\n"; + } + + public function toArray() + { + return []; + } + + public function addFormFields(&$array) + { + } +} + diff --git a/modules-available/serversetup-bwlp-ipxe/inc/ipxemenu.inc.php b/modules-available/serversetup-bwlp-ipxe/inc/ipxemenu.inc.php index 6deea7f7..991ee403 100644 --- a/modules-available/serversetup-bwlp-ipxe/inc/ipxemenu.inc.php +++ b/modules-available/serversetup-bwlp-ipxe/inc/ipxemenu.inc.php @@ -25,7 +25,7 @@ class IPxeMenu $this->timeoutMs = (int)$menu['timeoutms']; $this->title = $menu['title']; $this->defaultEntryId = $menu['defaultentryid']; - $res = Database::simpleQuery("SELECT e.menuentryid, e.entryid, e.hotkey, e.title, e.hidden, e.sortval, e.md5pass, + $res = Database::simpleQuery("SELECT e.menuentryid, e.entryid, e.refmenuid, e.hotkey, e.title, e.hidden, e.sortval, e.md5pass, b.data AS bootentry FROM serversetup_menuentry e LEFT JOIN serversetup_bootentry b USING (entryid) @@ -139,4 +139,4 @@ class EmptyIPxeMenu extends IPxeMenu ]); } -} \ No newline at end of file +} diff --git a/modules-available/serversetup-bwlp-ipxe/inc/menuentry.inc.php b/modules-available/serversetup-bwlp-ipxe/inc/menuentry.inc.php index d29995c6..27713b9e 100644 --- a/modules-available/serversetup-bwlp-ipxe/inc/menuentry.inc.php +++ b/modules-available/serversetup-bwlp-ipxe/inc/menuentry.inc.php @@ -49,8 +49,10 @@ class MenuEntry $this->hotkey = self::getKeyCode($row['hotkey']); if (!empty($row['bootentry'])) { $this->bootEntry = BootEntry::fromJson($row['bootentry']); + } elseif ($row['refmenuid'] !== null) { + $this->bootEntry = BootEntry::forMenu($row['refmenuid']); } - $this->gap = (array_key_exists('entryid', $row) && $row['entryid'] === null); + $this->gap = (array_key_exists('entryid', $row) && $row['entryid'] === null && $row['refmenuid'] === null); } settype($this->hidden, 'bool'); settype($this->gap, 'bool'); diff --git a/modules-available/serversetup-bwlp-ipxe/install.inc.php b/modules-available/serversetup-bwlp-ipxe/install.inc.php index 25579c13..201e0ced 100644 --- a/modules-available/serversetup-bwlp-ipxe/install.inc.php +++ b/modules-available/serversetup-bwlp-ipxe/install.inc.php @@ -25,7 +25,8 @@ $res[] = tableCreate('serversetup_menu', " $res[] = tableCreate('serversetup_menuentry', " `menuentryid` int(11) NOT NULL AUTO_INCREMENT, `menuid` int(11) NOT NULL, - `entryid` varchar(16) CHARACTER SET ascii NULL COMMENT 'If NULL, entry is gap', + `entryid` varchar(16) CHARACTER SET ascii NULL COMMENT 'If NULL, entry is gap or another menu', + `refmenuid` int(11) DEFAULT NULL COMMENT 'If entryid is NULL this can be a ref to another menu', `hotkey` varchar(8) CHARACTER SET ascii NOT NULL, `title` varchar(100) NOT NULL COMMENT 'Sanitize this before insert', `hidden` tinyint(1) NOT NULL, @@ -77,6 +78,18 @@ $res[] = tableAddConstraint('serversetup_menu_location', 'menuid', 'serversetup_ $res[] = tableAddConstraint('serversetup_menu_location', 'defaultentryid', 'serversetup_menuentry', 'menuentryid', 'ON UPDATE CASCADE ON DELETE SET NULL'); +// 2019-03-19 Add refmenuid to have cascaded menus +if (!tableHasColumn('serversetup_menuentry', 'refmenuid')) { + if (Database::exec("ALTER TABLE serversetup_menuentry ADD COLUMN `refmenuid` int(11) DEFAULT NULL COMMENT 'If entryid is NULL this can be a ref to another menu'") !== false) { + $res[] = UPDATE_DONE; + } else { + $res[] = UPDATE_FAILED; + } +} + +$res[] = tableAddConstraint('serversetup_menuentry', 'refmenuid', 'serversetup_menu', 'menuid', + 'ON UPDATE CASCADE ON DELETE SET NULL'); + if (Module::get('location') !== false) { if (!tableExists('location')) { $res[] = UPDATE_RETRY; diff --git a/modules-available/serversetup-bwlp-ipxe/page.inc.php b/modules-available/serversetup-bwlp-ipxe/page.inc.php index de802da8..8ea782b3 100644 --- a/modules-available/serversetup-bwlp-ipxe/page.inc.php +++ b/modules-available/serversetup-bwlp-ipxe/page.inc.php @@ -387,7 +387,7 @@ class Page_ServerSetup extends Page $menu['timeout'] = round($menu['timeoutms'] / 1000); $menu['entries'] = []; - $res = Database::simpleQuery("SELECT menuentryid, entryid, hotkey, title, hidden, sortval, plainpass FROM + $res = Database::simpleQuery("SELECT menuentryid, entryid, refmenuid, hotkey, title, hidden, sortval, plainpass FROM serversetup_menuentry WHERE menuid = :id ORDER BY sortval ASC", compact('id')); while ($row = $res->fetch(PDO::FETCH_ASSOC)) { if ($row['entryid'] == $highlight) { @@ -396,45 +396,51 @@ class Page_ServerSetup extends Page $menu['entries'][] = $row; } $menu['keys'] = array_map(function ($item) { return ['key' => $item]; }, MenuEntry::getKeyList()); - $menu['entrylist'] = Database::queryAll("SELECT entryid, title, hotkey, data FROM serversetup_bootentry ORDER BY title ASC"); + $menu['entrylist'] = array_merge( + Database::queryAll("SELECT entryid, title, hotkey, data FROM serversetup_bootentry ORDER BY title ASC"), + // Add all menus, so we can link + Database::queryAll("SELECT Concat('menu=', menuid) AS entryid, title FROM serversetup_menu ORDER BY title ASC") + ); class_exists('BootEntry'); // Leave this here for StandardBootEntry foreach ($menu['entrylist'] as &$bootentry) { + if (!isset($bootentry['data'])) + continue; $bootentry['data'] = json_decode($bootentry['data'], true); // Transform stuff suitable for mustache - if (array_key_exists('arch', $bootentry['data'])) { - // BIOS/EFI or both - if ($bootentry['data']['arch'] === StandardBootEntry::BIOS - || $bootentry['data']['arch'] === StandardBootEntry::BOTH) { - $bootentry['data']['PCBIOS'] = array('executable' => $bootentry['data']['executable']['PCBIOS'], - 'initRd' => $bootentry['data']['initRd']['PCBIOS'], - 'commandLine' => $bootentry['data']['commandLine']['PCBIOS']); - } - if ($bootentry['data']['arch'] === StandardBootEntry::EFI - || $bootentry['data']['arch'] === StandardBootEntry::BOTH) { - $bootentry['data']['EFI'] = array('executable' => $bootentry['data']['executable']['EFI'], - 'initRd' => $bootentry['data']['initRd']['EFI'], - 'commandLine' => $bootentry['data']['commandLine']['EFI']); - } - // Naming and agnostic - if ($bootentry['data']['arch'] === StandardBootEntry::BIOS) { - $bootentry['data']['arch'] = Dictionary::translateFile('template-tags','lang_biosOnly', true); - unset($bootentry['data']['EFI']); - } elseif ($bootentry['data']['arch'] === StandardBootEntry::EFI) { - $bootentry['data']['arch'] = Dictionary::translateFile('template-tags','lang_efiOnly', true); - unset($bootentry['data']['PCBIOS']); - } elseif ($bootentry['data']['arch'] === StandardBootEntry::AGNOSTIC) { - $bootentry['data']['archAgnostic'] = array('executable' => $bootentry['data']['executable']['PCBIOS'], - 'initRd' => $bootentry['data']['initRd']['PCBIOS'], - 'commandLine' => $bootentry['data']['commandLine']['PCBIOS']); - $bootentry['data']['arch'] = Dictionary::translateFile('template-tags','lang_archAgnostic', true); - unset($bootentry['data']['EFI']); - } else { - $bootentry['data']['arch'] = Dictionary::translateFile('template-tags','lang_archBoth', true); - } - foreach ($bootentry['data'] as &$e) { - if (isset($e['initRd']) && is_array($e['initRd'])) { - $e['initRd'] = implode(',', $e['initRd']); - } + if (!array_key_exists('arch', $bootentry['data'])) + continue; + // BIOS/EFI or both + if ($bootentry['data']['arch'] === StandardBootEntry::BIOS + || $bootentry['data']['arch'] === StandardBootEntry::BOTH) { + $bootentry['data']['PCBIOS'] = array('executable' => $bootentry['data']['executable']['PCBIOS'], + 'initRd' => $bootentry['data']['initRd']['PCBIOS'], + 'commandLine' => $bootentry['data']['commandLine']['PCBIOS']); + } + if ($bootentry['data']['arch'] === StandardBootEntry::EFI + || $bootentry['data']['arch'] === StandardBootEntry::BOTH) { + $bootentry['data']['EFI'] = array('executable' => $bootentry['data']['executable']['EFI'], + 'initRd' => $bootentry['data']['initRd']['EFI'], + 'commandLine' => $bootentry['data']['commandLine']['EFI']); + } + // Naming and agnostic + if ($bootentry['data']['arch'] === StandardBootEntry::BIOS) { + $bootentry['data']['arch'] = Dictionary::translateFile('template-tags','lang_biosOnly', true); + unset($bootentry['data']['EFI']); + } elseif ($bootentry['data']['arch'] === StandardBootEntry::EFI) { + $bootentry['data']['arch'] = Dictionary::translateFile('template-tags','lang_efiOnly', true); + unset($bootentry['data']['PCBIOS']); + } elseif ($bootentry['data']['arch'] === StandardBootEntry::AGNOSTIC) { + $bootentry['data']['archAgnostic'] = array('executable' => $bootentry['data']['executable']['PCBIOS'], + 'initRd' => $bootentry['data']['initRd']['PCBIOS'], + 'commandLine' => $bootentry['data']['commandLine']['PCBIOS']); + $bootentry['data']['arch'] = Dictionary::translateFile('template-tags','lang_archAgnostic', true); + unset($bootentry['data']['EFI']); + } else { + $bootentry['data']['arch'] = Dictionary::translateFile('template-tags','lang_archBoth', true); + } + foreach ($bootentry['data'] as &$e) { + if (isset($e['initRd']) && is_array($e['initRd'])) { + $e['initRd'] = implode(',', $e['initRd']); } } } @@ -613,7 +619,7 @@ class Page_ServerSetup extends Page if ($entries) { foreach ($entries as $key => $entry) { if (!isset($entry['sortval'])) { - error_log(print_r($entry, true)); + error_log("Incomplete entry $key with: " . print_r($entry, true)); continue; } // Fallback defaults @@ -624,6 +630,8 @@ class Page_ServerSetup extends Page 'plainpass' => '', ]; $params = [ + 'entryid' => null, + 'refmenuid' => null, 'title' => IPxe::sanitizeIpxeString($entry['title']), 'sortval' => (int)$entry['sortval'], 'menuid' => $menu['menuid'], @@ -631,18 +639,21 @@ class Page_ServerSetup extends Page if (empty($entry['entryid'])) { // Spacer $params += [ - 'entryid' => null, 'hotkey' => '', 'hidden' => 0, // Doesn't make any sense 'plainpass' => '', // Doesn't make any sense ]; } else { $params += [ - 'entryid' => $entry['entryid'], // TODO validate? 'hotkey' => MenuEntry::filterKeyName($entry['hotkey']), 'hidden' => (int)$entry['hidden'], // TODO (needs hotkey to make sense) 'plainpass' => $entry['plainpass'], ]; + if (preg_match('/^menu=(\d+)$/', $entry['entryid'], $out)) { + $params['refmenuid'] = $out[1]; + } else { + $params['entryid'] = $entry['entryid']; + } } if (is_numeric($key)) { if ((string)$key === $wantedDefaultEntryId) { // Check now that we have generated our key @@ -652,13 +663,13 @@ class Page_ServerSetup extends Page $params['menuentryid'] = $key; $params['md5pass'] = IPxe::makeMd5Pass($entry['plainpass'], $key); $ret = Database::exec('UPDATE serversetup_menuentry - SET entryid = :entryid, hotkey = :hotkey, title = :title, hidden = :hidden, sortval = :sortval, + SET entryid = :entryid, refmenuid = :refmenuid, hotkey = :hotkey, title = :title, hidden = :hidden, sortval = :sortval, plainpass = :plainpass, md5pass = :md5pass WHERE menuid = :menuid AND menuentryid = :menuentryid', $params, true); } else { $ret = Database::exec("INSERT INTO serversetup_menuentry - (menuid, entryid, hotkey, title, hidden, sortval, plainpass, md5pass) - VALUES (:menuid, :entryid, :hotkey, :title, :hidden, :sortval, :plainpass, '')", $params, true); + (menuid, entryid, refmenuid, hotkey, title, hidden, sortval, plainpass, md5pass) + VALUES (:menuid, :entryid, :refmenuid, :hotkey, :title, :hidden, :sortval, :plainpass, '')", $params, true); if ($ret) { $newKey = Database::lastInsertId(); if ((string)$key === $wantedDefaultEntryId) { // Check now that we have generated our key diff --git a/modules-available/serversetup-bwlp-ipxe/templates/menu-edit.html b/modules-available/serversetup-bwlp-ipxe/templates/menu-edit.html index 037cae28..701411bf 100644 --- a/modules-available/serversetup-bwlp-ipxe/templates/menu-edit.html +++ b/modules-available/serversetup-bwlp-ipxe/templates/menu-edit.html @@ -74,7 +74,12 @@ {{entryid}} {{/entryid}} {{^entryid}} - {{lang_spacer}} + {{^refmenuid}} + {{lang_spacer}} + {{/refmenuid}} + {{#refmenuid}} + menu={{refmenuid}} + {{/refmenuid}} {{/entryid}} @@ -132,7 +137,7 @@ @@ -365,4 +370,4 @@ $title.val(text).data('old', text); }); }); - \ No newline at end of file + -- cgit v1.2.3-55-g7522