mirror of
https://github.com/unraid/webgui.git
synced 2026-01-01 23:20:35 -06:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -18,9 +18,14 @@ Cond="exec(\"grep -o '^DOCKER_ENABLED=.yes' /boot/config/docker.cfg 2>/dev/null\
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
if ($var['fsState'] != 'Started') {
|
||||
echo "<div class='notice shift'>_(Array must be **Started** to view Docker containers)_.</div>";
|
||||
} elseif (!is_file('/var/run/dockerd.pid') || (!is_dir('/proc/'.@file_get_contents('/var/run/dockerd.pid')))) {
|
||||
echo "<div class='notice shift'>_(Docker Service failed to start)_.</div>";
|
||||
}
|
||||
$noticeMessage = null;
|
||||
if ($var['fsState'] != 'Started') {
|
||||
$noticeMessage = _('Array must be **Started** to view Docker containers');
|
||||
} elseif (!is_file('/var/run/dockerd.pid') || (!is_dir('/proc/'.@file_get_contents('/var/run/dockerd.pid')))) {
|
||||
$noticeMessage = _('Docker Service failed to start');
|
||||
}
|
||||
?>
|
||||
|
||||
<? if ($noticeMessage): ?>
|
||||
<p class="notice"><?= $noticeMessage ?></p>
|
||||
<? endif; ?>
|
||||
|
||||
@@ -9,40 +9,6 @@
|
||||
* all copies or substantial portions of the Software.
|
||||
*/
|
||||
?>
|
||||
<style>
|
||||
#header {
|
||||
z-index: 102 !important;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-pack: justify;
|
||||
-ms-flex-pack: justify;
|
||||
justify-content: space-between;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
#header unraid-i18n-host {
|
||||
font-size: 16px;
|
||||
margin-left: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tools page, rotate the Downgrade icon to prevent needing to add a new icon to the icon font.
|
||||
* The pseudo element is targeted here otherwise the rotation of the span would mess up spacing with the text.
|
||||
*/
|
||||
a[href="/Tools/Downgrade"] .icon-update:before {
|
||||
display: inline-block; /* required otherwise the rotation won't work */
|
||||
rotate: 180deg;
|
||||
}
|
||||
/* overriding #header .logo svg */
|
||||
#header .logo .partner-logo svg {
|
||||
fill: var(--header-text-primary);
|
||||
width: auto;
|
||||
height: 28px;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
require_once("$docroot/plugins/dynamix.my.servers/include/web-components-extractor.php");
|
||||
|
||||
|
||||
@@ -12,7 +12,4 @@ table tbody td {
|
||||
span.status.vhshift {
|
||||
margin-top: 8px !important;
|
||||
}
|
||||
table#plugin_table {
|
||||
margin-top: -43px !important;
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,19 @@ Cond="exec(\"grep -o '^SERVICE=.enable' /boot/config/domain.cfg 2>/dev/null\")"
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
if ($var['fsState'] != "Started") {
|
||||
echo "<div class='notice shift'>"._('Array must be **started** to view Virtual Machines').".</div>";
|
||||
} elseif (!is_file('/var/run/libvirt/libvirtd.pid') || (!is_dir('/proc/'.@file_get_contents('/var/run/libvirt/libvirtd.pid')))) {
|
||||
echo "<div class='notice shift'>"._('Libvirt Service failed to start').".</div>";
|
||||
$noticeMessage = null;
|
||||
if ($var['fsState'] != "Started") {
|
||||
$noticeMessage = _('Array must be **started** to view Virtual Machines');
|
||||
} elseif (!is_file('/var/run/libvirt/libvirtd.pid') || (!is_dir('/proc/'.@file_get_contents('/var/run/libvirt/libvirtd.pid')))) {
|
||||
$noticeMessage = _('Libvirt Service failed to start');
|
||||
}
|
||||
?>
|
||||
<? if ($noticeMessage): ?>
|
||||
<p class="notice"><?= $noticeMessage ?></p>
|
||||
<? endif; ?>
|
||||
|
||||
<?
|
||||
if (count($pages) == 2) {
|
||||
$tabbed = false;
|
||||
}
|
||||
if (count($pages)==2) $tabbed = false;
|
||||
?>
|
||||
@@ -51,7 +51,11 @@ function upload(lang) {
|
||||
}
|
||||
}
|
||||
function presetBanner(form) {
|
||||
if (form.banner.selectedIndex == 0) $('.js-bannerSettings').hide(); else $('.js-bannerSettings').show();
|
||||
if (form.banner && form.banner.selectedIndex == 0) {
|
||||
$('.js-bannerSettings').hide();
|
||||
} else {
|
||||
$('.js-bannerSettings').show()
|
||||
}
|
||||
}
|
||||
function presetRefresh(form) {
|
||||
for (var i=0,item; item=form.refresh.options[i]; i++) item.value *= -1;
|
||||
|
||||
@@ -2,10 +2,20 @@ Menu="Tasks:2"
|
||||
Type="xmenu"
|
||||
Code="e92a"
|
||||
---
|
||||
<?PHP
|
||||
if ($var['fsState']=="Stopped") {
|
||||
echo "<p class='notice shift'>"._('Array must be **Started** to view Shares').".</p>";
|
||||
return;
|
||||
}
|
||||
if (count($pages)==2) $tabbed = false;
|
||||
<?
|
||||
$noticeMessage = null;
|
||||
if ($var['fsState'] == "Stopped") {
|
||||
$noticeMessage = _('Array must be **Started** to view Shares');
|
||||
}
|
||||
?>
|
||||
|
||||
<? if ($noticeMessage): ?>
|
||||
<p class="notice"><?= $noticeMessage ?></p>
|
||||
<? return; ?>
|
||||
<? endif; ?>
|
||||
|
||||
<?
|
||||
if (count($pages) == 2) {
|
||||
$tabbed = false;
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -94,7 +94,6 @@ if (count($pages)) {
|
||||
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/default-color-palette.css")?>">
|
||||
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/default-base.css")?>">
|
||||
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/default-dynamix.css")?>">
|
||||
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/dynamix-jquery-ui.css")?>">
|
||||
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/themes/{$theme}.css")?>">
|
||||
|
||||
<style>
|
||||
|
||||
@@ -234,8 +234,6 @@ $(window).scroll(function() {
|
||||
$('.back_to_top').fadeOut(scrollDuration);
|
||||
}
|
||||
<?if ($themeHelper->isTopNavTheme()):?>
|
||||
var top = $('div#header').height()-1; // header height has 1 extra pixel to cover overlap
|
||||
$('div#menu').css($(this).scrollTop() > top ? {position:'fixed',top:'0'} : {position:'absolute',top:top+'px'});
|
||||
// banner
|
||||
$('div.upgrade_notice').css($(this).scrollTop() > 24 ? {position:'fixed',top:'0'} : {position:'absolute',top:'24px'});
|
||||
<?endif;?>
|
||||
@@ -285,8 +283,10 @@ $(function() {
|
||||
$(this).attr('onsubmit','clearTimeout(timers.flashReport);escapeQuotes(this);'+onsubmit);
|
||||
}
|
||||
});
|
||||
var top = ($.cookie('top')||0) - $('.tabs').offset().top - 75;
|
||||
if (top>0) {$('html,body').scrollTop(top);}
|
||||
const top = parseInt($.cookie('top') || '0', 10);
|
||||
if (top > 0) {
|
||||
$('html, body').scrollTop(top);
|
||||
}
|
||||
$.removeCookie('top');
|
||||
if ($.cookie('addAlert') != null) bannerAlert(addAlert.text,addAlert.cmd,addAlert.plg,addAlert.func);
|
||||
<?if ($safemode):?>
|
||||
|
||||
@@ -42,31 +42,41 @@ function getArrayStatus($var) {
|
||||
?>
|
||||
|
||||
<footer id="footer">
|
||||
<span id="statusraid">
|
||||
<span id="statusbar">
|
||||
<? $status = getArrayStatus($var); ?>
|
||||
<span class="<?=$status['class']?> strong">
|
||||
<i class="fa fa-<?=$status['icon']?>"></i> <?=$status['text']?>
|
||||
<div class="footer-left">
|
||||
<span id="statusraid">
|
||||
<span id="statusbar" aria-live="polite">
|
||||
<? $status = getArrayStatus($var); ?>
|
||||
<span class="<?=$status['class']?> strong">
|
||||
<i class="fa fa-<?=$status['icon']?>"></i> <?=$status['text']?>
|
||||
</span>
|
||||
<? if ($status['progress']): ?>
|
||||
•<span class="blue strong tour"><?=$status['progress']?></span>
|
||||
<? endif; ?>
|
||||
</span>
|
||||
<? if ($status['progress']): ?>
|
||||
•<span class="blue strong tour"><?=$status['progress']?></span>
|
||||
<? endif; ?>
|
||||
</span>
|
||||
</span>
|
||||
<span id="user-notice" class="red-text"></span>
|
||||
<? if ($wlan0): ?>
|
||||
<span id="wlan0" class="grey-text" onclick="wlanSettings()">
|
||||
<i class="fa fa-wifi fa-fw"></i>
|
||||
<span id="user-notice" class="red-text"></span>
|
||||
</div>
|
||||
<div class="footer-right">
|
||||
<span id="copyright">
|
||||
<unraid-theme-switcher
|
||||
current="<?=$theme?>"
|
||||
themes='<?=htmlspecialchars(json_encode(['azure', 'gray', 'black', 'white']), ENT_QUOTES, 'UTF-8')?>'>
|
||||
</unraid-theme-switcher>
|
||||
Unraid® webGui ©<?=releaseDateYear()?>, Lime Technology, Inc.
|
||||
<a
|
||||
class="footer-link"
|
||||
href="https://docs.unraid.net/go/manual/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="<?=_('Online manual')?>"
|
||||
>
|
||||
<i class="fa fa-book"></i> <?=_('manual')?>
|
||||
</a>
|
||||
</span>
|
||||
<? endif; ?>
|
||||
<span id="copyright">
|
||||
Unraid® webGui ©<?=releaseDateYear()?>, Lime Technology, Inc.
|
||||
<a href="https://docs.unraid.net/go/manual/" target="_blank" title="<?=_('Online manual')?>">
|
||||
<i class="fa fa-book"></i> <?=_('manual')?>
|
||||
</a>
|
||||
<unraid-theme-switcher
|
||||
current="<?=$theme?>"
|
||||
themes='<?=htmlspecialchars(json_encode(['azure', 'gray', 'black', 'white']), ENT_QUOTES, 'UTF-8')?>'>
|
||||
</unraid-theme-switcher>
|
||||
</span>
|
||||
<? if ($wlan0): ?>
|
||||
<span id="wlan0" class="grey-text" onclick="wlanSettings()">
|
||||
<i class="fa fa-wifi fa-fw"></i>
|
||||
</span>
|
||||
<? endif; ?>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -101,28 +101,12 @@ function refresh(top) {
|
||||
}
|
||||
}
|
||||
|
||||
function initab(page) {
|
||||
function initab(page) { // @todo remove in the future
|
||||
$.removeCookie('one');
|
||||
$.removeCookie('tab');
|
||||
if (page != null) location.replace(page);
|
||||
}
|
||||
|
||||
function settab(tab) {
|
||||
<?switch ($myPage['name']):?>
|
||||
<?case'Main':?>
|
||||
$.cookie('tab',tab);
|
||||
<?if (_var($var,'fsState')=='Started'):?>
|
||||
$.cookie('one','tab1');
|
||||
<?endif;?>
|
||||
<?break;?>
|
||||
<?case'Cache':case'Data':case'Device':case'Flash':case'Parity':?>
|
||||
$.cookie('one',tab);
|
||||
<?break;?>
|
||||
<?default:?>
|
||||
$.cookie('one',tab);
|
||||
<?endswitch;?>
|
||||
}
|
||||
|
||||
function done(key) {
|
||||
var url = location.pathname.split('/');
|
||||
var path = '/'+url[1];
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
<?
|
||||
<?php
|
||||
/**
|
||||
* Main content template for the Unraid web interface.
|
||||
* Handles the rendering of tabs and page content.
|
||||
* Main content delegator for the Unraid web interface.
|
||||
* Includes the correct template based on tabbed state.
|
||||
*
|
||||
* Even if DisplaySettings is not enabled for tabs, pages with Tabs="true" will use tabs
|
||||
* and pages with Tabs="false" will not use tabs.
|
||||
*/
|
||||
$display['tabs'] = isset($myPage['Tabs'])
|
||||
? (strtolower($myPage['Tabs']) == 'true' ? 0 : 1)
|
||||
: $display['tabs'];
|
||||
$tabbed = $display['tabs'] == 0 && count($pages) > 1;
|
||||
$contentInclude = $tabbed ? 'MainContentTabbed.php' : 'MainContentTabless.php';
|
||||
|
||||
$defaultIcon = "<i class=\"icon-app PanelIcon\"></i>";
|
||||
// Helper function to process icon
|
||||
function process_icon($icon, $docroot, $root) {
|
||||
global $defaultIcon;
|
||||
if (substr($icon, -4) == '.png') {
|
||||
@@ -26,75 +33,83 @@ function process_icon($icon, $docroot, $root) {
|
||||
return $icon;
|
||||
}
|
||||
|
||||
$tab = 1;
|
||||
// even if DisplaySettings is not enabled for tabs, pages with Tabs="true" will use tabs
|
||||
$display['tabs'] = isset($myPage['Tabs']) ? (strtolower($myPage['Tabs']) == 'true' ? 0 : 1) : 1;
|
||||
$tabbed = $display['tabs'] == 0 && count($pages) > 1;
|
||||
/**
|
||||
* Generates a Panel DOM element for menu items
|
||||
*
|
||||
* @param array $pg Page data array containing name, Title, Icon
|
||||
* @param string $path Current path
|
||||
* @param string $defaultIcon Default icon to use if none specified
|
||||
* @param string $docroot Document root path
|
||||
* @param bool $useTabCookie Whether to add tab cookie onclick handler
|
||||
* @return string HTML for the Panel element
|
||||
*/
|
||||
function generatePanel($pg, $path, $defaultIcon, $docroot, $useTabCookie = false) {
|
||||
$panelTitle = htmlspecialchars($pg['Title']);
|
||||
$icon = _var($pg, 'Icon', $defaultIcon);
|
||||
$icon = process_icon($icon, $docroot, $pg['root']);
|
||||
|
||||
$onclick = $useTabCookie ? ' onclick="$.cookie(\'one\',\'tab1\')"' : '';
|
||||
|
||||
return sprintf(
|
||||
'<div class="Panel">
|
||||
<a href="/%s/%s"%s>
|
||||
<span>%s</span>
|
||||
<div class="PanelText">%s</div>
|
||||
</a>
|
||||
</div>',
|
||||
$path,
|
||||
$pg['name'],
|
||||
$onclick,
|
||||
$icon,
|
||||
_($panelTitle)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates all panels for a menu page
|
||||
*
|
||||
* @param array $page Page data array containing Type and name
|
||||
* @param string $path Current path
|
||||
* @param string $defaultIcon Default icon to use if none specified
|
||||
* @param string $docroot Document root path
|
||||
* @param bool $useTabCookie Whether to add tab cookie onclick handler
|
||||
* @return string HTML for all panels or empty string if not a menu page
|
||||
*/
|
||||
function generatePanels($page, $path, $defaultIcon, $docroot, $useTabCookie = false) {
|
||||
if (!isset($page['Type']) || $page['Type'] != 'menu') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$output = '';
|
||||
$pgs = find_pages($page['name']);
|
||||
foreach ($pgs as $pg) {
|
||||
$output .= generatePanel($pg, $path, $defaultIcon, $docroot, $useTabCookie);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the content for a page
|
||||
*
|
||||
* @param array $page Page data array containing text and Markdown flag
|
||||
* @return string Parsed text ready for eval
|
||||
*
|
||||
* Usage example:
|
||||
* <? eval('?>'.generateContent($page)); ?>
|
||||
*/
|
||||
function generateContent($page) {
|
||||
if (empty($page['Markdown']) || $page['Markdown'] == 'true') {
|
||||
return Markdown(parse_text($page['text']));
|
||||
}
|
||||
return parse_text($page['text']);
|
||||
}
|
||||
?>
|
||||
<div id="displaybox">
|
||||
<div class="tabs">
|
||||
<? foreach ($pages as $page):
|
||||
$close = false;
|
||||
if (isset($page['Title'])):
|
||||
$title = htmlspecialchars($page['Title']) ?? '';
|
||||
if ($tabbed): ?>
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab<?= $tab ?>" name="tabs" onclick="settab(this.id)">
|
||||
<label for="tab<?= $tab ?>">
|
||||
<?= tab_title($title, $page['root'], _var($page, 'Tag', false)) ?>
|
||||
</label>
|
||||
<div class="content">
|
||||
<? $close = true;
|
||||
else:
|
||||
if ($tab == 1): ?>
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab<?= $tab ?>" name="tabs">
|
||||
<div class="content shift">
|
||||
<? endif; ?>
|
||||
<div class="title">
|
||||
<span class="left">
|
||||
<?= tab_title($title, $page['root'], _var($page, 'Tag', false)) ?>
|
||||
</span>
|
||||
</div>
|
||||
<? endif;
|
||||
$tab++;
|
||||
endif;
|
||||
|
||||
// Handle menu type pages
|
||||
if (isset($page['Type']) && $page['Type'] == 'menu'):
|
||||
$pgs = find_pages($page['name']);
|
||||
foreach ($pgs as $pg):
|
||||
// Set title variable with proper escaping (suppress errors)
|
||||
@$title = htmlspecialchars($pg['Title']);
|
||||
$icon = _var($pg, 'Icon', $defaultIcon);
|
||||
$icon = process_icon($icon, $docroot, $pg['root']); ?>
|
||||
<div class="Panel">
|
||||
<a href="/<?= $path ?>/<?= $pg['name'] ?>" onclick="$.cookie('one','tab1')">
|
||||
<span><?= $icon ?></span>
|
||||
<div class="PanelText"><?= _($title) ?></div>
|
||||
</a>
|
||||
</div>
|
||||
<? endforeach;
|
||||
endif;
|
||||
<?php require_once __DIR__ . "/$contentInclude"; ?>
|
||||
|
||||
// Annotate with HTML comment
|
||||
annotate($page['file']);
|
||||
|
||||
// Create page content
|
||||
if (empty($page['Markdown']) || $page['Markdown'] == 'true'):
|
||||
eval('?>'.Markdown(parse_text($page['text'])));
|
||||
else:
|
||||
eval('?>'.parse_text($page['text']));
|
||||
endif;
|
||||
|
||||
if ($close): ?>
|
||||
</div><!-- /.content -->
|
||||
</div><!-- /.tab -->
|
||||
<? endif;
|
||||
endforeach; ?>
|
||||
</div><!-- /.tabs -->
|
||||
</div><!-- /#displaybox -->
|
||||
<?
|
||||
// Clean up variables
|
||||
/**
|
||||
* Legacy carryover. Ideally wouldn't be needed.
|
||||
*/
|
||||
unset($pages, $page, $pgs, $pg, $icon, $nchan, $running, $start, $stop, $row, $script, $opt, $nchan_run);
|
||||
?>
|
||||
?>
|
||||
@@ -0,0 +1,130 @@
|
||||
<?
|
||||
/**
|
||||
* Tabbed content template for the Unraid web interface.
|
||||
* Accessible, modern, and decoupled from non-tabbed logic.
|
||||
*/
|
||||
?>
|
||||
<div id="displaybox">
|
||||
<nav class="tabs" role="tablist" aria-label="Page Tabs">
|
||||
<div class="tabs-container">
|
||||
<? $i = 0; ?>
|
||||
<? foreach ($pages as $page): ?>
|
||||
<? if (!isset($page['Title'])) continue; ?>
|
||||
<? $title = htmlspecialchars((string)$page['Title']); ?>
|
||||
<? $tabId = "tab" . ($i+1); ?>
|
||||
<button
|
||||
role="tab"
|
||||
id="<?= $tabId ?>"
|
||||
aria-controls="<?= $tabId ?>-panel"
|
||||
tabindex="<?= $i === 0 ? '0' : '-1' ?>"
|
||||
aria-selected="<?= $i === 0 ? 'true' : 'false' ?>"
|
||||
>
|
||||
<?= tab_title($title, $page['root'], _var($page, 'Tag', false)) ?>
|
||||
</button>
|
||||
<? $i++; ?>
|
||||
<? endforeach; ?>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<? $i = 0; ?>
|
||||
<? foreach ($pages as $page): ?>
|
||||
<?
|
||||
if (!isset($page['Title'])) {
|
||||
continue;
|
||||
}
|
||||
$title = htmlspecialchars((string)$page['Title']);
|
||||
$tabId = "tab" . ($i+1);
|
||||
annotate($page['file']);
|
||||
?>
|
||||
<section
|
||||
id="<?= $tabId ?>-panel"
|
||||
role="tabpanel"
|
||||
aria-labelledby="<?= $tabId ?>"
|
||||
class="content"
|
||||
tabindex="0"
|
||||
style="display:none;"
|
||||
>
|
||||
<?= generatePanels($page, $path, $defaultIcon, $docroot) ?>
|
||||
|
||||
<? eval('?>'.generateContent($page)); ?>
|
||||
</section>
|
||||
<? $i++; ?>
|
||||
<? endforeach; ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const tabs = document.querySelectorAll('.tabs [role="tab"]');
|
||||
const panels = document.querySelectorAll('[role="tabpanel"]');
|
||||
|
||||
// Hide all panels by default (avoid flash)
|
||||
panels.forEach(panel => panel.style.display = 'none');
|
||||
|
||||
// Figure out which cookie to use (matches settab logic)
|
||||
let cookieName = 'tab';
|
||||
<?
|
||||
// Emulate settab's switch logic for cookie name
|
||||
switch ($myPage['name']) {
|
||||
case 'Main':
|
||||
echo "cookieName = 'tab';\n";
|
||||
break;
|
||||
case 'Cache':
|
||||
case 'Data':
|
||||
case 'Device':
|
||||
case 'Flash':
|
||||
case 'Parity':
|
||||
echo "cookieName = 'one';\n";
|
||||
break;
|
||||
default:
|
||||
echo "cookieName = 'one';\n";
|
||||
break;
|
||||
}
|
||||
?>
|
||||
|
||||
// On load: select correct tab from cookie, or default to first
|
||||
let activeIdx = 0;
|
||||
const cookieVal = $.cookie(cookieName);
|
||||
if (cookieVal) {
|
||||
const idx = Array.from(tabs).findIndex(tab => tab.id === cookieVal);
|
||||
if (idx !== -1) activeIdx = idx;
|
||||
} else {
|
||||
// If no cookie exists, clear both cookies to match the origial initab function behavior
|
||||
$.removeCookie('one');
|
||||
$.removeCookie('tab');
|
||||
}
|
||||
|
||||
tabs.forEach((tab, i) => {
|
||||
if (i === activeIdx) {
|
||||
tab.setAttribute('aria-selected', 'true');
|
||||
tab.setAttribute('tabindex', '0');
|
||||
panels[i].style.display = 'block';
|
||||
} else {
|
||||
tab.setAttribute('aria-selected', 'false');
|
||||
tab.setAttribute('tabindex', '-1');
|
||||
panels[i].style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// On tab click: update cookie and show correct panel
|
||||
// Also update ARIA
|
||||
// No content flash
|
||||
|
||||
tabs.forEach((tab, i) => {
|
||||
tab.addEventListener('click', () => {
|
||||
tabs.forEach((t, j) => {
|
||||
t.setAttribute('aria-selected', j === i ? 'true' : 'false');
|
||||
t.setAttribute('tabindex', j === i ? '0' : '-1');
|
||||
panels[j].style.display = j === i ? 'block' : 'none';
|
||||
});
|
||||
$.cookie(cookieName, tab.id);
|
||||
tab.focus();
|
||||
});
|
||||
tab.addEventListener('keydown', e => {
|
||||
let idx = Array.prototype.indexOf.call(tabs, document.activeElement);
|
||||
if (e.key === 'ArrowRight') {
|
||||
tabs[(idx+1)%tabs.length].focus();
|
||||
} else if (e.key === 'ArrowLeft') {
|
||||
tabs[(idx-1+tabs.length)%tabs.length].focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* Non-tabbed content template for the Unraid web interface.
|
||||
* Renders all pages in sequence without tabs, using original per-page logic.
|
||||
*/
|
||||
?>
|
||||
<div id="displaybox">
|
||||
<div class="content">
|
||||
<? foreach ($pages as $page): ?>
|
||||
<? annotate($page['file']); ?>
|
||||
<? includePageStylesheets($page); ?>
|
||||
|
||||
<? if (isset($page['Title'])): ?>
|
||||
<div class="title">
|
||||
<?= tab_title($page['Title'], $page['root'], _var($page, 'Tag', false)) ?>
|
||||
</div>
|
||||
<? endif; ?>
|
||||
|
||||
<?= generatePanels($page, $path, $defaultIcon, $docroot, true) ?>
|
||||
|
||||
<? eval('?>'.generateContent($page)); ?>
|
||||
<? endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -12,8 +12,8 @@
|
||||
}
|
||||
|
||||
div.frame {
|
||||
padding-top: 14px;
|
||||
padding-bottom: 160px;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 5rem;
|
||||
}
|
||||
div.tile,
|
||||
i.tile {
|
||||
|
||||
@@ -358,9 +358,17 @@ textarea {
|
||||
border-bottom: 1px solid var(--input-border-color);
|
||||
}
|
||||
#header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
position: relative;
|
||||
z-index: 102 !important;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-pack: justify;
|
||||
-ms-flex-pack: justify;
|
||||
justify-content: space-between;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 91px;
|
||||
z-index: 102;
|
||||
@@ -371,6 +379,27 @@ textarea {
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#header unraid-i18n-host {
|
||||
font-size: 16px;
|
||||
margin-left: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tools page, rotate the Downgrade icon to prevent needing to add a new icon to the icon font.
|
||||
* The pseudo element is targeted here otherwise the rotation of the span would mess up spacing with the text.
|
||||
*/
|
||||
a[href="/Tools/Downgrade"] .icon-update:before {
|
||||
display: inline-block; /* required otherwise the rotation won't work */
|
||||
rotate: 180deg;
|
||||
}
|
||||
/* overriding #header .logo svg */
|
||||
#header .logo .partner-logo svg {
|
||||
fill: var(--header-text-primary);
|
||||
width: auto;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
#header.image { /* .image is conditionally added by DefaultPageLayout.php */
|
||||
background-image: var(--customer-header-background-image);
|
||||
}
|
||||
@@ -419,10 +448,11 @@ div.title {
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
background-color: var(--title-header-background-color);
|
||||
letter-spacing: 1.8px;
|
||||
}
|
||||
div.title span.left {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
/* div.title span.left {
|
||||
font-size: 1.4rem;
|
||||
} */
|
||||
div.title span.right {
|
||||
font-size: 1.4rem;
|
||||
padding-top: 2px;
|
||||
@@ -436,10 +466,9 @@ div.title.shift {
|
||||
margin-top: -30px;
|
||||
}
|
||||
#menu {
|
||||
position: absolute;
|
||||
top: 90px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: auto max-content;
|
||||
z-index: 101;
|
||||
@@ -536,30 +565,38 @@ div.title.shift {
|
||||
clear: both;
|
||||
}
|
||||
#footer {
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
color: var(--footer-text);
|
||||
background-color: var(--footer-background-color);
|
||||
padding: 5px 0;
|
||||
padding: .5rem 1rem;
|
||||
width: 100%;
|
||||
height: 1.6rem;
|
||||
line-height: 1.6rem;
|
||||
text-align: center;
|
||||
z-index: 10000;
|
||||
}
|
||||
#statusraid {
|
||||
float: left;
|
||||
padding-left: 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
#countdown {
|
||||
margin: 0 auto;
|
||||
}
|
||||
.footer-right {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
.footer-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
#copyright {
|
||||
font-family: bitstream;
|
||||
font-size: 1.1rem;
|
||||
float: right;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.green {
|
||||
color: var(--green-800);
|
||||
@@ -728,7 +765,7 @@ table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border-style: hidden;
|
||||
margin: -30px 0 0 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
@@ -998,11 +1035,7 @@ span.cpu-speed {
|
||||
color: var(--blue-900);
|
||||
}
|
||||
span.status {
|
||||
float: right;
|
||||
font-size: 1.4rem;
|
||||
margin-top: 30px;
|
||||
padding-right: 8px;
|
||||
letter-spacing: 1.8px;
|
||||
}
|
||||
span.status.vhshift {
|
||||
margin-top: 0;
|
||||
@@ -1119,67 +1152,70 @@ a.list {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
div.content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
.content {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-bottom: 30px;
|
||||
z-index: -1;
|
||||
clear: both;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
div.content.shift {
|
||||
margin-top: 1px;
|
||||
.tabs,
|
||||
.tabs-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
label + .content {
|
||||
margin-top: 86px;
|
||||
|
||||
.tabs {
|
||||
justify-content: space-between;
|
||||
}
|
||||
div.tabs {
|
||||
position: relative;
|
||||
margin: 120px 0 0 0;
|
||||
}
|
||||
div.tab {
|
||||
float: left;
|
||||
margin-top: 30px;
|
||||
}
|
||||
div.tab input[id^="tab"] {
|
||||
display: none;
|
||||
}
|
||||
div.tab [type="radio"] + label:hover {
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--brand-orange);
|
||||
border-bottom: none;
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
}
|
||||
div.tab [type="radio"]:checked + label {
|
||||
cursor: default;
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--brand-orange);
|
||||
border-bottom: none;
|
||||
opacity: 1;
|
||||
}
|
||||
div.tab [type="radio"] + label ~ .content {
|
||||
display: none;
|
||||
}
|
||||
div.tab [type="radio"]:checked + label ~ .content {
|
||||
display: inline;
|
||||
}
|
||||
div.tab [type="radio"] + label {
|
||||
position: relative;
|
||||
font-size: 1.4rem;
|
||||
letter-spacing: 1.8px;
|
||||
padding: 4px 10px;
|
||||
margin-right: 2px;
|
||||
|
||||
.tabs button[role="tab"] {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
background-color: var(--radio-background-color);
|
||||
border: 1px solid var(--disabled-input-border-color);
|
||||
border-radius: 0;
|
||||
border-top-left-radius: 6px;
|
||||
border-top-right-radius: 6px;
|
||||
border: 1px solid var(--disabled-input-border-color);
|
||||
border-bottom: none;
|
||||
background-color: var(--radio-background-color);
|
||||
opacity: 0.5;
|
||||
border-bottom: 1px solid transparent;
|
||||
color: var(--gray-300);
|
||||
font-weight: normal;
|
||||
font-family: inherit;
|
||||
font-size: 1.4rem;
|
||||
letter-spacing: 1.8px;
|
||||
margin-right: 4px;
|
||||
margin-bottom: 0;
|
||||
padding: .75rem 1rem;
|
||||
min-width: 0;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
vertical-align: middle;
|
||||
line-height: 1.0;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s, color 0.2s, background 0.2s, opacity 0.2s;
|
||||
text-transform: none;
|
||||
background-image: none;
|
||||
opacity: .75;
|
||||
}
|
||||
div.tab [type="radio"] + label img {
|
||||
padding-right: 4px;
|
||||
.tabs button[role="tab"] > .tab-icon {
|
||||
margin-right: 8px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
.tabs button[role="tab"]:focus,
|
||||
.tabs button[role="tab"]:hover,
|
||||
.tabs button[role="tab"][aria-selected="true"] {
|
||||
background: transparent;
|
||||
color: var(--text-color);
|
||||
border-color: var(--brand-orange);
|
||||
border-bottom: 1px solid transparent;
|
||||
opacity: 1;
|
||||
}
|
||||
.tabs button[role="tab"]:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
div.Panel {
|
||||
text-align: center;
|
||||
@@ -1509,8 +1545,6 @@ pre#swalbody p {
|
||||
margin-block-end: 1em;
|
||||
}
|
||||
span#wlan0 {
|
||||
float: right;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -1708,8 +1742,7 @@ span#wlan0 {
|
||||
}
|
||||
|
||||
#header {
|
||||
position: fixed;
|
||||
height: 90px;
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
border-bottom: 1px solid var(--gray-600);
|
||||
box-sizing: border-box;
|
||||
@@ -1904,6 +1937,7 @@ span#wlan0 {
|
||||
|
||||
div.title {
|
||||
color: var(--text-color);
|
||||
font-size: 1.6rem;
|
||||
margin: 20px 0 10px 0;
|
||||
padding: 10px 0;
|
||||
clear: both;
|
||||
@@ -1911,10 +1945,10 @@ span#wlan0 {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
letter-spacing: 1.8px;
|
||||
}
|
||||
div.title span.left {
|
||||
/* div.title span.left {
|
||||
font-size: 1.6rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
} */
|
||||
div.title span.right {
|
||||
font-size: 1.6rem;
|
||||
padding-right: 10px;
|
||||
@@ -2075,7 +2109,6 @@ span#wlan0 {
|
||||
}
|
||||
|
||||
div.tabs {
|
||||
margin: 110px 20px 30px 90px;
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
|
||||
@@ -2225,6 +2258,10 @@ span#wlan0 {
|
||||
}
|
||||
}
|
||||
|
||||
#displaybox {
|
||||
padding: 0 1rem 4rem;
|
||||
}
|
||||
|
||||
/* Theme width styles */
|
||||
.Theme--width-boxed #displaybox {
|
||||
min-width: 1280px;
|
||||
|
||||
@@ -7,7 +7,7 @@ if [ $DISABLE == "yes" ]
|
||||
exit 1 ;
|
||||
fi
|
||||
PCI="no"
|
||||
PCI=$(/usr/local/emhttp/plugins/dynamix.vm.manager/scripts/pcicheck.php "$@");
|
||||
#PCI=$(/usr/local/emhttp/plugins/dynamix.vm.manager/scripts/pcicheck.php "$@");
|
||||
if [ $PCI == "yes" ]
|
||||
then
|
||||
printf '\n%s\n' "Start/autostart is disabled PCI Change detected." >&2 ## Send message to stderr.
|
||||
|
||||
Reference in New Issue
Block a user