diff options
Diffstat (limited to 'modules-available')
20 files changed, 589 insertions, 132 deletions
diff --git a/modules-available/minilinux/hooks/ipxe-bootentry.inc.php b/modules-available/minilinux/hooks/ipxe-bootentry.inc.php index 4e2cbb5e..090a14da 100644 --- a/modules-available/minilinux/hooks/ipxe-bootentry.inc.php +++ b/modules-available/minilinux/hooks/ipxe-bootentry.inc.php @@ -1,11 +1,23 @@ <?php -class Wurst extends BootEntryHook +class LinuxBootEntryHook extends BootEntryHook { public function name() { - return 'Wurst'; + return Dictionary::translateFileModule('minilinux', 'module', 'module_name', true); + } + + public function extraFields() + { + /* For translate module: + * Dictionary::translate('ipxe-kcl-extra'); + * Dictionary::translate('ipxe-debug'); + */ + return [ + new HookExtraField('kcl-extra', 'string', ''), + new HookExtraField('debug', 'bool', false), + ]; } /** @@ -13,30 +25,105 @@ class Wurst extends BootEntryHook */ protected function groupsInternal() { - return [ - new HookEntryGroup('Senf Gruppe', [ - new HookEntry('senf-1', 'Senf v1'), - new HookEntry('senf-2', 'Senf v2'), - ]), - new HookEntryGroup('Schnecke Gruppe', [ - new HookEntry('s-1', 'Trulla'), - new HookEntry('s-2', 'Herbert'), - ]), - ]; + /* + * Dictionary::translate('default_boot_entry'); + * Dictionary::translate('not_installed_hint'); + */ + $array = []; + $array[] = new HookEntryGroup($this->name(), [ + new HookEntry('default', + Dictionary::translateFileModule('minilinux', 'module', 'default_boot_entry', true), + MiniLinux::updateCurrentBootSetting()) + ]); + $branches = Database::queryAll('SELECT sourceid, branchid, title FROM minilinux_branch ORDER BY title'); + $versions = MiniLinux::queryAllVersionsByBranch(); + // Group by branch for detailed listing + foreach ($branches as $branch) { + if (isset($versions[$branch['branchid']])) { + $group = []; + foreach ($versions[$branch['branchid']] as $version) { + $valid = $version['installed'] != 0; + $title = $version['versionid'] . ' ' . $version['title']; + if (!$valid) { + $title .= ' ' . Dictionary::translateFileModule('minilinux', 'module', 'not_installed_hint'); + } + $group[] = new HookEntry($version['versionid'], $title, $valid); + } + $array[] = new HookEntryGroup($branch['title'] ? $branch['title'] : $branch['branchid'], $group); + } + } + return $array; } /** * @param $id * @return BootEntry the actual boot entry instance for given entry, false if invalid id */ - public function getBootEntry($id) + public function getBootEntryInternal($data) + { + $id = $data['id']; + if ($id === 'default') { // Special case + $effectiveId = Property::get(MiniLinux::PROPERTY_DEFAULT_BOOT_EFFECTIVE); + } else { + $effectiveId = $id; + } + $res = Database::queryFirst('SELECT installed, data FROM minilinux_version WHERE versionid = :id', ['id' => $effectiveId]); + if ($res === false) { + return BootEntry::newCustomBootEntry(['script' => 'prompt Invalid minilinux boot entry id: ' . $id]); + } + if ($res['installed'] == 0) { + return BootEntry::newCustomBootEntry(['script' => 'prompt Selected version not currently installed on server: ' . $id]); + } + $exec = new ExecData(); + // Defaults + $root = '/boot/' . $id . '/'; + $exec->executable = 'kernel'; + $exec->initRd = ['initramfs-stage31']; + $exec->imageFree = true; + $exec->commandLine = 'slxbase=boot/%ID% slxsrv=${serverip} quiet splash ${ipappend1} ${ipappend2}'; + // Overrides + $remoteData = json_decode($res['data'], true); + // TODO: agnostic hard coded, support EFI and PCBIOS + if (isset($remoteData['agnostic']) && is_array($remoteData['agnostic'])) { + foreach (['executable', 'commandLine', 'initRd', 'imageFree'] as $key) { + if (isset($remoteData['agnostic'][$key])) { + $exec->{$key} = $remoteData['agnostic'][$key]; + } + } + } + unset($rd); + // KCL hacks + if (isset($data['debug']) && $data['debug']) { + if (!isset($data['kcl-extra'])) { + $data['kcl-extra'] = ''; + } + $data['kcl-extra'] = '-quiet -splash -loglevel loglevel=7 ' . $data['kcl-extra']; + } + if (isset($data['kcl-extra'])) { + $items = preg_split('/\s+/', $data['kcl-extra'], -1, PREG_SPLIT_NO_EMPTY); + // TODO: Make this a function, somewhere in serversetup-ipxe, this could be useful for other stuff + foreach ($items as $item) { + if ($item{0} === '-') { + $item = preg_quote(substr($item, 1), '/'); + $exec->commandLine = preg_replace('/(^|\s)' . $item . '(=\S*)?($|\s)/', ' ', $exec->commandLine); + } else { + $exec->commandLine .= ' ' . $item; + } + } + } + $exec->commandLine = str_replace('%ID%', $id, $exec->commandLine); + $exec->executable = $root . $exec->executable; + foreach ($exec->initRd as &$rd) { + $rd = $root . $rd; + } + return BootEntry::newStandardBootEntry($exec, false, 'agnostic'); + } + + public function isValidId($id) { - $bios = new ExecData(); - $bios->executable = 'mspaint.exe'; - $bios->initRd = 'www.google.de'; - $bios->commandLine = '-q'; - return BootEntry::newStandardBootEntry($bios, false, 'agnostic'); + $res = Database::queryFirst('SELECT installed FROM minilinux_version WHERE versionid = :id', ['id' => $id]); + return $res !== false && $res['installed']; } } -return new Wurst();
\ No newline at end of file +return new LinuxBootEntryHook();
\ No newline at end of file diff --git a/modules-available/minilinux/hooks/main-warning.inc.php b/modules-available/minilinux/hooks/main-warning.inc.php index 31668e6c..8b052471 100644 --- a/modules-available/minilinux/hooks/main-warning.inc.php +++ b/modules-available/minilinux/hooks/main-warning.inc.php @@ -1,6 +1,8 @@ <?php -if (!file_exists(CONFIG_HTTP_DIR . '/default/kernel') || !file_exists(CONFIG_HTTP_DIR . '/default/initramfs-stage31') || !file_exists(CONFIG_HTTP_DIR . '/default/stage32.sqfs')) { +if (!is_dir(CONFIG_HTTP_DIR . '/bwlp/default')) { Message::addError('minilinux.please-download-minilinux', true); $needSetup = true; +} else { + $needSetup = MiniLinux::generateUpdateNotice(); }
\ No newline at end of file diff --git a/modules-available/minilinux/inc/minilinux.inc.php b/modules-available/minilinux/inc/minilinux.inc.php index 46c63c94..e940380c 100644 --- a/modules-available/minilinux/inc/minilinux.inc.php +++ b/modules-available/minilinux/inc/minilinux.inc.php @@ -5,6 +5,20 @@ class MiniLinux const PROPERTY_KEY_FETCHTIME = 'ml-list-fetch'; + const PROPERTY_DEFAULT_BOOT = 'ml-default'; + + const PROPERTY_DEFAULT_BOOT_EFFECTIVE = 'ml-default-eff'; + + const INVALID = 'invalid'; + + /* + * Update of available versions by querying sources + */ + + /** + * Query all known sources for meta data + * @return int number of sources query was just initialized for + */ public static function updateList() { $stamp = time(); @@ -27,19 +41,25 @@ class MiniLinux 'id' => $source['taskid'], 'url' => $source['url'], ), true); - TaskmanagerCallback::addCallback($source['taskid'], 'mlDownload', $source['sourceid']); + TaskmanagerCallback::addCallback($source['taskid'], 'mlGotList', $source['sourceid']); } Database::exec('UNLOCK TABLES'); return count($list); } + /** + * Called when downloading metadata from a specific update source is finished + * @param mixed $task task structure + * @param string $sourceid see minilinux_source table + */ public static function listDownloadCallback($task, $sourceid) { + if ($task['statusCode'] !== 'TASK_FINISHED') + return; $taskId = $task['id']; $data = json_decode($task['data']['content'], true); if (!is_array($data)) { EventLog::warning('Cannot download Linux version meta data for ' . $sourceid); - error_log(print_r($task, true)); $lastupdate = 'lastupdate'; } else { if (isset($data['systems']) && is_array($data['systems'])) { @@ -141,6 +161,10 @@ class MiniLinux return preg_match('/^[a-z0-9_\-]+$/', $str) > 0; } + /* + * Download of specific version + */ + public static function validateDownloadTask($versionid, $taskid) { if ($taskid === null) @@ -193,7 +217,6 @@ class MiniLinux 'gpg' => $file['gpg'], ]; } - error_log(print_r($list, true)); $uuid = Util::randomUuid(); Database::exec('LOCK TABLES minilinux_version WRITE'); $aff = Database::exec('UPDATE minilinux_version SET taskid = :taskid WHERE versionid = :versionid AND taskid IS NULL', @@ -206,7 +229,6 @@ class MiniLinux 'files' => $list, ]); if (Taskmanager::isFailed($task)) { - error_log(print_r($task, true)); $task = false; } else { $task = $task['id']; @@ -215,6 +237,10 @@ class MiniLinux $task = false; } Database::exec('UNLOCK TABLES'); + if ($task !== false) { + // Callback for db column + TaskmanagerCallback::addCallback($task, 'mlGotLinux', $versionid); + } if ($aff === 0) return self::downloadVersion($versionid); return $task; @@ -225,4 +251,108 @@ class MiniLinux return 'x' . substr(md5($fileName . $versionid), 0, 8); } + /* + * Check status, availability of updates + */ + + /** + * Geenrate messages regarding setup und update availability. + * @return bool true if severe problems were found, false otherwise + */ + public static function generateUpdateNotice() + { + // Messages in here are with module name, as required by the + // main-warning hook. + $default = Property::get(self::PROPERTY_DEFAULT_BOOT); + if ($default === false) { + Message::addError('minilinux.no-default-set', true); + return true; + } + $installed = self::updateCurrentBootSetting(); + $effective = Property::get(self::PROPERTY_DEFAULT_BOOT_EFFECTIVE); + $slashes = substr_count($default, '/'); + if ($slashes === 1) { + // BrĂ´nche, always latest version + $latest = Database::queryFirst('SELECT versionid FROM minilinux_version + WHERE branchid = :branchid ORDER BY dateline DESC', ['branchid' => $default]); + if ($latest === false) { + Message::addError('minilinux.default-is-invalid', true); + return true; + } elseif ($latest['versionid'] !== $effective) { + Message::addInfo('minilinux.default-update-available', true, $default, $latest['versionid']); + } + } elseif ($slashes === 2) { + // Specific version selected + if ($effective === self::INVALID) { + Message::addError('minilinux.default-is-invalid', true); + return true; + } + } + if (!$installed) { + Message::addError('minilinux.default-not-installed', true, $default); + return true; + } + return false; + } + + /** + * Update the effective current default version to boot. + * If the version does not exist, it is set to INVALID. + * Function returns whether the currently selected version is + * actually installed locally. + * @return bool true if installed locally, false otherwise + */ + public static function updateCurrentBootSetting() + { + $default = Property::get(self::PROPERTY_DEFAULT_BOOT); + if ($default === false) + return false; + $slashes = substr_count($default, '/'); + if ($slashes === 2) { + // Specific version + $ver = Database::queryFirst('SELECT versionid, installed FROM minilinux_version + WHERE versionid = :versionid', ['versionid' => $default]); + } elseif ($slashes === 1) { + // Latest from branch + $ver = Database::queryFirst('SELECT versionid, installed FROM minilinux_version + WHERE branchid = :branchid AND installed = 1 ORDER BY dateline DESC', ['branchid' => $default]); + } else { + // Unknown + return false; + } + // Determine state + if ($ver === false) { // Doesn't exist + Property::set(self::PROPERTY_DEFAULT_BOOT_EFFECTIVE, self::INVALID); + return false; + } + Property::set(self::PROPERTY_DEFAULT_BOOT_EFFECTIVE, $ver['versionid']); + return $ver['installed'] != 0; + } + + public static function linuxDownloadCallback($task, $versionid) + { + self::setInstalledState($versionid, $task['statusCode'] === 'TASK_FINISHED'); + } + + public static function setInstalledState($versionid, $installed) + { + settype($installed, 'int'); + error_log("Setting $versionid to $installed"); + Database::exec('UPDATE minilinux_version SET installed = :installed WHERE versionid = :versionid', [ + 'versionid' => $versionid, + 'installed' => $installed, + ]); + } + + public static function queryAllVersionsByBranch() + { + $list = []; + $res = Database::simpleQuery('SELECT branchid, versionid, title, dateline, orphan, taskid, installed + FROM minilinux_version ORDER BY branchid, dateline, versionid'); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $list[$row['branchid']][$row['versionid']] = $row; + } + return $list; + } + }
\ No newline at end of file diff --git a/modules-available/minilinux/install.inc.php b/modules-available/minilinux/install.inc.php index 50be13a5..5387542e 100644 --- a/modules-available/minilinux/install.inc.php +++ b/modules-available/minilinux/install.inc.php @@ -26,9 +26,11 @@ $result[] = tableCreate('minilinux_version', " `data` blob NOT NULL, `orphan` tinyint(3) UNSIGNED NOT NULL, `taskid` char(36) CHARACTER SET ascii DEFAULT NULL, + `installed` tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (`versionid`), KEY (`title`), - KEY (`branchid`, `dateline`, `versionid`) + KEY (`branchid`, `dateline`, `versionid`), + KEY (`branchid`, `installed`, `dateline`) "); $result[] = tableAddConstraint('minilinux_version', 'branchid', 'minilinux_branch', 'branchid', diff --git a/modules-available/minilinux/lang/de/messages.json b/modules-available/minilinux/lang/de/messages.json index 1c38a8c1..e957ee09 100644 --- a/modules-available/minilinux/lang/de/messages.json +++ b/modules-available/minilinux/lang/de/messages.json @@ -1,5 +1,9 @@ { + "default-is-invalid": "Gew\u00e4hltes Linux-Standardsystem ist ung\u00fcltig", + "default-not-installed": "Gew\u00e4hltes Linux-Standardsystem {{0}} ist nicht (mehr) installiert", + "default-update-available": "F\u00fcr das Gew\u00e4hlte Linux-Standardsystem {{0}} ist die Aktualisierung {{1}} verf\u00fcgbar", "delete-error": "Fehler beim L\u00f6schen der Version {{0}}: {{1}}", + "no-default-set": "Kein Linux-Standardsystem festgelegt", "no-such-version": "Ung\u00fcltige\/Unbekannte Version: {{0}}", "please-download-minilinux": "Wichtige Dateien der MiniLinux-Installation fehlen", "version-deleted": "Version {{0}} wurde gel\u00f6scht" diff --git a/modules-available/minilinux/lang/de/module.json b/modules-available/minilinux/lang/de/module.json index 3ad539ca..da6ea4bb 100644 --- a/modules-available/minilinux/lang/de/module.json +++ b/modules-available/minilinux/lang/de/module.json @@ -1,8 +1,13 @@ { + "default_boot_entry": "(Globalen Standard verwenden)", "file-checksum-bad": "Pr\u00fcfsummenfehler", "file-missing": "Datei fehlt", + "file-not-readable": "Datei nicht lesbar", "file-ok": "OK", "file-size-mismatch": "Dateigr\u00f6\u00dfe stimmt nicht", + "ipxe-debug": "Debug-Ausgaben statt Bootlogo", + "ipxe-kcl-extra": "Modifikation der Kernel-Command-Line", "module_name": "bwLehrpool MiniLinux", + "not_installed_hint": "(nicht installiert)", "page_title": "Linuxvarianten f\u00fcr Netboot verwalten" }
\ No newline at end of file diff --git a/modules-available/minilinux/lang/de/template-tags.json b/modules-available/minilinux/lang/de/template-tags.json index 144887c8..2054896c 100644 --- a/modules-available/minilinux/lang/de/template-tags.json +++ b/modules-available/minilinux/lang/de/template-tags.json @@ -1,14 +1,19 @@ { + "lang_branchesHeading": "Verf\u00fcgbare Varianten und Versionen", + "lang_changelog": "Changelog", "lang_confirmDeleteVersion": "Diese Version wirklich l\u00f6schen?", "lang_download": "Herunterladen", "lang_id": "ID", + "lang_installed": "Installiert", "lang_introText": "Hier gibts MiniLinux.", "lang_key": "GPG-Key", "lang_lastUpdate": "Zuletzt \u00fcberpr\u00fcft", "lang_minilinuxHeading": "Netboot Linux verwalten", "lang_orphanedVersion": "Verwaiste Version", "lang_releaseDate": "Ver\u00f6ffentlichungsdatum", + "lang_selectedDefaultIs": "Gew\u00e4hltes Standardsystem ist", "lang_sources": "Quellen", + "lang_sourcesIntro": "Liste der Quellen, aus denen Updates bezogen werden k\u00f6nnen.", "lang_title": "Titel", "lang_updateSourcesButton": "Nach neuen Updates suchen", "lang_url": "URL", diff --git a/modules-available/minilinux/lang/en/module.json b/modules-available/minilinux/lang/en/module.json index 899435e7..b1526869 100644 --- a/modules-available/minilinux/lang/en/module.json +++ b/modules-available/minilinux/lang/en/module.json @@ -1,6 +1,7 @@ { "file-checksum-bad": "Bad checksum", "file-missing": "File missing", + "file-not-readable": "File not readable", "file-ok": "OK", "file-size-mismatch": "File size mismatch", "module_name": "Minilinux", diff --git a/modules-available/minilinux/page.inc.php b/modules-available/minilinux/page.inc.php index b54050bf..53632699 100644 --- a/modules-available/minilinux/page.inc.php +++ b/modules-available/minilinux/page.inc.php @@ -18,6 +18,8 @@ class Page_MiniLinux extends Page $this->deleteVersion(); } elseif ($show === 'updatesources') { $this->updateSources(); + } elseif ($show === 'setdefault') { + $this->setDefault(); } Util::redirect('?do=minilinux'); } @@ -27,15 +29,21 @@ class Page_MiniLinux extends Page protected function doRender() { - Render::addTemplate('page-minilinux'); + Render::addTemplate('page-minilinux', ['default' => Property::get(MiniLinux::PROPERTY_DEFAULT_BOOT)]); + // Warning + if (!MiniLinux::updateCurrentBootSetting()) { + Message::addError('default-not-installed', Property::get(MiniLinux::PROPERTY_DEFAULT_BOOT)); + } // List branches and versions $branches = Database::queryAll('SELECT sourceid, branchid, title FROM minilinux_branch ORDER BY title ASC'); - $versions = $this->queryAllVersionsByBranch(); + $versions = MiniLinux::queryAllVersionsByBranch(); + // Group by branch for detailed listing foreach ($branches as &$branch) { if (isset($versions[$branch['branchid']])) { $branch['versionlist'] = $this->renderVersionList($versions[$branch['branchid']]); } } + unset($branch); Render::addTemplate('branches', ['branches' => $branches]); // List sources $res = Database::simpleQuery('SELECT sourceid, title, url, lastupdate, pubkey FROM minilinux_source ORDER BY title, sourceid'); @@ -60,35 +68,34 @@ class Page_MiniLinux extends Page User::load(); $show = Request::post('show', false, 'string'); if ($show === 'version') { - $this->ajaxVersion(); + $this->ajaxVersionDetails(); } elseif ($show === 'download') { $this->ajaxDownload(); } } - private function queryAllVersionsByBranch() - { - $list = []; - $res = Database::simpleQuery('SELECT branchid, versionid, title, dateline, orphan, taskid - FROM minilinux_version ORDER BY branchid, dateline, versionid'); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - $list[$row['branchid']][] = $row; - } - return $list; - } - private function renderVersionList($versions) { + $def = Property::get(MiniLinux::PROPERTY_DEFAULT_BOOT); + $eff = Property::get(MiniLinux::PROPERTY_DEFAULT_BOOT_EFFECTIVE); foreach ($versions as &$version) { $version['dateline_s'] = Util::prettyTime($version['dateline']); $version['orphan'] = ($version['orphan'] > 5); - $version['installed'] = is_dir(CONFIG_HTTP_DIR . '/' . $version['versionid']); $version['downloading'] = $version['taskid'] && Taskmanager::isRunning(Taskmanager::status($version['taskid'])); + if ($version['installed'] && $version['versionid'] !== $def) { + $version['showsetdefault'] = true; + } + if ($version['versionid'] === $def) { + $version['isdefault'] = true; + if (!$version['installed']) { + $version['default_class'] = 'bg-danger'; + } + } } - return Render::parse('versionlist', ['versions' => $versions]); + return Render::parse('versionlist', ['versions' => array_values($versions)]); } - private function ajaxVersion() + private function ajaxVersionDetails() { User::assertPermission('view'); $verify = Request::post('verify', false, 'bool'); @@ -96,7 +103,7 @@ class Page_MiniLinux extends Page if ($versionid === false) { die('What!'); } - $ver = Database::queryFirst('SELECT versionid, taskid, data FROM minilinux_version WHERE versionid = :versionid', + $ver = Database::queryFirst('SELECT versionid, taskid, data, installed FROM minilinux_version WHERE versionid = :versionid', ['versionid' => $versionid]); if ($ver === false) { die('No such version'); @@ -110,6 +117,7 @@ class Page_MiniLinux extends Page $data['dltask'] = MiniLinux::validateDownloadTask($versionid, $ver['taskid']); $data['verify_button'] = !$verify && $data['dltask'] === false; if (is_array($data['files'])) { + $valid = true; $sort = []; foreach ($data['files'] as &$file) { if (empty($file['name'])) { @@ -119,8 +127,7 @@ class Page_MiniLinux extends Page $sort[] = $file['name']; $s = $this->getFileState($versionid, $file, $verify); if ($s !== self::FILE_OK) { - $data['verify_button'] = false; - $data['download_button'] = !$data['dltask']; + $valid = false; } if ($s !== self::FILE_MISSING) { $data['delete_button'] = true; @@ -138,6 +145,15 @@ class Page_MiniLinux extends Page } unset($file); array_multisort($sort, SORT_ASC, $data['files']); + if (!$valid) { + $data['verify_button'] = false; + $data['download_button'] = !$data['dltask']; + if ($ver['installed']) { + MiniLinux::setInstalledState($versionid, false); + } + } elseif (!$ver['installed'] && $verify) { + MiniLinux::setInstalledState($versionid, true); + } } echo Render::parse('filelist', $data); } @@ -146,6 +162,7 @@ class Page_MiniLinux extends Page const FILE_MISSING = 1; const FILE_SIZE_MISMATCH = 2; const FILE_CHECKSUM_BAD = 3; + const FILE_NOT_READABLE = 4; private function getFileState($versionid, $file, $verify) { @@ -154,11 +171,17 @@ class Page_MiniLinux extends Page return self::FILE_MISSING; if (isset($file['size']) && filesize($path) != $file['size']) return self::FILE_SIZE_MISMATCH; + if (!is_readable($path)) + return self::FILE_NOT_READABLE; if ($verify) { - // TODO: Others - if (isset($file['md5'])) { - if (md5_file($path) !== $file['md5']) - return self::FILE_CHECKSUM_BAD; + foreach (['sha512', 'sha384', 'sha256', 'sha224', 'sha1', 'md5'] as $algo) { + if (isset($file[$algo])) { + $calced = hash_file($algo, $path); + if ($calced === false) + continue; // Algo not supported? + if ($calced !== $file['md5']) + return self::FILE_CHECKSUM_BAD; + } } } return self::FILE_OK; @@ -173,6 +196,8 @@ class Page_MiniLinux extends Page return Dictionary::translate('file-size-mismatch', true); case self::FILE_MISSING: return Dictionary::translate('file-missing', true); + case self::FILE_NOT_READABLE: + return Dictionary::translate('file-not-readable', true); case self::FILE_OK: return Dictionary::translate('file-ok', true); } @@ -191,7 +216,7 @@ class Page_MiniLinux extends Page Message::addError('no-such-version', $version); Message::renderList(); } else { - $this->ajaxVersion(); + $this->ajaxVersionDetails(); } } @@ -209,6 +234,7 @@ class Page_MiniLinux extends Page Message::addError('no-such-version'); return; } + MiniLinux::setInstalledState($version['versionid'], false); $path = CONFIG_HTTP_DIR . '/' . $version['versionid']; $task = Taskmanager::submit('DeleteDirectory', [ 'path' => $path, @@ -233,4 +259,20 @@ class Page_MiniLinux extends Page } } + private function setDefault() + { + $versionid = Request::post('version', false, 'string'); + if ($versionid === false) { + Message::addError('main.parameter-missing', 'versionid'); + return; + } + $version = Database::queryFirst('SELECT versionid FROM minilinux_version WHERE versionid = :versionid', + ['versionid' => $versionid]); + if ($version === false) { + Message::addError('no-such-version'); + return; + } + Property::set(MiniLinux::PROPERTY_DEFAULT_BOOT, $version['versionid']); + } + } diff --git a/modules-available/minilinux/templates/branches.html b/modules-available/minilinux/templates/branches.html index 1ba9497c..64adda16 100644 --- a/modules-available/minilinux/templates/branches.html +++ b/modules-available/minilinux/templates/branches.html @@ -1,17 +1,19 @@ +<h3>{{lang_branchesHeading}}</h3> {{#branches}} <div id="ibm-mainframe" class="panel panel-default"> <div class="panel-heading"> <div class="pull-right"> {{sourceid}} {{branchid}} </div> - {{title}} + <b>{{title}}</b> </div> <div class="panel-body"> - + {{description}} </div> {{{versionlist}}} </div> {{/branches}} + <script> document.addEventListener('DOMContentLoaded', function () { var addHandlers = function(parent) { @@ -44,13 +46,21 @@ }; $('.version-link').click(function(e) { e.preventDefault(); - $(this).removeClass('version-link').off().removeAttr('href'); - loadDetails($(this).data('version'), { show: "version" }); + var ver = $(this).data('version'); + var c = $('.version-container[data-version="' + ver + '"]'); + if (c.is(':visible')) { + c.hide(); + } else if (c.is(':empty')) { + loadDetails(ver, {show: "version"}); + } else { + c.show(); + } }); addHandlers($('#ibm-mainframe')); $('[data-autoclick="true"]').click(); }); var taskDone = {}; + var wasUnfinished = false; function dlTmCb(task) { if (!task.data || !task.data.files) return; @@ -69,5 +79,7 @@ taskDone[id] = wasDone ? true : f.progress; } } + if (wasUnfinished && task.statusCode === 'TASK_FINISHED') location.reload(true); + if (task.statusCode !== 'TASK_FINISHED') wasUnfinished = true; } </script>
\ No newline at end of file diff --git a/modules-available/minilinux/templates/filelist.html b/modules-available/minilinux/templates/filelist.html index 2c26edf9..9aa175bd 100644 --- a/modules-available/minilinux/templates/filelist.html +++ b/modules-available/minilinux/templates/filelist.html @@ -50,4 +50,8 @@ {{#dltask}} <div class="hidden" data-tm-id="{{dltask}}" data-tm-callback="dlTmCb"></div> {{/dltask}} +{{#changelog}} +<h4>{{lang_changelog}}</h4> +{{changelog}} +{{/changelog}} <div class="slx-space"></div>
\ No newline at end of file diff --git a/modules-available/minilinux/templates/page-minilinux.html b/modules-available/minilinux/templates/page-minilinux.html index 2cbde608..3059e827 100644 --- a/modules-available/minilinux/templates/page-minilinux.html +++ b/modules-available/minilinux/templates/page-minilinux.html @@ -1,3 +1,5 @@ <h1>{{lang_minilinuxHeading}}</h1> -<p>{{lang_introText}}</p>
\ No newline at end of file +<p>{{lang_introText}}</p> + +{{lang_selectedDefaultIs}}: <b>{{default}}</b>
\ No newline at end of file diff --git a/modules-available/minilinux/templates/sources.html b/modules-available/minilinux/templates/sources.html index f2e54745..dabc7f4d 100644 --- a/modules-available/minilinux/templates/sources.html +++ b/modules-available/minilinux/templates/sources.html @@ -1,42 +1,41 @@ -<div class="panel panel-default"> - <div class="panel-heading"> - {{lang_sources}} - </div> - <table class="table table-condensed"> - <thead> - <tr> - <th>{{lang_id}}</th> - <th>{{lang_title}}</th> - <th>{{lang_url}}</th> - <th>{{lang_lastUpdate}}</th> - <th>{{lang_key}}</th> - </tr> - </thead> - <tbody> - {{#list}} - <tr> - <td class="small">{{sourceid}}</td> - <td>{{title}}</td> - <td class="small">{{url}}</td> - <td class="{{update_class}}">{{lastupdate_s}}</td> - <td class="text-center"> - <button type="button" class="btn btn-default btn-xs" data-confirm="#confirm-{{source}}" data-close="{{lang_close}}"> - <span class="glyphicon glyphicon-eye-open"></span> - </button> - <pre id="confirm-{{source}}" class="hidden">{{pubkey}}</pre> - </td> - </tr> - {{/list}} - </tbody> - </table> - <div class="panel-body text-right"> - <form method="post" action="?do=minilinux"> - <input type="hidden" name="token" value="{{token}}"> - <button type="submit" name="show" value="updatesources" class="btn btn-default" - onclick="$(this).find('.glyphicon').addClass('slx-rotation')" {{^show_refresh}}disabled{{/show_refresh}}> - <span class="glyphicon glyphicon-refresh"></span> - {{lang_updateSourcesButton}} +<h3>{{lang_sources}}</h3> + +<p>{{lang_sourcesIntro}}</p> + +<table class="table table-condensed"> + <thead> + <tr> + <th>{{lang_id}}</th> + <th>{{lang_title}}</th> + <th>{{lang_url}}</th> + <th>{{lang_lastUpdate}}</th> + <th>{{lang_key}}</th> + </tr> + </thead> + <tbody> + {{#list}} + <tr> + <td class="small">{{sourceid}}</td> + <td>{{title}}</td> + <td class="small">{{url}}</td> + <td class="{{update_class}}">{{lastupdate_s}}</td> + <td class="text-center"> + <button type="button" class="btn btn-default btn-xs" data-confirm="#confirm-{{source}}" data-close="{{lang_close}}"> + <span class="glyphicon glyphicon-eye-open"></span> </button> - </form> - </div> + <pre id="confirm-{{source}}" class="hidden">{{pubkey}}</pre> + </td> + </tr> + {{/list}} + </tbody> +</table> +<div class="text-right"> + <form method="post" action="?do=minilinux"> + <input type="hidden" name="token" value="{{token}}"> + <button type="submit" name="show" value="updatesources" class="btn btn-default" + onclick="$(this).find('.glyphicon').addClass('slx-rotation')" {{^show_refresh}}disabled{{/show_refresh}}> + <span class="glyphicon glyphicon-refresh"></span> + {{lang_updateSourcesButton}} + </button> + </form> </div>
\ No newline at end of file diff --git a/modules-available/minilinux/templates/versionlist.html b/modules-available/minilinux/templates/versionlist.html index 1e5c7c96..4ef4e631 100644 --- a/modules-available/minilinux/templates/versionlist.html +++ b/modules-available/minilinux/templates/versionlist.html @@ -1,22 +1,45 @@ <table class="table table-striped"> <tr> - <th>{{lang_version}}</th> - <th>{{lang_releaseDate}}</th> + <th class="slx-smallcol">{{lang_version}}</th> + <th class="slx-smallcol">{{lang_releaseDate}}</th> <th>{{lang_title}}</th> <th class="slx-smallcol"></th> <th class="slx-smallcol"></th> + <th class="slx-smallcol"></th> </tr> {{#versions}} <tr> - <td> + <td class="text-nowrap"> <a href="#" class="version-link" data-version="{{versionid}}" {{#downloading}}data-autoclick="true"{{/downloading}}> {{versionid}} <b class="caret"></b> </a> </td> - <td>{{dateline_s}}</td> + <td class="text-nowrap">{{dateline_s}}</td> <td>{{title}}</td> - <td> + <td class="text-nowrap"> + {{#orphan}} + {{lang_orphanedVersion}} + {{/orphan}} + </td> + <td class="text-nowrap text-center {{default_class}}"> + {{#showsetdefault}} + <form method="post" action="?do=minilinux" style="margin:0;padding:0;display:inline"> + <input type="hidden" name="token" value="{{token}}"> + <input type="hidden" name="version" value="{{versionid}}"> + <button type="submit" name="show" value="setdefault" class="btn btn-xs btn-info"> + <span class="glyphicon glyphicon-flag"></span> + </button> + </form> + {{/showsetdefault}} + {{#isdefault}} + <span class="glyphicon glyphicon-ok"></span> + {{/isdefault}} + </td> + <td class="text-nowrap text-center"> + {{#installed}} + <span class="btn btn-default btn-xs disabled">{{lang_installed}}</span> + {{/installed}} {{^installed}} {{^downloading}} <button type="button" class="btn btn-xs btn-success btn-download" data-version="{{versionid}}"> @@ -26,14 +49,9 @@ {{/downloading}} {{/installed}} </td> - <td> - {{#orphan}} - {{lang_orphanedVersion}} - {{/orphan}} - </td> </tr> <tr> - <td colspan="5" class="version-container collapse" data-version="{{versionid}}"></td> + <td colspan="6" class="version-container collapse" data-version="{{versionid}}"></td> </tr> {{/versions}} </table>
\ No newline at end of file diff --git a/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php b/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php index e97d1389..174f4459 100644 --- a/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php +++ b/modules-available/serversetup-bwlp-ipxe/inc/bootentry.inc.php @@ -260,6 +260,7 @@ class StandardBootEntry extends BootEntry } else { $entry = $this->efi; } + $entry->sanitize(); $script = ''; if ($entry->resetConsole) { diff --git a/modules-available/serversetup-bwlp-ipxe/inc/bootentryhook.inc.php b/modules-available/serversetup-bwlp-ipxe/inc/bootentryhook.inc.php index 2e2e5009..f89031a3 100644 --- a/modules-available/serversetup-bwlp-ipxe/inc/bootentryhook.inc.php +++ b/modules-available/serversetup-bwlp-ipxe/inc/bootentryhook.inc.php @@ -14,18 +14,24 @@ abstract class BootEntryHook private $selectedId; + private $data = []; + + /** + * @return string + */ public abstract function name(); /** - * @return HookEntryGroup[] + * @return HookExtraField[] */ - protected abstract function groupsInternal(); + public abstract function extraFields(); + + public abstract function isValidId($id); /** - * @param $id - * @return BootEntry|null the actual boot entry instance for given entry, null if invalid id + * @return HookEntryGroup[] */ - public abstract function getBootEntry($id); + protected abstract function groupsInternal(); /** * @return HookEntryGroup[] @@ -43,11 +49,40 @@ abstract class BootEntryHook return $groups; } + /** + * @param $id + * @return BootEntry|null the actual boot entry instance for given entry, null if invalid id + */ + public abstract function getBootEntryInternal($data); + + public final function getBootEntry($data) + { + if (!is_array($data)) { + $data = json_decode($data, true); + } + return $this->getBootEntryInternal($data); + } + public function setSelected($id) { + $json = @json_decode($id, true); + if (is_array($json)) { + $id = $json['id']; + $this->data = $json; + } $this->selectedId = $id; } + public function renderExtraFields() + { + $list = $this->extraFields(); + foreach ($list as &$entry) { + $entry->currentValue = isset($this->data[$entry->name]) ? $this->data[$entry->name] : $entry->default; + $entry->hook = $this; + } + return $list; + } + } class HookEntryGroup @@ -79,13 +114,100 @@ class HookEntry */ public $name; /** + * @var bool + */ + public $valid; + /** + * @var string if !valid, this will be the string 'disabled', empty otherwise + */ + public $disabled; + /** * @var string internal - to be set by ipxe module */ public $selected; - public function __construct($id, $name) + /** + * HookEntry constructor. + * + * @param string $id + * @param string $name + * @param bool $valid + */ + public function __construct($id, $name, $valid) { $this->id = $id; $this->name = $name; + $this->valid = $valid; + $this->disabled = $valid ? '' : 'disabled'; } +} + +class HookExtraField +{ + /** + * @var string ID of extra field, [a-z0-9\-] please. Must not be 'id' + */ + public $name; + /** + * @var string type of field, use string, bool, or an array of predefined options + */ + public $type; + /** + * @var mixed default value + */ + public $default; + + public $currentValue; + + /** + * @var BootEntryHook + */ + public $hook; + + public function __construct($name, $type, $default) + { + $this->name = $name; + $this->type = $type; + $this->default = $default; + } + + public function fromPost($typePrefix) + { + if (is_array($this->type)) { + $val = Request::post('extra-' . $typePrefix . '-' . $this->name, '', 'array'); + if (!in_array($val, $this->type)) { + $val = $this->default; + } + } else { + $val = Request::post('extra-' . $typePrefix . '-' . $this->name, '', $this->type); + settype($val, $this->type); + } + return $val; + } + + public function html() + { + $fieldId = 'extra-' . $this->hook->moduleId . '-' . $this->name; + $fieldText = htmlspecialchars(Dictionary::translateFileModule($this->hook->moduleId, 'module', 'ipxe-' . $this->name, true)); + if (is_array($this->type)) { + $out = '<label for="' . $fieldId . '">' . $fieldText . '</label><select class="form-control" name="' . $fieldId . '" id="' . $fieldId . '">'; + foreach ($this->type as $entry) { + $selected = ($entry === $this->currentValue) ? 'selected' : ''; + $out .= '<option ' . $selected . '>' . htmlspecialchars($entry) . '</option>'; + } + $out .= '</select>'; + return $out; + } + if ($this->type === 'bool') { + $checked = $this->currentValue ? 'checked' : ''; + return '<div class="checkbox"><input type="checkbox" id="' . $fieldId + . '" name="' . $fieldId . '" ' . $checked . '><label for="' . $fieldId . '">' + . $fieldText . '</label></div>'; + } + // Default + return '<label for="' . $fieldId . '">' . $fieldText . '</label>' + . '<input class="form-control" type="text" id="' . $fieldId + . '" name="' . $fieldId . '" value="' . htmlspecialchars($this->currentValue) . '">'; + } + }
\ No newline at end of file diff --git a/modules-available/serversetup-bwlp-ipxe/inc/execdata.inc.php b/modules-available/serversetup-bwlp-ipxe/inc/execdata.inc.php index b82ce2e7..97f98b94 100644 --- a/modules-available/serversetup-bwlp-ipxe/inc/execdata.inc.php +++ b/modules-available/serversetup-bwlp-ipxe/inc/execdata.inc.php @@ -73,7 +73,7 @@ class ExecData ], ]; - private function sanitize() + public function sanitize() { settype($this->executable, 'string'); settype($this->initRd, 'array'); diff --git a/modules-available/serversetup-bwlp-ipxe/lang/de/template-tags.json b/modules-available/serversetup-bwlp-ipxe/lang/de/template-tags.json index 07cf8fa3..f7a62091 100644 --- a/modules-available/serversetup-bwlp-ipxe/lang/de/template-tags.json +++ b/modules-available/serversetup-bwlp-ipxe/lang/de/template-tags.json @@ -11,6 +11,7 @@ "lang_biosOnly": "Nur BIOS", "lang_bootAddress": "Boot-Adresse des Servers", "lang_bootEntryData": "Daten des Men\u00fceintrags", + "lang_bootEntryDetailsHeading": "Typspezifische Konfiguration", "lang_bootentryDeleteConfirm": "Sind Sie sicher, dass Sie diesen Men\u00fceintrag l\u00f6schen wollen?", "lang_bootentryHead": "Men\u00fceintr\u00e4ge", "lang_bootentryIntro": "Hier k\u00f6nnen Sie Men\u00fceintr\u00e4ge definieren, die sich sp\u00e4ter einem Men\u00fc zuweisen lassen. Ein Men\u00fceintrag besteht entweder aus einem zu ladenden Kernel\/Image plus optional initrd, oder aus einem iPXE-Skript.", @@ -38,6 +39,7 @@ "lang_forceRecompile": "Jetzt neu kompilieren", "lang_generationFailed": "Erzeugen des Bootmen\u00fcs fehlgeschlagen. Der Netzwerkboot von bwLehrpool wird wahrscheinlich nicht funktionieren. Wenn Sie den Fehler nicht selbst beheben k\u00f6nnen, melden Sie bitte die Logausgabe an das bwLehrpool-Projekt.", "lang_hex": "Hex", + "lang_hookExtraOptionHeading": "Weitere Angaben", "lang_hotkey": "Hotkey", "lang_idFormatHint": "(Max. 16 Zeichen, nur a-z 0-9 - _)", "lang_imageToLoad": "Zu ladendes Image (z.B. Kernel)", diff --git a/modules-available/serversetup-bwlp-ipxe/page.inc.php b/modules-available/serversetup-bwlp-ipxe/page.inc.php index 40b75dd2..f14c1e57 100644 --- a/modules-available/serversetup-bwlp-ipxe/page.inc.php +++ b/modules-available/serversetup-bwlp-ipxe/page.inc.php @@ -519,25 +519,25 @@ class Page_ServerSetup extends Page $params['oldentryid'] = $params['entryid'] = $row['entryid']; $params['builtin'] = $row['builtin']; } - if (!is_array($params['entries'])) { - $params['entries'] = []; - } - $f = []; - foreach ($params['entries'] as $e) { - if (isset($e['mode'])) { - $f[] = $e['mode']; - } - } - if (!in_array('PCBIOS', $f)) { - $params['entries'][] = ['mode' => 'PCBIOS']; - } - if (!in_array('EFI', $f)) { - $params['entries'][] = ['mode' => 'EFI']; - } $params['menus'] = Database::queryAll('SELECT m.menuid, m.title FROM serversetup_menu m INNER JOIN serversetup_menuentry me ON (me.menuid = m.menuid) WHERE me.entryid = :entryid', ['entryid' => $row['entryid']]); } + if (!isset($params['entries']) || !is_array($params['entries'])) { + $params['entries'] = []; + } + $f = []; + foreach ($params['entries'] as $e) { + if (isset($e['mode'])) { + $f[] = $e['mode']; + } + } + if (!in_array('PCBIOS', $f)) { + $params['entries'][] = ['mode' => 'PCBIOS']; + } + if (!in_array('EFI', $f)) { + $params['entries'][] = ['mode' => 'EFI']; + } $params['disabled'] = User::hasPermission('ipxe.bootentry.edit') ? '' : 'disabled'; Render::addTemplate('ipxe-new-boot-entry', $params); @@ -823,12 +823,19 @@ class Page_ServerSetup extends Page } /** @var BootEntryHook $module */ $module = $hook->run(); - $entryData = Request::post('selection-' . $type, false, 'string'); - $entry = $module->getBootEntry($entryData); + $id = Request::post('selection-' . $type, false, 'string'); + $entry = $module->isValidId($id); if ($entry === null) { - Message::addError('invalid-custom-entry-id', $type, $entryData); + Message::addError('invalid-custom-entry-id', $type, $id); return; } + $entryData = ['id' => $id]; + foreach ($module->extraFields() as $field) { + if ($field->name === 'id') + continue; + $entryData[$field->name] = $field->fromPost($type); + } + $entryData = json_encode($entryData); } $params = [ 'entryid' => $newId, diff --git a/modules-available/serversetup-bwlp-ipxe/templates/ipxe-new-boot-entry.html b/modules-available/serversetup-bwlp-ipxe/templates/ipxe-new-boot-entry.html index fd76c1d5..5b7e3134 100644 --- a/modules-available/serversetup-bwlp-ipxe/templates/ipxe-new-boot-entry.html +++ b/modules-available/serversetup-bwlp-ipxe/templates/ipxe-new-boot-entry.html @@ -52,6 +52,8 @@ <input id="input-title" class="form-control" name="title" value="{{title}}" maxlength="100" {{disabled}}> </div> + <h4>{{lang_bootEntryDetailsHeading}}</h4> + <div class="type-form" id="form-exec"> <div class="form-group"> <label for="arch-selector"> @@ -170,12 +172,20 @@ {{#groups}} <optgroup label="{{groupName}}"> {{#entries}} - <option value="{{id}}" {{selected}}>{{name}}</option> + <option value="{{id}}" {{selected}} {{disabled}}>{{name}}</option> {{/entries}} </optgroup> {{/groups}} </select> </div> + <div class="form-group"> + {{#renderExtraFields.0}} + <h4>{{lang_hookExtraOptionHeading}}</h4> + {{/renderExtraFields.0}} + {{#renderExtraFields}} + {{{html}}} + {{/renderExtraFields}} + </div> </div> {{/hooks}} @@ -185,6 +195,8 @@ </div> {{/builtin}} + <hr> + <p class="slx-bold">{{lang_referencingMenus}}:</p> <ul> {{#menus}} |