Merge remote-tracking branch 'upstream/master' into File-System-Snapshots

This commit is contained in:
SimonFair
2023-12-30 15:55:46 +00:00
36 changed files with 553 additions and 270 deletions
+7
View File
@@ -1527,6 +1527,13 @@ Change this setting to YES when troubleshooting is required and it is not possib
A mirror of the syslog file is stored in the **logs** folder of the flash device.
:end
:syslog_shutdown_flash_help:
This setting is YES by default and enables the system to copy the syslog file to the USB device on shutdown or reboot.
After rebooting, the syslog from this run will be visible on Tools > Syslog > syslog-previous;
it will also be included in diagnostics as logs/syslog-previous.txt
:end
:confirm_reboot_help:
Choose if rebooting or powering down the server needs a confirmation checkbox.
:end
@@ -428,7 +428,7 @@ class DockerUpdate{
// DEPRECATED: Only used for Docker Index V1 type update checks
public function getRemoteVersion($image) {
[$strRepo, $strTag] = array_pad(explode(':', DockerUtil::ensureImageTag($image)),2,'');
extract(DockerUtil::parseImageTag($image));
$apiUrl = sprintf('http://index.docker.io/v1/repositories/%s/tags/%s', $strRepo, $strTag);
//$this->debug("API URL: $apiUrl");
$apiContent = $this->download_url($apiUrl);
@@ -436,7 +436,7 @@ class DockerUpdate{
}
public function getRemoteVersionV2($image) {
[$strRepo, $strTag] = array_pad(explode(':', DockerUtil::ensureImageTag($image)),2,'');
extract(DockerUtil::parseImageTag($image));
/*
* Step 1: Check whether or not the image is in a private registry, get corresponding auth data and generate manifest url
*/
@@ -980,7 +980,7 @@ class DockerClient {
$c['Size'] = $this->formatBytes($ct['Size']);
$c['VirtualSize'] = $this->formatBytes($ct['VirtualSize']);
$c['Tags'] = array_map('htmlspecialchars', $ct['RepoTags'] ?? []);
$c['Repository'] = vsprintf('%1$s/%2$s', preg_split("#[:\/]#", DockerUtil::ensureImageTag($ct['RepoTags'][0]??'')));
$c['Repository'] = DockerUtil::parseImageTag($ct['RepoTags'][0]??'')['strRepo'];
$c['usedBy'] = $this->usedBy($c['Id']);
$this::$imagesCache[$c['Id']] = $c;
}
@@ -993,18 +993,50 @@ class DockerClient {
##################################
class DockerUtil {
public static function ensureImageTag($image) {
[$strRepo, $strTag] = array_map('trim', array_pad(explode(':', $image.':'),2,''));
if (strpos($strRepo, 'sha256:') === 0) {
public static function ensureImageTag($image): string
{
extract(static::parseImageTag($image));
return "$strRepo:$strTag";
}
public static function parseImageTag($image): array
{
if (strpos($image, 'sha256:') === 0) {
// sha256 was provided instead of actual repo name so truncate it for display:
$strRepo = substr($strRepo, 7, 12);
} elseif (strpos($strRepo, '/') === false) {
// Prefix library/ if there's no author (maybe a Docker offical image?)
$strRepo = "library/$strRepo";
$strRepo = substr($image, 7, 12);
} elseif (strpos($image, '/') === false) {
return static::parseImageTag('library/' . $image);
} else {
$parsedImage = static::splitImage($image);
if (!empty($parsedImage)) {
$strRepo = $parsedImage['strRepo'];
$strTag = $parsedImage['strTag'];
} else {
// Unprocessable input
$strRepo = $image;
}
}
// Add :latest tag to image if it's absent
if (empty($strTag)) $strTag = 'latest';
return "$strRepo:$strTag";
return array_map('trim', ['strRepo' => $strRepo, 'strTag' => $strTag]);
}
private static function splitImage($image): ?array
{
if (false === preg_match('@^(.+/)*([^/:]+)(:[^:/]*)*$@', $image, $newSections) || count($newSections) < 3) {
return null;
} else {
[, $strRepo, $image, $strTag] = array_merge($newSections, ['']);
$strTag = str_replace(':','',$strTag??'');
return [
'strRepo' => $strRepo . $image,
'strTag' => $strTag,
];
}
}
public static function loadJSON($path) {
@@ -110,7 +110,7 @@ foreach ($containers as $ct) {
if ($ct['BaseImage']) echo "<i class='fa fa-cubes' style='margin-right:5px'></i>".htmlspecialchars($ct['BaseImage'])."<br>";
echo _('By').": ";
$registry = $info['registry'];
[$author,$version] = my_explode(':',$ct['Image']);
['strRepo' => $author, 'strTag' => $version] = DockerUtil::parseImageTag($ct['Image']);
if ($registry) {
echo "<a href='".htmlspecialchars($registry)."' target='_blank'>".htmlspecialchars(compress($author,24))."</a>";
} else {
@@ -32,7 +32,7 @@ input:hover[type=button],input:hover[type=reset],input:hover[type=submit],button
input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled],button[disabled],button[type=button][disabled],a.button[disabled]
input:hover[type=button][disabled],input:hover[type=reset][disabled],input:hover[type=submit][disabled],button:hover[disabled],button:hover[type=button][disabled],a.button:hover[disabled]
input:active[type=button][disabled],input:active[type=reset][disabled],input:active[type=submit][disabled],button:active[disabled],button:active[type=button][disabled],a.button:active[disabled]
{cursor:default;color:#808080;background:-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#404040),to(#404040)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#808080),to(#808080)) 100% 100% no-repeat;background:linear-gradient(90deg,#404040 0,#808080) 0 0 no-repeat,linear-gradient(90deg,#404040 0,#808080) 0 100% no-repeat,linear-gradient(0deg,#404040 0,#404040) 0 100% no-repeat,linear-gradient(0deg,#808080 0,#808080) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%}
{opacity:0.5;cursor:default;color:#808080;background:-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#404040),to(#404040)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#808080),to(#808080)) 100% 100% no-repeat;background:linear-gradient(90deg,#404040 0,#808080) 0 0 no-repeat,linear-gradient(90deg,#404040 0,#808080) 0 100% no-repeat,linear-gradient(0deg,#404040 0,#404040) 0 100% no-repeat,linear-gradient(0deg,#808080 0,#808080) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%}
p.centered{text-align:center}
span.error{color:#D8000C;background-color:#FFBABA;display:block;width:100%}
span.warn{color:#9F6000;background-color:#FEEFB3;display:block;width:100%}
+5 -2
View File
@@ -83,6 +83,8 @@ function print_error($error) {
?>
<script>
var ctrl = '<span class="status <?=$tabbed?"":"vhshift"?>"><a style="cursor:pointer" class="tooltip_diskio" title="_(Toggle reads/writes display)_" onclick="toggle_diskio();return false"><i class="toggle fa"></i></a></span>';
var recall = null;
var recover = null;
function base64(str) {
return window.btoa(unescape(encodeURIComponent(str)));
@@ -310,6 +312,7 @@ devices.on('message', function(msg,meta) {
// array + pool + ua + flash devices
var get = JSON.parse(msg);
for (var name in get) $('#'+name).html(get[name]);
if (recall !== null) recall.html('&nbsp;');
display_diskio();
// stop updating when array is stopped
if (get.stop==1) {
@@ -322,8 +325,8 @@ devices.on('message', function(msg,meta) {
} else {
// make truncated descriptions fully visible when hovering over them
$('td.desc').hover(
function(){if ($(this)[0].offsetWidth < $(this)[0].scrollWidth) {devices.stop();$(this).next().html('');}},
function(){if ($(this)[0].offsetWidth < $(this)[0].scrollWidth) {devices.start();}}
function(){if ($(this)[0].offsetWidth < $(this)[0].scrollWidth) {recall=$(this).next(); recover=recall.html(); recall.html('&nbsp;');}},
function(){if ($(this)[0].offsetWidth < $(this)[0].scrollWidth) {recall.html(recover); recall=null;}}
);
}
break;
+66 -47
View File
@@ -209,7 +209,7 @@ switch ($theme) {
</td></tr>
<tr><td>
<div class='leftside'>
<a href="/Settings/DateTime" class="hand none" title="_(Go to date and time settings)_"><span id="current_time"></span></a><br><span id="current_date"></span><br><br>
<a href="/Settings/DateTime" class="hand none"><span id="current_time"></span></a><br><span id="current_date"></span><br><br>
<span class='header'><i class='indent fa fa-file-text-o'></i>_(Model)_</span><br><i class='indent'></i><?=_var($var,'SYS_MODEL')?:'---'?><br><br>
<span class='header'><i class='indent fa fa-id-badge'></i>_(Registration)_</span><br><i class='indent'></i>Unraid OS <b><em><?=_var($var,'regTy')?></em></b><br><br>
<span class='header'><i class='indent fa fa-clock-o'></i>_(Uptime)_</span><br><i class='indent'></i><span class='uptime'></span>
@@ -273,18 +273,18 @@ foreach ($cpus as $pair) {
<span class="head_info"><span><i class='ups fa fa-line-chart'></i>_(Memory)_: <?="$memory_installed $unit $memory_type $ecc"?></span></span>
<span class="switch">_(RAM)_:<span class="head_bar"><span class='sys0_ load'>0%</span><div class='usage-disk sys'><span id='sys0_'></span><span></span></div></span></span><br></div>
<a href='/Dashboard/Tools/Processes' title="_(View Running Processes)_"><i class='fa fa-fw fa-info-circle control'></i></a>
</td></tr>
<tr><td>
<span class='w26'><div class='more'>
<i class='ups fa fa-compress'></i>_(Usable size)_: <?=$ramsize?><br>
<i class='ups fa fa-expand'></i>_(Maximum size)_: <?="$memory_maximum $unit"?><?=$low?'*':''?>
<legend>_(Legend)_</legend>
<span id='dynamic'></span>
</div></span>
<span class='w18 center'><span class='center'><a class='info hand none'>_(RAM usage)_<span>_(Percent of total used memory)_ (<?=$ramsize?>)</span></a></span><div class='pie' id='sys0'><span class='sys0'></span><span class='var0'></span></div></span>
<span class='w18 center'><span class='center'><a class='info hand none'>_(Flash device)_<span>_(Percent usage of flash usb device)_ (<?=$flashsize?>)</span></a></span><div class='pie' id='sys1'><span class='sys1'></span><span class='var1'></span></div></span>
<span class='w18 center'><span class='center'><a class='info hand none'>_(Log filesystem)_<span>_(Percent usage of LOG file system)_ (<?=$logsize?>)</span></a></span><div class='pie' id='sys2'><span class='sys2'></span><span class='var2'></span></div></span>
<span class='w18 center'><span class='center'><a class='info hand none'><?=$vdisk?><span><?=_("Percent usage of $vdisk")?> (<?=$dockersize?>)</span></a></span><div class='pie' id='sys3'><span class='sys3'></span><span class='var3'></span></div></span>
</td></tr><tr><td>
<span class='w26'><i class='ups fa fa-compress'></i>_(Usable size)_: <?=$ramsize?><br><i class='ups fa fa-expand'></i>_(Maximum size)_: <?="$memory_maximum $unit"?><?=$low?'*':''?></span>
<span class='w18 center'><a class='info hand none'>_(RAM usage)_<span>_(Percent of total used memory)_ (<?=$ramsize?>)</span></a></span>
<span class='w18 center'><a class='info hand none'>_(Flash device)_<span>_(Percent usage of flash usb device)_ (<?=$flashsize?>)</span></a></span>
<span class='w18 center'><a class='info hand none'>_(Log filesystem)_<span>_(Percent usage of LOG file system)_ (<?=$logsize?>)</span></a></span>
<span class='w18 center'><a class='info hand none'><?=$vdisk?><span><?=_("Percent usage of $vdisk")?> (<?=$dockersize?>)</span></a></span>
</td></tr><tr><td>
<span class='w26'><legend>_(Legend)_</legend><span id='dynamic'></span></span>
<span class='w18 center'><div class='pie' id='sys0'><span class='sys0'></span><span class='var0'></span></div></span>
<span class='w18 center'><div class='pie' id='sys1'><span class='sys1'></span><span class='var1'></span></div></span>
<span class='w18 center'><div class='pie' id='sys2'><span class='sys2'></span><span class='var2'></span></div></span>
<span class='w18 center'><div class='pie' id='sys3'><span class='sys3'></span><span class='var3'></span></div></span>
</td></tr>
</tbody>
@@ -800,6 +800,8 @@ var update2 = true;
var box = null;
var startup = true;
var stopgap = '<thead class="stopgap"><tr><td class="stopgap"></td></tr></thead>';
var recall = null;
var recover = null;
var options_cpu = {
series:[{name:'load', data:cpu.slice()}],
@@ -1030,10 +1032,10 @@ function portSelect(name) {
}
function moreInfo(data,table) {
var info = [];
if (data[1]>0) info.push(data[1]+" _(failed device)_"+(data[1]==1?'':'s'));
if (data[2]>0) info.push(data[2]+" _(heat warning)_"+(data[2]==1?'':'s'));
if (data[3]>0) info.push(data[3]+" _(SMART error)_"+(data[3]==1?'':'s'));
if (data[4]>0) info.push(data[4]+" _(utilization warning)_"+(data[4]==1?'':'s'));
if (data[1]>0) info.push(data[1]+' '+(data[1]==1 ? "_(failed device)_" : "_(failed devices)_"));
if (data[2]>0) info.push(data[2]+' '+(data[2]==1 ? "_(heat warning)_" : "_(heat warnings)_"));
if (data[3]>0) info.push(data[3]+' '+(data[3]==1 ? "_(SMART error)_" : "_(SMART errors)_"));
if (data[4]>0) info.push(data[4]+' '+(data[4]==1 ? "_(utilization warning)_" : "_(utilization warnings)_"));
return info.length ? "<div class='last'><i class='icon-u-triangle failed'></i><span class='failed'>"+table+" _(has)_ "+info.join('. ')+".</span></div>" : "";
}
function autoscale(value,text,size,kilo) {
@@ -1118,7 +1120,7 @@ function StopArray() {
}
function StopArrayNow() {
$('span.hand').prop('onclick',null).off('click').addClass('busy').css({'cursor':'default'});
$.post('/update.htm',{startState:'<?=_var($var,'mdState')?>',cmdStop:'Stop',csrf_token:'<?=_var($var,'csrf_token')?>'},function(){refresh();});
$.post('/update.htm',{cmdStop:'Stop'},function(){refresh();});
}
function StartArray() {
<?if ($confirm['stop']):?>
@@ -1129,7 +1131,7 @@ function StartArray() {
}
function StartArrayNow() {
$('span.hand').prop('onclick',null).off('click').addClass('busy').css({'cursor':'default'});
$.post('/update.htm',{startState:'<?=_var($var,'mdState')?>',cmdStart:'Start',csrf_token:'<?=_var($var,'csrf_token')?>'},function(){refresh();});
$.post('/update.htm',{cmdStart:'Start'},function(){refresh();});
}
function Reboot() {
<?if ($confirm['down']):?>
@@ -1204,6 +1206,12 @@ function addProperties() {
$('div#sys1').hover(function(){$('.sys1').hide();$('.var1').show();},function(){$('.sys1').show();$('.var1').hide();});
$('div#sys2').hover(function(){$('.sys2').hide();$('.var2').show();},function(){$('.sys2').show();$('.var2').hide();});
$('div#sys3').hover(function(){$('.sys3').hide();$('.var3').show();},function(){$('.sys3').show();$('.var3').hide();});
$('#current_time').hover(function(){$.post('/webGui/include/DashboardApps.php',{ntp:'ntp'},function(ntp){$('#current_time').prop('title',ntp+"\n_(Go to date and time settings)_");});});
// make truncated descriptions fully visible when hovering over them
$('span.w18').hover(
function(){if ($(this)[0].offsetWidth < $(this)[0].scrollWidth) {recall = $(this).next(); recover=recall.html(); recall.html('&nbsp;');}},
function(){if ($(this)[0].offsetWidth < $(this)[0].scrollWidth) {recall.html(recover); recall=null;}}
);
}
function showContent() {
var count = {'db-box1':$('table#db-box1 tbody').length, 'db-box2':$('table#db-box2 tbody').length, 'db-box3':$('table#db-box3 tbody').length}
@@ -1381,7 +1389,7 @@ function selectsnapshot(uuid, name ,snaps, opt, getlist){
box.find('#targetsnapl').html(snaps);
if (getlist) {
var only = (opt == "remove") ? 0 : 1;
$.post("/plugins/dynamix.vm.manager/include/VMajax.php", {action:"snap-images", uuid:uuid, snapshotname:snaps, only:only}, function(data){if (data.html) box.find('#targetsnapimages').html(data.html);},'json');
$.post("/plugins/dynamix.vm.manager/include/VMajax.php",{action:"snap-images",uuid:uuid,snapshotname:snaps,only:only},function(data){if (data.html) box.find('#targetsnapimages').html(data.html);},'json');
}
document.getElementById("targetsnapfspc").checked = true;
box.dialog({
@@ -1473,14 +1481,29 @@ function LockButton() {
$('table.dashboard').sortable('destroy');
}
}
function cpu_parse(msg) {
var parse = {}, section = '';
msg.split('\n').forEach(function(row) {
if (row.substr(0,1) == '[') {
section = row.substr(1,row.length-2);
parse[section] = {};
} else {
var data = row.split('=');
parse[section][data[0]] = data[1];
}
});
return parse;
}
var dashboard = new NchanSubscriber('/sub/cpuload,update1,update2,update3<?=$wireguard?",wireguard":""?>',{subscriber:'websocket'});
dashboard.on('message',function(msg,meta) {
switch (meta.id.channel()) {
case 0:
var get = parseINI(msg);
var get = cpu_parse(msg);
// cpu load
$.each(get,function(k,v) {
var load = v['host'];
var load = v.host;
var color = setColor(load, 90, 70);
if (k=='cpu') {
addChartCpu(load);
@@ -1536,30 +1559,27 @@ dashboard.on('message',function(msg,meta) {
case 2:
if (!update2) break;
var get = JSON.parse(msg);
var data = get.disk.split('\n');
var info = moreInfo(data,"_(Array)_");
var info = moreInfo(get.disk,"_(Array)_");
// array devices
$('#array_list tr.updated').remove();
$('#array_list').append(data[0]).hideMe();
$('#array_list').append(get.disk[0]).hideMe();
$('#array_info').parent().css({'display':info?'':'none'});
$('#array_info').html(info);
smartMenu('#array_list');
// pool devices
for (var i=0,pool; pool=get.pool[i]; i++) {
var data = pool.split('\n');
var info = moreInfo(data,"_(Pool)_");
for (let i=0; i < get.pool.length; i++) {
var info = moreInfo(get.pool[i],"_(Pool)_");
$('#pool_list'+i+' tr.updated').remove();
$('#pool_list'+i).append(pool).hideMe();
$('#pool_list'+i).append(get.pool[i][0]).hideMe();
$('#pool_info'+i).parent().css({'display':info?'':'none'});
$('#pool_info'+i).html(info);
smartMenu('#pool_list'+i);
}
<?if ($devs):?>
// unassigned devices
var data = get.open.split('\n');
var info = moreInfo(data,"_(Unassigned)_");
var info = moreInfo(get.open,"_(Unassigned)_");
$('#devs_list tr.updated').remove();
$('#devs_list').append(data[0]).hideMe();
$('#devs_list').append(get.open[0]).hideMe();
$('#devs_info').parent().css({'display':info?'':'none'});
$('#devs_info').html(info);
smartMenu('#devs_list');
@@ -1567,14 +1587,13 @@ dashboard.on('message',function(msg,meta) {
// parity status
$('span.parity').html(get.parity);
// parity schedule
var data = get.schedule.split('\n');
$('#parity').html(data[0]);
$('#program').html(data[1]);
$('#parity').html(get.schedule[0]);
$('#program').html(get.schedule[1]);
break;
case 3:
var get = JSON.parse(msg);
// rx & tx speeds
for (var i=0,port; port=get.port[i]; i++) {
for (let i=0,port; port=get.port[i]; i++) {
if (port[0] == port_select) {
$('#inbound').text(port[1]);
$('#outbound').text(port[2]);
@@ -1618,17 +1637,17 @@ dashboard.on('message',function(msg,meta) {
});
<?if ($apcupsd):?>
var apcups = new NchanSubscriber('/sub/apcups',{subscriber:'websocket'});
apcups.on('message',function(data) {
data = data.split(';');
$('#ups_model').html(data[0]);
$('#ups_status').html(data[1]);
$('#ups_status_').html(data[1]);
$('#ups_bcharge').html(data[2]);
$('#ups_timeleft').html(data[3]);
$('#ups_nompower').html(data[4]);
$('#ups_loadpct').html(data[5]);
$('#ups_loadpct_').html(data[5]);
$('#ups_outputv').html(data[6]);
apcups.on('message',function(msg) {
var get = JSON.parse(msg);
$('#ups_model').html(get[0]);
$('#ups_status').html(get[1]);
$('#ups_status_').html(get[1]);
$('#ups_bcharge').html(get[2]);
$('#ups_timeleft').html(get[3]);
$('#ups_nompower').html(get[4]);
$('#ups_loadpct').html(get[5]);
$('#ups_loadpct_').html(get[5]);
$('#ups_outputv').html(get[6]);
});
<?endif;?>
+19
View File
@@ -0,0 +1,19 @@
Menu="Tasks:2"
Type="xmenu"
Code="e970"
---
<script>
function delPage(page) {
$.post('/webGui/include/MyFavorites.php',{action:'del',page:page},function(){refresh();});
}
$(function(){
$('i.PanelIcon').each(function(){
var icon = $(this);
var page = icon.closest('a').prop('href').split('/').pop();
icon.after('<i class="fa fa-heartbeat favo" title="_(Remove from favorites)_" onclick="delPage(&quot;'+page+'&quot;);return false"></i>');
icon.parent().parent().parent().hover(function(){icon.next().show();},function(){icon.next().hide();});
});
if ($('i.PanelIcon').length==0) $('#nofavs').show();
});
</script>
+6
View File
@@ -0,0 +1,6 @@
Menu="Favorites"
Type="menu"
Title="My Favorites"
Tag="icon-u-star"
---
<div id="nofavs" style="display:none;width:100%"><center>_(No favorites available)_</center></div>
+68
View File
@@ -0,0 +1,68 @@
Menu="OtherSettings"
Type="xmenu"
Title="Power Mode"
Icon="icon-energysaving"
Tag="icon-energysaving"
---
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$cpufreq = '/sys/devices/system/cpu/cpu0/cpufreq';
$current = exec("cat $cpufreq/scaling_governor 2>/dev/null");
exec("cat $cpufreq/scaling_available_governors 2>/dev/null | tr ' ' '\n' | sed '/^$/d' | sort -u",$governor);
function value(...$modes) {
global $current, $governor;
$checked = $value = '';
$disabled = ' disabled';
foreach ($modes as $mode) {
if ($mode==$current) $checked = ' checked';
if (in_array($mode,$governor)) {$value = "value=\"$mode\""; $disabled = '';}
}
return $value.$checked.$disabled;
}
?>
<script>
function preparePowermode(form) {
$(form).find('[name="#arg[1]"]').val(form.powermode.value);
}
$(function(){
$('input[type=radio]').each(function(){
if ($(this).prop('disabled')) $(this).next('span').html(" <i>(_(unavailable)_)</i>");
});
<?if (exec("dmesg | grep -Pom1 'Hypervisor detected'")):?>
$('#vm').show();
<?endif;?>
});
</script>
<form markdown="1" name="PowerMode" method="POST" action="/update.php" target="progressFrame" onsubmit="preparePowermode(this)">
<input type="hidden" name="#file" value="dynamix/dynamix.cfg">
<input type="hidden" name="#section" value="powermode">
<input type="hidden" name="#command" value="/webGui/scripts/powermode">
<input type="hidden" name="#arg[1]" value="">
_(Change power mode)_:
: <input name="powermode" type="radio"<?=value('powersave')?>>_(Best power efficiency)_<span></span>
&nbsp;
: <input name="powermode" type="radio"<?=value('ondemand','balance_power')?>>_(Balanced operation)_<span></span>
&nbsp;
: <input name="powermode" type="radio"<?=value('performance')?>>_(Best performance)_<span></span>
&nbsp;
: <input type="submit" name="#apply" value="_(Apply)_" disabled><input type="button" value="_(Done)_" onclick="done()">
</form>
<div id="vm" class="notice">_(When running Unraid virtualized, there are no available power modes)_</div>
+18 -1
View File
@@ -1,4 +1,21 @@
Menu="Tasks:4"
Type="xmenu"
Tabs="false"
Code="e924"
Code="e924"
---
<script>
function addPage(page) {
$.post('/webGui/include/MyFavorites.php',{action:'add',page:page},function(){
swal({title:"_(Added to Favorites)_",text:"",type:"success",html:true,confirmButtonText:"_(Ok)_"});
});
}
$(function(){
$('i.PanelIcon').each(function(){
var icon = $(this);
var page = icon.closest('a').prop('href').split('/').pop();
icon.after('<i class="fa fa-heart favo" title="_(Add to favorites)_" onclick="addPage(&quot;'+page+'&quot;);return false"></i>');
icon.parent().parent().parent().hover(function(){icon.next().show();},function(){icon.next().hide();});
});
});
</script>
+10 -2
View File
@@ -147,7 +147,7 @@ _(Local syslog number of files)_:
_(Remote syslog server)_:
: <span class="span"><input type="text" name="remote_server" class="narrow" value="<?=_var($syslog,'remote_server')?>" maxlength="23" placeholder="_(name or ip address)_"></span>
<select name="remote_protocol" class="narrow" size="1">
<select name="remote_protocol" class="narrow">
<?=mk_option(_var($syslog,'remote_protocol'), "udp", _("UDP"))?>
<?=mk_option(_var($syslog,'remote_protocol'), "tcp", _("TCP"))?>
</select>
@@ -156,13 +156,21 @@ _(Remote syslog server)_:
:syslog_remote_server_help:
_(Mirror syslog to flash)_:
: <select name="syslog_flash" size="1">
: <select name="syslog_flash">
<?=mk_option(_var($syslog,'syslog_flash'), "", _("No"))?>
<?=mk_option(_var($syslog,'syslog_flash'), "1", _("Yes"))?>
</select>
:syslog_mirror_flash_help:
_(Copy syslog to flash on shutdown)_:
: <select name="syslog_shutdown">
<?=mk_option(_var($syslog,'syslog_shutdown'), "", _("Yes"))?>
<?=mk_option(_var($syslog,'syslog_shutdown'), "1", _("No"))?>
</select>
:syslog_shutdown_flash_help:
&nbsp;
: <input type="button" value="_(Apply)_" onclick='validatePort(this.form)' disabled><input type="button" value="_(Done)_" onclick="done()">
</form>
+18 -1
View File
@@ -1,4 +1,21 @@
Menu="Tasks:90"
Type="xmenu"
Tabs="false"
Code="e909"
Code="e909"
---
<script>
function addPage(page) {
$.post('/webGui/include/MyFavorites.php',{action:'add',page:page},function(){
swal({title:"_(Added to Favorites)_",text:"",type:"success",html:true,confirmButtonText:"_(Ok)_"});
});
}
$(function(){
$('i.PanelIcon').each(function(){
var icon = $(this);
var page = icon.closest('a').prop('href').split('/').pop();
icon.after('<i class="fa fa-heart favo" title="_(Add to favorites)_" onclick="addPage(&quot;'+page+'&quot;);return false"></i>');
icon.parent().parent().parent().hover(function(){icon.next().show();},function(){icon.next().hide();});
});
});
</script>
+1
View File
@@ -26,6 +26,7 @@ hot="45"
max="55"
hotssd="60"
maxssd="70"
power=""
theme="white"
locale=""
raw=""
@@ -0,0 +1,2 @@
#!/bin/bash
/usr/local/emhttp/webGui/scripts/update_services 10
@@ -13,13 +13,17 @@
?>
<?
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
// add translations
$_SERVER['REQUEST_URI'] = 'dashboard';
require_once "$docroot/webGui/include/Translations.php";
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
// add translations
$_SERVER['REQUEST_URI'] = 'dashboard';
require_once "$docroot/webGui/include/Translations.php";
if (isset($_POST['ntp'])) {
$ntp = exec("ntpq -pn|awk '{if (NR>3 && $2!=\".INIT.\") c++} END {print c}'");
die($ntp ? sprintf(_('Clock synchronized with %s NTP server'.($ntp==1?'':'s')),$ntp) : _('Clock is unsynchronized with no NTP servers'));
}
if ($_POST['docker']) {
$user_prefs = $dockerManPaths['user-prefs'];
@@ -0,0 +1,47 @@
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2012-2023, Bergware International.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
$permit = ['del','add'];
$action = $_POST['action']??'';
$page = glob("$docroot/plugins/*/{$_POST['page']}.page",GLOB_NOSORT)[0];
$cfg = '/boot/config/favorites.cfg';
// validate input
if (!$page || !in_array($action,$permit)) exit;
$file = fopen($page,'r');
// get current Menu settings
extract(parse_ini_string(fgets($file)));
fclose($file);
// remove label and escape single quotes for sed command
$Menu = str_replace([' MyFavorites',"'"],['',"'\''"],$Menu);
switch ($action) {
case $permit[0]: // del
$del = str_replace('/','\/',$page);
exec("sed -i '/$del/d' $cfg 2>/dev/null");
break;
case $permit[1]: // add
$file = fopen($cfg,'a+');
fseek($file,0,0);
while (($line = fgets($file))!==false) {
if (rtrim($line) == $page) break;
}
if (feof($file)) fwrite($file, $page."\n");
fclose($file);
$Menu .= ' MyFavorites';
break;
}
// update Menu settings
exec("sed -ri '0,/^Menu=\".+\"$/s//Menu=\"$Menu\"/' $page 2>/dev/null");
@@ -38,6 +38,9 @@ function duration(&$hrs) {
$age = date_diff($poh,$now);
$hrs = "$hrs (".($age->y?"{$age->y}y, ":"").($age->m?"{$age->m}m, ":"").($age->d?"{$age->d}d, ":"")."{$age->h}h)";
}
function blocks_size(&$blks,$blk_size) {
$blks = "$blks (".my_scale($blks*$blk_size,$unit)." $unit)";
}
function append(&$ref, &$info) {
if ($info) $ref .= ($ref ? " " : "").$info;
}
@@ -62,6 +65,7 @@ case "attributes":
$hot = _var($disk,'hotTemp',-1)>=0 ? $disk['hotTemp'] : ($hotNVME>=0 ? $hotNVME : (_var($disk,'rotational',1)==0 && $display['hotssd']>=0 ? $display['hotssd'] : $display['hot']));
$max = _var($disk,'maxTemp',-1)>=0 ? $disk['maxTemp'] : ($maxNVME>=0 ? $maxNVME : (_var($disk,'rotational',1)==0 && $display['maxssd']>=0 ? $display['maxssd'] : $display['max']));
$top = $_POST['top'] ?? 120;
$ssd_remaining = NULL;
$empty = true;
exec("smartctl -n standby -A $type ".escapeshellarg("/dev/$port"),$output);
// remove empty rows
@@ -83,6 +87,8 @@ case "attributes":
}
if ($info[8]=='-') $info[8] = 'Never';
if ($info[0]==9 && is_numeric(size($info[9]))) duration($info[9]);
if (str_starts_with($info[1], 'Total_LBAs_')) blocks_size($info[9],512); // Assumes 512 byte sectors
if (str_ends_with($info[1], '_32MiB')) blocks_size($info[9],32*1024*1024);
echo "<tr{$color}>".implode('',array_map('normalize', $info))."</tr>";
$empty = false;
}
@@ -101,11 +107,38 @@ case "attributes":
case 'Power on hours':
if (is_numeric(size($value))) duration($value);
break;
case 'Percentage used':
$ssd_remaining = 100 - str_replace('%', '', $value);
break;
}
if (str_ends_with($name, ', hours') && str_starts_with($value, 'minutes ')) {
$name = substr($name, 0, -7);
$value = substr($value, 8);
if (is_numeric(size($value))) duration($value);
}
echo "<tr{$color}><td>-</td><td>$name</td><td colspan='8'>$value</td></tr>";
$empty = false;
}
}
if (is_null($ssd_remaining)) {
// Try to look up SSD's 'Percentage Used Endurance Indicator' with special command
exec("smartctl -n standby -l ssd $type ".escapeshellarg("/dev/$port"), $ssd_out);
$ssd_out = array_filter($ssd_out);
foreach ($ssd_out as $row) {
if (str_ends_with($row, 'Percentage Used Endurance Indicator')) {
// Probably a SATA SSD
$info = explode(' ', trim(preg_replace('/\s+/',' ',$row)), 6);
$ssd_remaining = 100 - $info[3];
} elseif (str_starts_with($row, 'Percentage used endurance indicator:')) {
// Probably a SAS SSD
[$name,$value] = array_map('trim',explode(':', $row));
$ssd_remaining = 100 - str_replace('%','',$value);
}
}
}
if (!is_null($ssd_remaining)) {
echo "<tr><td>-</td><td>SSD endurance remaining</td><td colspan='8'>$ssd_remaining %</td></tr>";
}
if ($empty) echo "<tr><td colspan='10' style='text-align:center;padding-top:12px'>"._('Attributes not available')."</td></tr>";
break;
case "capabilities":
+3 -3
View File
@@ -38,9 +38,9 @@ function update_translation($locale) {
if (!file_exists($store)) file_put_contents($store,serialize(parse_lang_file($text)));
$language = unserialize(file_get_contents($store));
}
$text = "$docroot/languages/$locale/main.txt";
$text = "$docroot/languages/$locale/dashboard.txt";
if (file_exists($text)) {
$store = "$docroot/languages/$locale/main.dot";
$store = "$docroot/languages/$locale/dashboard.dot";
if (!file_exists($store)) file_put_contents($store,serialize(parse_lang_file($text)));
$language = array_merge($language,unserialize(file_get_contents($store)));
}
@@ -92,7 +92,7 @@ while (true) {
// add fans information
if (count($fans)) $echo['fan'] = array_map(function($fan){return "$fan RPM";},$fans);
// add streams information
exec("LANG='en_US.UTF8' lsof -Fn /mnt/disk[0-9]* 2>/dev/null|awk -F/ '(NR>1){print \$4}'",$lsof);
exec('LANG="en_US.UTF8" lsof -Fn /mnt/* 2>/dev/null|awk -F/ \'$1=="n" && $2=="mnt" && $5!="" {print $4"/"$5"/"$6"/"$7}\'|sort -u|awk -F/ \'{print $1}\'',$lsof);
$share = array_keys(parse_ini_file("$varroot/shares.ini",true));
$count = array_count_values($lsof);
foreach ($share as $name) $echo['stream'][] = $count[$name]??0;
+17 -36
View File
@@ -223,16 +223,17 @@ function device_usage(&$disk, &$full, &$high) {
if ($critical>0 && $load>=$critical) {$class = 'redbar'; $full++;}
elseif ($warning>0 && $load>=$warning) {$class = 'orangebar'; $high++;}
else $class = 'greenbar';
}
else
} else {
$class = false;
}
return $text%10==0 ? $used : "<span class='load'>$used</span><div class='usage-disk sys'><span style='width:$used'".($class?" class='$class'":"")."></span><span></span></div>";
}
else
} else {
return $text%10==0 ? "-" : "<span class='load'>-</span><div class='usage-disk sys none'><span></span></div>";
}
}
function array_group($type, $pool=false) {
global $disks,$error,$warning,$red,$orange,$fail,$smart,$full,$high;
if ($type != 'Data') {$error = $warning = $red = $orange = $fail = $smart = $full = $high = 0;}
$echo = [];
foreach ($disks as $disk) if (_var($disk,'type')==$type && strpos(_var($disk,'status'),'DISK_NP')===false && (!$pool||$pool==prefix(_var($disk,'name')))) {
$echo[] = "<tr class='updated'><td>";
@@ -247,6 +248,7 @@ function array_group($type, $pool=false) {
}
function extra_group() {
global $devs,$error,$warning,$red,$orange,$fail,$smart,$full,$high;
$error = $warning = $red = $orange = $fail = $smart = $full = $high = 0;
$echo = [];
foreach ($devs as $disk) {
$name = _var($disk,'name');
@@ -292,9 +294,9 @@ while (true) {
$devs = (array)@parse_ini_file("$varroot/devs.ini",true);
$disks = (array)@parse_ini_file("$varroot/disks.ini",true);
$saved = (array)@parse_ini_file("$varroot/monitor.ini",true);
$echo = [];
$size = _var($var,'mdResyncSize');
$spot = _var($var,'mdResyncPos');
$echo = []; $p = 0;
$size = _var($var,'mdResyncSize',0);
$spot = _var($var,'mdResyncPos',0);
require "$docroot/webGui/include/CustomMerge.php";
require "$docroot/webGui/include/Preselect.php";
// check for language changes
@@ -304,33 +306,15 @@ while (true) {
update_translation($locale_init);
}
//array devices
$a = 'disk';
$echo[$a] = [];
$error = $warning = $red = $orange = $fail = $smart = $full = $high = 0;
$echo[$a][] = array_group('Parity');
$echo[$a][] = array_group('Data');
$echo[$a][] = "\n".($error+$warning)."\n".($red+$orange)."\n".($fail+$smart)."\n".($full+$high);
$echo[$a] = implode($echo[$a]);
$echo['disk'] = [array_group('Parity').array_group('Data'), $error+$warning, $red+$orange, $fail+$smart, $full+$high];
//pool devices
$a = 'pool'; $p = 0;
$echo[$a] = [];
foreach (pools_filter($disks) as $pool) {
if (empty($disks[$pool]['devices'])) continue;
$error = $warning = $red = $orange = $fail = $smart = $full = $high = 0;
$echo[$a][$p] = [];
$echo[$a][$p][] = array_group('Cache',$pool);
$echo[$a][$p][] = "\n".($error+$warning)."\n".($red+$orange)."\n".($fail+$smart)."\n".($full+$high);
$echo[$a][$p] = implode($echo[$a][$p]);
$p++;
$echo['pool'][$p++] = [array_group('Cache',$pool), $error+$warning, $red+$orange, $fail+$smart, $full+$high];
}
//unassigned devices
$a = 'open';
$echo[$a] = [];
$error = $warning = $red = $orange = $fail = $smart = $full = $high = 0;
$echo[$a][] = extra_group();
$echo[$a][] = "\n".($error+$warning)."\n".($red+$orange)."\n".($fail+$smart)."\n".($full+$high);
$echo[$a] = implode($echo[$a]);
$echo['open'] = [extra_group(), $error+$warning, $red+$orange, $fail+$smart, $full+$high];
// parity status
$a = 'parity';
@@ -371,7 +355,6 @@ while (true) {
// parity schedule
$a = 'schedule';
$echo[$a] = [];
[$delta,$bytes] = [_var($var,'mdResyncDt',0),_var($var,'mdResyncDb',0)];
$synced = create_sync($stamps);
$sbSynced = array_shift($synced) ?: _var($var,'sbSynced');
@@ -381,6 +364,7 @@ while (true) {
$echo[$a][] = "<br><i class='fa fa-fw fa-clock-o'></i> "._('Elapsed time').": "._(my_clock(floor((time()-$sbSynced)/60)),2);
$echo[$a][] = "<br><i class='fa fa-fw fa-flag-checkered'></i> "._('Estimated finish').': '.($bytes ? _(my_clock(round(((($delta*(($size-$spot)/($bytes/100+1)))/100)/60),0)),2) : _('Unknown'));
$echo[$a][] = "<br><i class='fa fa-fw fa-search'></i> ".print_error(_var($var,'sbSyncErrs',0));
$echo[$a] = implode($echo[$a]);
} else {
[$date,$duration,$speed,$status,$error,$action,$size] = last_parity_log();
if (_var($var,'sbSyncExit',0)!=0) {
@@ -414,7 +398,7 @@ while (true) {
$echo[$a][] = "<br><i class='fa fa-fw fa-clock-o'></i> "._('Duration').': '.my_check($duration,$speed);
$echo[$a][] = "<br><i class='fa fa-fw fa-search'></i> ".print_error(_var($var,'sbSyncErrs',0));
}
[$m,$h] = explode(' ', $parity['hour']);
[$m,$h] = explode(' ',$parity['hour']);
$time = time();
$check = true;
switch ($parity['mode']) {
@@ -476,17 +460,14 @@ while (true) {
case 'WL': $t = stage(0); break;}
break;
}
$echo[$a][] = "\n";
if ($check) {
$frmt = _var($display,'date').(_var($display,'date')!='%c' ? ", "._var($display,'time') : "");
$echo[$a][] = sprintf(_('Next check scheduled on **%s**'),_(my_date($frmt,$time+$t),0));
$echo[$a][] = "<br><i class='fa fa-fw fa-clock-o'></i> "._('Due in').": "._(my_clock(floor($t/60)),2);
$extra = sprintf(_('Next check scheduled on **%s**'),_(my_date($frmt,$time+$t),0))."<br><i class='fa fa-fw fa-clock-o'></i> "._('Due in').": "._(my_clock(floor($t/60)),2);
} else {
$echo[$a][] = _('Scheduled parity check is disabled');
$extra = _('Scheduled parity check is disabled');
}
$echo[$a] = [implode($echo[$a]), $extra];
}
$echo[$a] = implode($echo[$a]);
$echo = json_encode($echo);
$md5_new = md5($echo,true);
if ($md5_new !== $md5_old) {
+13 -13
View File
@@ -68,8 +68,8 @@ while (true) {
$locale_init = _var($display,'locale');
update_translation($locale_init);
}
unset($status,$rows,$power,$load,$freq,$output,$volt);
$status = array_fill(0,7,"<span>-</span>");
unset($echo,$rows,$power,$load,$freq,$output,$volt);
$echo = array_fill(0,7,"<span>-</span>");
if (file_exists("/var/run/apcupsd.pid")) {
// get battery-level and runtime settings
$cfg = parse_plugin_cfg('dynamix.apcupsd');
@@ -80,32 +80,32 @@ while (true) {
[$key, $val] = array_map('trim', explode(':', $row, 2));
switch ($key) {
case 'MODEL':
$status[0] = $val;
$echo[0] = $val;
break;
case 'STATUS':
$text = strtr($val, $state);
$status[1] = $val ? (strpos($val,'ONLINE')!==false ? "<span $green>$text</span>" : "<span $red>$text</span>") : "<span $orange>"._('Refreshing')."...</span>";
$echo[1] = $val ? (strpos($val,'ONLINE')!==false ? "<span $green>$text</span>" : "<span $red>$text</span>") : "<span $orange>"._('Refreshing')."...</span>";
break;
case 'BCHARGE':
$charge = round($val);
$status[2] = $charge>$level ? "<span $green>$charge %</span>" : "<span $red>$charge %</span>";
$echo[2] = $charge>$level ? "<span $green>$charge %</span>" : "<span $red>$charge %</span>";
break;
case 'TIMELEFT':
$time = round($val);
$unit = _('minutes');
$status[3] = $time>$runtime ? "<span $green>$time $unit</span>" : "<span $red>$time $unit</span>";
$echo[3] = $time>$runtime ? "<span $green>$time $unit</span>" : "<span $red>$time $unit</span>";
break;
case 'NOMPOWER':
$power = $val;
$status[4] = $power>0 ? "<span $green>$power W</span>" : "<span $red>$power W</span>";
$echo[4] = $power>0 ? "<span $green>$power W</span>" : "<span $red>$power W</span>";
break;
case 'LOADPCT':
$load = $val;
$status[5] = round($load)." %";
$echo[5] = round($load)." %";
break;
case 'OUTPUTV':
$output = round($val);
$status[6] = "$output V";
$echo[6] = "$output V";
break;
case 'NOMINV':
$volt = $val;
@@ -117,11 +117,11 @@ while (true) {
break;
}
}
if (isset($power) && isset($load)) $status[5] = ($load<90 ? "<span $green>" : "<span $red>").round($power*$load/100)." W (".$status[5].")</span>";
elseif (isset($load)) $status[5] = ($load<90 ? "<span>" : "<span $red>").$status[5]."</span>";
$status[6] = isset($output) ? ((!$volt || ($minv<$output && $output<$maxv) ? "<span $green>" : "<span $red>").$status[6].(isset($freq) ? " ~ $freq Hz" : "")."</span>") : $status[6];
if (isset($power) && isset($load)) $echo[5] = ($load<90 ? "<span $green>" : "<span $red>").round($power*$load/100)." W (".$echo[5].")</span>";
elseif (isset($load)) $echo[5] = ($load<90 ? "<span>" : "<span $red>").$echo[5]."</span>";
$echo[6] = isset($output) ? ((empty($volt) || ($minv<$output && $output<$maxv) ? "<span $green>" : "<span $red>").$echo[6].(isset($freq) ? " ~ $freq Hz" : "")."</span>") : $echo[6];
}
$echo = implode(';', $status);
$echo = json_encode($echo);
$md5_new = md5($echo,true);
if ($md5_new !== $md5_old) {
publish('apcups', $echo);
+8 -2
View File
@@ -51,12 +51,12 @@ function write(...$messages){
}
curl_close($com);
}
function run($cmd, &$save=null) {
function run($cmd, &$save=null, $timeout=30) {
global $cli,$diag;
// output command for display
write($cmd);
// execute command with timeout of 30s
exec("timeout -s9 30 $cmd", $save);
exec("timeout -s9 $timeout $cmd", $save);
return implode("\n",$save);
}
@@ -351,6 +351,12 @@ if ($cli) {
$date = "{$split[2]}-{$split[3]}";
}
// Run Fix Common Problems if present and results are old / not present
if ( is_file("/usr/local/emhttp/plugins/fix.common.problems/scripts/scan.php") ) {
if ( ! is_file("/tmp/fix.common.problems/errors.json") || (time() - filemtime("/tmp/fix.common.problems/errors.json")) > 86400) {
run("/usr/local/emhttp/plugins/fix.common.problems/scripts/scan.php diagnostics",$ignore,60);
}
}
// don't anonymize system share names
$vardomain = (array)@parse_ini_file('/boot/config/domain.cfg');
$vardocker = (array)@parse_ini_file('/boot/config/docker.cfg');
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
if [[ -n $1 ]]; then
# $1 is new governor
echo $1 | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor &>/dev/null
fi
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/php -q
<?PHP
$cfg = '/boot/config/favorites.cfg';
if (!file_exists($cfg)) exit(0);
$file = fopen($cfg,'r');
while (($page = fgets($file))!==false) {
// update each favorite
$page = rtrim($page);
$line = fopen($page,'r');
// get current Menu settings
extract(parse_ini_string(fgets($line)));
fclose($line);
// remove and re-add label and escape single quotes for sed command
$Menu = str_replace([' MyFavorites',"'"],['',"'\''"],$Menu).' MyFavorites';
// update Menu settings
exec("sed -ri '0,/^Menu=\".+\"$/s//Menu=\"$Menu\"/' $page 2>/dev/null");
}
fclose($file);
exit(0);
?>
+7 -7
View File
@@ -35,8 +35,9 @@ span.w26{min-width:26%;max-width:26%;display:inline-block;float:left;overflow:hi
span.w36{min-width:36%;max-width:36%;display:inline-block;float:left;overflow:hidden;text-overflow:ellipsis}
span.w44{min-width:44%;max-width:44%;display:inline-block;float:left;overflow:hidden;text-overflow:ellipsis}
span.w72{min-width:72%;max-width:72%;display:inline-block;float:left;overflow:hidden;text-overflow:ellipsis}
span.w18:hover{overflow:visible}
span.center{display:block;width:96px;text-align:center;margin:4px 0 8px 0}
span.w26:hover,span.w18:hover{overflow:visible}
span.center{text-align:center;margin-bottom:4px}
span.center>div{margin:0 auto}
select#cpuline,select#netline{border:none;padding:0 12px 0 0}
select[name=enter_share],select[name=enter_user]{margin-top:0;float:none}
img#mycase{width:auto;max-width:128px;height:auto;max-height:128px}
@@ -53,7 +54,7 @@ i[class^="icon-u-"]{font-size:inherit}
i#mycase[class^="case-"]{font-size:128px}
i#mycase[class^="fa "]{font-size:96px}
a.cpu_close,span.hand{cursor:pointer;z-index:1001}
a.info{white-space:normal;word-spacing:40px}
a.info{white-space:normal;word-spacing:92px;word-break:break-all}
a.info span{word-spacing:normal}
tr#var0,tr#var1,tr#var2,tr#var3,tr#var4{cursor:alias}
tr#var1,tr#var4,tr#cpu_chart,.cpu_open{display:none}
@@ -70,9 +71,8 @@ input[value=Edit]{margin:12px 0 0 0;padding:5px 10px}
#current_date{font-size:1.3rem;margin-left:18px}
table.snapshot{margin-top:0}
.switch-button-background{margin-top:4px!important}
div.pie{height:96px;width:96px;border-radius:50%;display:flex;justify-content:center;align-items:center}
div.pie::after{content:'';position:absolute;height:72px;width:72px;border-radius:50%}
div.pie{height:92px;width:92px;border-radius:50%;display:flex;justify-content:center;align-items:center}
div.pie::after{content:'';position:absolute;height:68px;width:68px;border-radius:50%}
div.pie span{z-index:2;font-size:1.6rem;font-weight:bold}
div.more{margin-top:8px}
.var0,.var1,.var2,.var3{display:none;font-size:1.3rem!important}
legend{font-variant:small-caps;margin:8px 0 4px 0}
legend{font-variant:small-caps;margin-bottom:4px}
@@ -0,0 +1,2 @@
input[type=radio]{margin:0 10px 0 0}
div#vm{display:none;margin-left:35%}
@@ -19,6 +19,7 @@ i.spacing{margin-left:0;margin-right:10px}
i.icon{font-size:1.6rem;margin-right:4px;vertical-align:middle}
i.title{display:none}
i.control{cursor:pointer;color:#909090;font-size:1.8rem}
i.favo{display:none;font-size:1.8rem;position:absolute}
pre ul{margin:0;padding-top:0;padding-bottom:0;padding-left:28px}
pre li{margin:0;padding-top:0;padding-bottom:0;padding-left:18px}
big{font-size:1.4rem;font-weight:bold;text-transform:uppercase}
@@ -121,6 +122,7 @@ table.unraid thead tr:first-child>td{font-size:1.2rem;text-transform:uppercase;l
table.unraid tbody tr:not(.tr_last):hover>td{background-color:rgba(0,0,0,0.05)}
table.unraid tr>td{overflow:hidden;text-overflow:ellipsis;padding-left:8px}
table.unraid tr>td:hover{overflow:visible}
table.legacy{table-layout:auto}
table.disk_status{table-layout:fixed}
table.disk_status tr>td:last-child{padding-right:8px}
table.disk_status tr>td:nth-child(1){width:13%}
@@ -19,6 +19,7 @@ i.spacing{margin-left:-6px}
i.icon{font-size:1.6rem;margin-right:4px;vertical-align:middle}
i.title{margin-right:8px}
i.control{cursor:pointer;color:#606060;font-size:1.8rem}
i.favo{display:none;font-size:1.8rem;position:absolute;margin-left:12px}
hr{border:none;height:1px!important;color:#2b2b2b;background-color:#2b2b2b}
input[type=text],input[type=password],input[type=number],input[type=url],input[type=email],input[type=date],input[type=file],textarea,.textarea{font-family:clear-sans;font-size:1.3rem;background-color:transparent;border:none;border-bottom:1px solid #e5e5e5;padding:4px 0;text-indent:0;min-height:2rem;line-height:2rem;outline:none;width:300px;margin:0 20px 0 0;box-shadow:none;border-radius:0;color:#f2f2f2}
input[type=button],input[type=reset],input[type=submit],button,button[type=button],a.button{font-family:clear-sans;font-size:1.1rem;font-weight:bold;letter-spacing:1.8px;text-transform:uppercase;min-width:86px;margin:10px 12px 10px 0;padding:8px;text-align:center;text-decoration:none;white-space:nowrap;cursor:pointer;outline:none;border-radius:4px;border:none;color:#ff8c2f;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#e22828),to(#e22828)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#ff8c2f),to(#ff8c2f)) 100% 100% no-repeat;background:linear-gradient(90deg,#e22828 0,#ff8c2f) 0 0 no-repeat,linear-gradient(90deg,#e22828 0,#ff8c2f) 0 100% no-repeat,linear-gradient(0deg,#e22828 0,#e22828) 0 100% no-repeat,linear-gradient(0deg,#ff8c2f 0,#ff8c2f) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%}
@@ -30,7 +31,7 @@ input:hover[type=button],input:hover[type=reset],input:hover[type=submit],button
input[disabled],textarea[disabled]{color:#f2f2f2;border-bottom-color:#6c6c6c;opacity:0.5;cursor:default}
input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled],button[disabled],button[type=button][disabled],a.button[disabled]
input:hover[type=button][disabled],input:hover[type=reset][disabled],input:hover[type=submit][disabled],button:hover[disabled],button:hover[type=button][disabled],a.button:hover[disabled]
input:active[type=button][disabled],input:active[type=reset][disabled],input:active[type=submit][disabled],button:active[disabled],button:active[type=button][disabled],a.button:active[disabled]{cursor:default;color:#808080;background:-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#404040),to(#404040)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#808080),to(#808080)) 100% 100% no-repeat;background:linear-gradient(90deg,#404040 0,#808080) 0 0 no-repeat,linear-gradient(90deg,#404040 0,#808080) 0 100% no-repeat,linear-gradient(0deg,#404040 0,#404040) 0 100% no-repeat,linear-gradient(0deg,#808080 0,#808080) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%}
input:active[type=button][disabled],input:active[type=reset][disabled],input:active[type=submit][disabled],button:active[disabled],button:active[type=button][disabled],a.button:active[disabled]{opacity:0.5;cursor:default;color:#808080;background:-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#404040),to(#404040)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#808080),to(#808080)) 100% 100% no-repeat;background:linear-gradient(90deg,#404040 0,#808080) 0 0 no-repeat,linear-gradient(90deg,#404040 0,#808080) 0 100% no-repeat,linear-gradient(0deg,#404040 0,#404040) 0 100% no-repeat,linear-gradient(0deg,#808080 0,#808080) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%}
input::-webkit-input-placeholder{color:#486dba}
select{-webkit-appearance:none;font-family:clear-sans;font-size:1.3rem;min-width:166px;max-width:300px;padding:5px 8px 5px 0;text-indent:0;margin:0 10px 0 0;border:none;border-bottom:1px solid #e5e5e5;box-shadow:none;border-radius:0;color:#f2f2f2;background-color:transparent;background-image:linear-gradient(66.6deg, transparent 60%, #f2f2f2 40%),linear-gradient(113.4deg, #f2f2f2 40%, transparent 60%);background-position:calc(100% - 4px),100%;background-size:4px 6px,4px 6px;background-repeat:no-repeat;outline:none;display:inline-block;cursor:pointer}
select option{color:#f2f2f2;background-color:#262626}
@@ -119,6 +120,7 @@ table.unraid tbody tr:nth-child(even){background-color:#212121}
table.unraid tbody tr:not(.tr_last):hover>td{background-color:rgba(255,255,255,0.1)}
table.unraid tr>td{overflow:hidden;text-overflow:ellipsis;padding-left:8px}
table.unraid tr>td:hover{overflow:visible}
table.legacy{table-layout:auto}
table.disk_status{table-layout:fixed}
table.disk_status tr>td:last-child{padding-right:8px}
table.disk_status tr>td:nth-child(1){width:13%}
@@ -209,7 +211,7 @@ 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;border-top-left-radius:6px;border-top-right-radius:6px;border:1px solid #6c6c6c;border-bottom:none;background-color:#3c3c3c;opacity:0.5}
div.tab [type=radio]+label img{padding-right:4px}
div.Panel{text-align:center;float:left;margin:0 30px 30px 12px;height:8rem}
div.Panel{text-align:center;float:left;margin:0 0 30px 10px;padding-right:50px;height:8rem}
div.Panel a{text-decoration:none}
div.Panel span{height:42px;display:block}
div.Panel:hover .PanelText{text-decoration:underline}
@@ -19,6 +19,7 @@ i.spacing{margin-left:0;margin-right:10px}
i.icon{font-size:1.6rem;margin-right:4px;vertical-align:middle}
i.title{display:none}
i.control{cursor:pointer;color:#606060;font-size:1.8rem}
i.favo{display:none;font-size:1.8rem;position:absolute}
pre ul{margin:0;padding-top:0;padding-bottom:0;padding-left:28px}
pre li{margin:0;padding-top:0;padding-bottom:0;padding-left:18px}
big{font-size:1.4rem;font-weight:bold;text-transform:uppercase}
@@ -31,7 +32,7 @@ input[type=number]{-moz-appearance:textfield}
input:focus[type=text],input:focus[type=password],input:focus[type=number],input:focus[type=url],input:focus[type=email],input:focus[type=file],textarea:focus{background-color:#121510;border-color:#0072c6}
input:hover[type=button],input:hover[type=reset],input:hover[type=submit],button:hover,button:hover[type=button],a.button:hover{border-color:#0072c6;color:#b0b0b0}
input:active[type=button],input:active[type=reset],input:active[type=submit],button:active,button:active[type=button],a.button:active{border-color:#0072c6;box-shadow:none}
input[disabled],button[disabled],input:hover[type=button][disabled],input:hover[type=reset][disabled],input:hover[type=submit][disabled],button:hover[disabled],button:hover[type=button][disabled],input:active[type=button][disabled],input:active[type=reset][disabled],input:active[type=submit][disabled],button:active[disabled],button:active[type=button][disabled],textarea[disabled]{color:#808080;border-color:#808080;background-color:#383a34;opacity:0.3;cursor:default}
input[disabled],button[disabled],input:hover[type=button][disabled],input:hover[type=reset][disabled],input:hover[type=submit][disabled],button:hover[disabled],button:hover[type=button][disabled],input:active[type=button][disabled],input:active[type=reset][disabled],input:active[type=submit][disabled],button:active[disabled],button:active[type=button][disabled],textarea[disabled]{color:#808080;border-color:#808080;background-color:#383a34;opacity:0.5;cursor:default}
input::-webkit-input-placeholder{color:#00529b}
select{-webkit-appearance:none;font-family:clear-sans;font-size:1.3rem;min-width:188px;max-width:314px;padding:6px 14px 6px 6px;margin:0 10px 0 0;border:1px solid #606e7f;box-shadow:none;border-radius:0;color:#606e7f;background-color:transparent;background-image:linear-gradient(66.6deg, transparent 60%, #606e7f 40%),linear-gradient(113.4deg, #606e7f 40%, transparent 60%);background-position:calc(100% - 8px),calc(100% - 4px);background-size:4px 6px,4px 6px;background-repeat:no-repeat;outline:none;display:inline-block;cursor:pointer}
select option{color:#606e7f;background-color:#121510}
@@ -121,6 +122,7 @@ table.unraid thead tr:first-child>td{font-size:1.2rem;text-transform:uppercase;l
table.unraid tbody tr:not(.tr_last):hover>td{background-color:rgba(255,255,255,0.05)}
table.unraid tr>td{overflow:hidden;text-overflow:ellipsis;padding-left:8px}
table.unraid tr>td:hover{overflow:visible}
table.legacy{table-layout:auto}
table.disk_status{table-layout:fixed}
table.disk_status tr>td:last-child{padding-right:8px}
table.disk_status tr>td:nth-child(1){width:13%}
@@ -6,7 +6,7 @@ input:hover[type=button],input:hover[type=reset],input:hover[type=submit],button
input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled],button[disabled],button[type=button][disabled],a.button[disabled]
input:hover[type=button][disabled],input:hover[type=reset][disabled],input:hover[type=submit][disabled],button:hover[disabled],button:hover[type=button][disabled],a.button:hover[disabled]
input:active[type=button][disabled],input:active[type=reset][disabled],input:active[type=submit][disabled],button:active[disabled],button:active[type=button][disabled],a.button:active[disabled]
{cursor:default;color:#808080;background:-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#404040),to(#404040)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#808080),to(#808080)) 100% 100% no-repeat;background:linear-gradient(90deg,#404040 0,#808080) 0 0 no-repeat,linear-gradient(90deg,#404040 0,#808080) 0 100% no-repeat,linear-gradient(0deg,#404040 0,#404040) 0 100% no-repeat,linear-gradient(0deg,#808080 0,#808080) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%}
{opacity:0.5;cursor:default;color:#808080;background:-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#404040),to(#404040)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#808080),to(#808080)) 100% 100% no-repeat;background:linear-gradient(90deg,#404040 0,#808080) 0 0 no-repeat,linear-gradient(90deg,#404040 0,#808080) 0 100% no-repeat,linear-gradient(0deg,#404040 0,#404040) 0 100% no-repeat,linear-gradient(0deg,#808080 0,#808080) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%}
table{border-collapse:collapse;border-spacing:0;margin:0;width:100%}
table thead td{padding:6px 0}
table tbody td{padding:4px 0}
@@ -19,6 +19,7 @@ i.spacing{margin-left:-6px}
i.icon{font-size:1.6rem;margin-right:4px;vertical-align:middle}
i.title{margin-right:8px}
i.control{cursor:pointer;color:#909090;font-size:1.8rem}
i.favo{display:none;font-size:1.8rem;position:absolute;margin-left:12px}
hr{border:none;height:1px!important;color:#e3e3e3;background-color:#e3e3e3}
input[type=text],input[type=password],input[type=number],input[type=url],input[type=email],input[type=date],input[type=file],textarea,.textarea{font-family:clear-sans;font-size:1.3rem;background-color:transparent;border:none;border-bottom:1px solid #1c1b1b;padding:4px 0;text-indent:0;min-height:2rem;line-height:2rem;outline:none;width:300px;margin:0 20px 0 0;box-shadow:none;border-radius:0;color:#1c1b1b}
input[type=button],input[type=reset],input[type=submit],button,button[type=button],a.button{font-family:clear-sans;font-size:1.1rem;font-weight:bold;letter-spacing:1.8px;text-transform:uppercase;min-width:86px;margin:10px 12px 10px 0;padding:8px;text-align:center;text-decoration:none;white-space:nowrap;cursor:pointer;outline:none;border-radius:4px;border:none;color:#ff8c2f;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#e22828),to(#e22828)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#ff8c2f),to(#ff8c2f)) 100% 100% no-repeat;background:linear-gradient(90deg,#e22828 0,#ff8c2f) 0 0 no-repeat,linear-gradient(90deg,#e22828 0,#ff8c2f) 0 100% no-repeat,linear-gradient(0deg,#e22828 0,#e22828) 0 100% no-repeat,linear-gradient(0deg,#ff8c2f 0,#ff8c2f) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%}
@@ -30,7 +31,7 @@ input:hover[type=button],input:hover[type=reset],input:hover[type=submit],button
input[disabled],textarea[disabled]{color:#1c1b1b;border-bottom-color:#a2a2a2;opacity:0.5;cursor:default}
input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled],button[disabled],button[type=button][disabled],a.button[disabled]
input:hover[type=button][disabled],input:hover[type=reset][disabled],input:hover[type=submit][disabled],button:hover[disabled],button:hover[type=button][disabled],a.button:hover[disabled]
input:active[type=button][disabled],input:active[type=reset][disabled],input:active[type=submit][disabled],button:active[disabled],button:active[type=button][disabled],a.button:active[disabled]{cursor:default;color:#808080;background:-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#404040),to(#404040)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#808080),to(#808080)) 100% 100% no-repeat;background:linear-gradient(90deg,#404040 0,#808080) 0 0 no-repeat,linear-gradient(90deg,#404040 0,#808080) 0 100% no-repeat,linear-gradient(0deg,#404040 0,#404040) 0 100% no-repeat,linear-gradient(0deg,#808080 0,#808080) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%}
input:active[type=button][disabled],input:active[type=reset][disabled],input:active[type=submit][disabled],button:active[disabled],button:active[type=button][disabled],a.button:active[disabled]{opacity:0.5;cursor:default;color:#808080;background:-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#404040),to(#404040)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#808080),to(#808080)) 100% 100% no-repeat;background:linear-gradient(90deg,#404040 0,#808080) 0 0 no-repeat,linear-gradient(90deg,#404040 0,#808080) 0 100% no-repeat,linear-gradient(0deg,#404040 0,#404040) 0 100% no-repeat,linear-gradient(0deg,#808080 0,#808080) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%}
input::-webkit-input-placeholder{color:#486dba}
select{-webkit-appearance:none;font-family:clear-sans;font-size:1.3rem;min-width:166px;max-width:300px;padding:5px 8px 5px 0;text-indent:0;margin:0 10px 0 0;border:none;border-bottom:1px solid #1c1b1b;box-shadow:none;border-radius:0;color:#1c1b1b;background-color:transparent;background-image:linear-gradient(66.6deg, transparent 60%, #1c1b1b 40%),linear-gradient(113.4deg, #1c1b1b 40%, transparent 60%);background-position:calc(100% - 4px),100%;background-size:4px 6px,4px 6px;background-repeat:no-repeat;outline:none;display:inline-block;cursor:pointer}
select option{color:#1c1b1b;background-color:#e8e8e8}
@@ -119,6 +120,7 @@ table.unraid tbody tr:nth-child(even){background-color:#ededed}
table.unraid tbody tr:not(.tr_last):hover>td{background-color:rgba(0,0,0,0.1)}
table.unraid tr>td{overflow:hidden;text-overflow:ellipsis;padding-left:8px}
table.unraid tr>td:hover{overflow:visible}
table.legacy{table-layout:auto}
table.disk_status{table-layout:fixed}
table.disk_status tr>td:last-child{padding-right:8px}
table.disk_status tr>td:nth-child(1){width:13%}
@@ -209,7 +211,7 @@ 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;border-top-left-radius:6px;border-top-right-radius:6px;border:1px solid #b2b2b2;border-bottom:none;background-color:#e2e2e2;opacity:0.5}
div.tab [type=radio]+label img{padding-right:4px}
div.Panel{text-align:center;float:left;margin:0 30px 30px 12px;height:8rem}
div.Panel{text-align:center;float:left;margin:0 0 30px 10px;padding-right:50px;height:8rem}
div.Panel a{text-decoration:none}
div.Panel span{height:42px;display:block}
div.Panel:hover .PanelText{text-decoration:underline}
+12
View File
@@ -70,6 +70,18 @@ $path = substr(strtok(_var($_SERVER,'REQUEST_URI'),'?'),1);
// The current "task" is the first element of the path
$task = strtok($path,'/');
// Add translation for favorites page
if ($locale && $task=='Favorites') {
foreach(['settings','tools'] as $more) {
$text = "$docroot/languages/$locale/$more.txt";
if (!file_exists($text)) continue;
// additional translations
$store = "$docroot/languages/$locale/$more.dot";
if (!file_exists($store)) file_put_contents($store,serialize(parse_lang_file($text)));
$language = array_merge($language,unserialize(file_get_contents($store)));
}
}
// Here's the page we're rendering
$myPage = $site[basename($path)];
$pageroot = $docroot.'/'._var($myPage,'root');