repo reorg

This commit is contained in:
Tom Mortensen
2023-06-02 12:49:33 -07:00
parent 25e934f0a7
commit f9ec00b488
699 changed files with 6700 additions and 68456 deletions
@@ -0,0 +1,25 @@
Title="Add Container"
Cond="(pgrep('dockerd')!==false)"
Markdown="false"
---
<?PHP
/* Copyright 2005-2020, Lime Technology
* Copyright 2014-2020, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* Copyright 2012-2020, 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.
*/
?>
<?
// add docker translations (if needed)
if (substr($_SERVER['REQUEST_URI'],0,7) != '/Docker') {
$docker = "$docroot/languages/$locale/docker.dot";
if (file_exists($docker)) $language = array_merge($language,unserialize(file_get_contents($docker)));
}
eval('?>'.parse_file("$docroot/plugins/dynamix.docker.manager/include/CreateDocker.php"));
?>
@@ -0,0 +1,25 @@
Menu="Tasks:60"
Type="xmenu"
Code="e90b"
Lock="true"
Cond="exec(\"grep -o '^DOCKER_ENABLED=.yes' /boot/config/docker.cfg 2>/dev/null\")"
---
<?PHP
/* Copyright 2005-2021, Lime Technology
* Copyright 2014-2021, Guilherme Jardim, Eric Schultz, Jon Panozzo.
*
* 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.
*/
?>
<?
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>";
}
?>
@@ -0,0 +1,193 @@
Menu="Docker:1"
Title="Docker Containers"
Tag="cubes"
Cond="is_file('/var/run/dockerd.pid')"
Markdown="false"
Nchan="docker_load:stop"
---
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2014-2023, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* 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.
*/
?>
<?
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
$width = in_array($theme,['white','black']) ? -58: -44;
$top = in_array($theme,['white','black']) ? 40 : 20;
$busy = "<i class='fa fa-spin fa-circle-o-notch'></i> "._('Please wait')."... "._('starting up containers');
$cpus = cpu_list();
?>
<link type="text/css" rel="stylesheet" href="<?autov('/webGui/styles/jquery.ui.css')?>">
<link type="text/css" rel="stylesheet" href="<?autov('/webGui/styles/jquery.switchbutton.css')?>">
<link type="text/css" rel="stylesheet" href="<?autov("/plugins/dynamix.docker.manager/styles/style-$theme.css")?>">
<style>
.basic{display:block}
.advanced{display:none;white-space:nowrap}
.log{cursor:zoom-in}
.exec{cursor:pointer}
table#docker_containers{text-align:left}
th.five{width:5%}
th.nine{width:9%}
th.load{width:140px}
input.wait{width:24px;margin:0 4px;padding:0 5px;border:none;box-shadow:none;background-color:transparent}
table tbody td{line-height:normal}
i.mover{margin-right:8px;display:none}
#resetsort{margin-left:12px;display:inline-block;width:32px}
</style>
<table id="docker_containers" class="tablesorter shift">
<thead><tr><th><a id="resetsort" class="nohand" onclick="resetSorting()" title="_(Reset sorting)_"><i class="fa fa-th-list"></i></a>_(Application)_</th><th>_(Version)_</th><th>_(Network)_</th><th>_(Port Mappings)_ <small>(_(App to Host)_)</small></th><th>_(Volume Mappings)_ <small>(_(App to Host)_)</small></th><th class="load advanced">_(CPU & Memory load)_</th><th class="nine">_(Autostart)_</th><th class="five">_(Uptime)_</th></tr></thead>
<tbody id="docker_list"><tr><td colspan='8'></td></tr></tbody>
</table>
<input type="button" onclick="addContainer()" value="_(Add Container)_" style="display:none">
<input type="button" onclick="startAll()" value="_(Start All)_" style="display:none">
<input type="button" onclick="stopAll()" value="_(Stop All)_" style="display:none">
<input type="button" onclick="pauseAll()" value="_(Pause All)_" style="display:none">
<input type="button" onclick="resumeAll()" value="_(Resume All)_" style="display:none">
<input type="button" onclick="checkAll()" value="_(Check for Updates)_" id="checkAll" style="display:none">
<input type="button" onclick="updateAll()" value="_(Update All)_" id="updateAll" style="display:none">
<input type="button" onclick="contSizes()" value="_(Container Size)_" style="display:none">
<div id="iframe-popup" style="display:none;-webkit-overflow-scrolling:touch;"></div>
<script src="<?autov('/webGui/javascript/jquery.switchbutton.js')?>"></script>
<script src="<?autov('/plugins/dynamix.docker.manager/javascript/docker.js')?>"></script>
<script>
var docker = [];
<?if (!$tabbed):?>
$('.title').append("<span id='busy' class='red-text strong' style='display:none;margin-left:40px'><?=$busy?></span>");
<?else:?>
$('.tabs').append("<span id='busy' class='red-text strong' style='display:none;position:relative;top:<?=$top?>px;left:40px;font-size:1.4rem;letter-spacing:2px'><?=$busy?></span>");
<?endif;?>
<?if (_var($display,'resize')):?>
function resize() {
$('#docker_list').height(Math.max(window.innerHeight-340,330));
$('#docker_containers thead,#docker_containers tbody').removeClass('fixed');
$('#docker_containers thead tr th').each(function(){$(this).width($(this).width());});
$('#docker_containers tbody tr td').each(function(){$(this).width($(this).width());});
$('#docker_containers thead,#docker_containers tbody').addClass('fixed');
}
<?endif;?>
function resetSorting() {
if ($.cookie('lockbutton')==null) return;
$('input[type=button]').prop('disabled',true);
$.post('/plugins/dynamix.docker.manager/include/UserPrefs.php',{reset:true},function(){loadlist();});
}
function listview() {
var more = $.cookie('docker_listview_mode')=='advanced';
<?if(($dockercfg['DOCKER_READMORE']??'yes') === 'yes'):?>
$('.docker_readmore').readmore({maxHeight:32,moreLink:"<a href='#' style='text-align:center'><i class='fa fa-chevron-down'></i></a>",lessLink:"<a href='#' style='text-align:center'><i class='fa fa-chevron-up'></i></a>"});
<?endif;?>
$('input.autostart').each(function(){
var wait = $('#'+$(this).prop('id').replace('auto','wait'));
var auto = $(this).prop('checked');
if (auto && more) wait.show(); else wait.hide();
});
}
function LockButton() {
if ($.cookie('lockbutton')==null) {
$.cookie('lockbutton','lockbutton');
$('#resetsort').removeClass('nohand').addClass('hand');
$('i.mover').show();
$('#docker_list .sortable').css({'cursor':'move'});
<?if ($themes1):?>
$('div.nav-item.LockButton').find('a').prop('title',"_(Lock sortable items)_");
$('div.nav-item.LockButton').find('b').removeClass('icon-u-lock green-text').addClass('icon-u-lock-open red-text');
<?endif;?>
$('div.nav-item.LockButton').find('span').text("_(Lock sortable items)_");
$('#docker_list').sortable({helper:'clone',items:'.sortable',cursor:'grab',axis:'y',containment:'parent',cancel:'span.docker_readmore,input',delay:100,opacity:0.5,zIndex:9999,forcePlaceholderSize:true,
update:function(e,ui){
var row = $('#docker_list').find('tr:first');
var names = ''; var index = '';
row.parent().children().find('td.ct-name').each(function(){names+=$(this).find('.appname').text()+';';index+=$(this).parent().parent().children().index($(this).parent())+';';});
$.post('/plugins/dynamix.docker.manager/include/UserPrefs.php',{names:names,index:index});
}});
} else {
$.removeCookie('lockbutton');
$('#resetsort').removeClass('hand').addClass('nohand');
$('i.mover').hide();
$('#docker_list .sortable').css({'cursor':'default'});
<?if ($themes1):?>
$('div.nav-item.LockButton').find('a').prop('title',"_(Unlock sortable items)_");
$('div.nav-item.LockButton').find('b').removeClass('icon-u-lock-open red-text').addClass('icon-u-lock green-text');
<?endif;?>
$('div.nav-item.LockButton').find('span').text("_(Unlock sortable items)_");
$('#docker_list').sortable('destroy');
}
}
function loadlist(init) {
timers.docker = setTimeout(function(){$('div.spinner.fixed').show('slow');},500);
$.get('/plugins/dynamix.docker.manager/include/DockerContainers.php',function(d) {
clearTimeout(timers.docker);
var data = d.split(/\0/);
$('#docker_list').html(data[0]);
$('head').append('<script>'+data[1]+'<\/script>');
<?if (_var($display,'resize')):?>
resize();
if (init) $(window).bind('resize',function(){resize();});
<?endif;?>
$('.iconstatus').each(function(){
if ($(this).hasClass('stopped')) $('div.'+$(this).prop('id')).hide();
});
$('.autostart').switchButton({labels_placement:'right', on_label:"_(On)_", off_label:"_(Off)_"});
$('.autostart').change(function(){
var more = $.cookie('docker_listview_mode')=='advanced';
var wait = $('#'+$(this).prop('id').replace('auto','wait'));
var auto = $(this).prop('checked');
if (auto && more) wait.show(); else wait.hide();
$.post('/plugins/dynamix.docker.manager/include/UpdateConfig.php',{action:'autostart',container:$(this).attr('container'),auto:auto,wait:wait.find('input.wait').val()});
});
$('input.wait').change(function(){
$.post('/plugins/dynamix.docker.manager/include/UpdateConfig.php',{action:'wait',container:$(this).attr('container'),wait:$(this).val()});
});
if ($.cookie('docker_listview_mode')=='advanced') {$('.advanced').show(); $('.basic').hide();}
$('input[type=button]').prop('disabled',false).show('slow');
var update = false, rebuild = false;
for (var i=0,ct; ct=docker[i]; i++) {
if (ct.update==1) update = true;
if (ct.update==2) rebuild = true;
}
listview();
$('div.spinner.fixed').hide('slow');
if (data[2]==1) {$('#busy').show(); setTimeout(loadlist,5000);} else if ($('#busy').is(':visible')) {$('#busy').hide(); setTimeout(loadlist,3000);}
if (!update) $('input#updateAll').prop('disabled',true);
if (rebuild) rebuildAll();
});
}
function contSizes() {
// show spinner over window
$('div.spinner.fixed').css({'z-index':'100000'}).show();
openPlugin('container_size', "_(Container Size)_");
}
var dockerload = new NchanSubscriber('/sub/dockerload',{subscriber:'websocket'});
dockerload.on('message', function(msg){
var data = msg.split('\n');
for (var i=0,row; row=data[i]; i++) {
var id = row.split(';');
var w1 = Math.round(Math.min(id[1].slice(0,-1)/<?=count($cpus)*count(preg_split('/[,-]/',$cpus[0]))?>,100)*100)/100+'%';
$('.cpu-'+id[0]).text(w1.replace('.','<?=_var($display,'number','.,')[0]?>'));
$('.mem-'+id[0]).text(id[2]);
$('#cpu-'+id[0]).css('width',w1);
}
});
$(function() {
$(".tabs").append('<span class="status"><span><input type="checkbox" class="advancedview"></span></span>');
$('.advancedview').switchButton({labels_placement:'left', on_label:"_(Advanced View)_", off_label:"_(Basic View)_", checked:$.cookie('docker_listview_mode')=='advanced'});
$('.advancedview').change(function(){
$('.advanced').toggle('slow');
$('.basic').toggle('slow');
$.cookie('docker_listview_mode',$('.advancedview').is(':checked')?'advanced':'basic',{expires:3650});
listview();
});
$.removeCookie('lockbutton');
loadlist(true);
dockerload.start();
});
</script>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,12 @@
EXTENDED DOCKER CONFIGURATION PAGE
Copyright (C) 2014 Guilherme Jardim
FORKED FROM:
**Copyright 2005-2014 Lime Technology.**
This Software is licensed under [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html).
Unraid is a registered trademark of [Lime Technology, Inc.](http://lime-technology.com).
This file shall be included in all copies or substantial portions of the Software.
@@ -0,0 +1,25 @@
Title="Update Container"
Cond="(pgrep('dockerd')!==false)"
Markdown="false"
---
<?PHP
/* Copyright 2005-2020, Lime Technology
* Copyright 2014-2020, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* Copyright 2012-2020, 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.
*/
?>
<?
// add docker translations (if needed)
if (substr($_SERVER['REQUEST_URI'],0,7) != '/Docker') {
$docker = "$docroot/languages/$locale/docker.dot";
if (file_exists($docker)) $language = array_merge($language,unserialize(file_get_contents($docker)));
}
eval('?>'.parse_file("$docroot/plugins/dynamix.docker.manager/include/CreateDocker.php"));
?>
@@ -0,0 +1,10 @@
DOCKER_ENABLED="no"
DOCKER_IMAGE_SIZE="20"
DOCKER_LOG_ROTATION="yes"
DOCKER_LOG_SIZE="50m"
DOCKER_LOG_FILES="1"
DOCKER_AUTHORING_MODE="no"
DOCKER_USER_NETWORKS="remove"
DOCKER_ALLOW_ACCESS=""
DOCKER_TIMEOUT=10
DOCKER_READMORE="yes"
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

@@ -0,0 +1,33 @@
<?PHP
/* Copyright 2005-2022, Lime Technology
* Copyright 2014-2022, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* Copyright 2012-2022, 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 = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
$user_prefs = $dockerManPaths['user-prefs'];
$action = $_POST['action'];
$status = $action=='start' ? 'exited' : ($action=='unpause' ? 'paused' : 'running');
$containers = DockerUtil::docker("ps -a --filter status='$status' --format='{{.Names}}'",true);
if (file_exists($user_prefs)) {
$prefs = parse_ini_file($user_prefs); $sort = [];
foreach ($containers as $ct) $sort[] = array_search($ct,$prefs) ?? 999;
array_multisort($sort, ($action=='start'?SORT_ASC:SORT_DESC), SORT_NUMERIC, $containers);
}
foreach ($containers as $ct) {
DockerUtil::docker("$action $ct >/dev/null");
addRoute($ct);
}
?>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,160 @@
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2014-2023, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* 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 = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
// add translations
$_SERVER['REQUEST_URI'] = 'docker';
require_once "$docroot/webGui/include/Translations.php";
$DockerClient = new DockerClient();
$DockerTemplates = new DockerTemplates();
$containers = $DockerClient->getDockerContainers();
$images = $DockerClient->getDockerImages();
$user_prefs = $dockerManPaths['user-prefs'];
$autostart_file = $dockerManPaths['autostart-file'];
if (!$containers && !$images) {
echo "<tr><td colspan='7' style='text-align:center;padding-top:12px'>"._('No Docker containers installed')."</td></tr>";
return;
}
if (file_exists($user_prefs)) {
$prefs = @parse_ini_file($user_prefs) ?: [];
$sort = [];
foreach ($containers as $ct) $sort[] = array_search($ct['Name'],$prefs);
array_multisort($sort,SORT_NUMERIC,$containers);
unset($sort);
}
// Read container info
$allInfo = $DockerTemplates->getAllInfo();
$docker = [];
$null = '0.0.0.0';
$autostart = @file($autostart_file,FILE_IGNORE_NEW_LINES) ?: [];
$names = array_map('var_split',$autostart);
function my_lang_time($text) {
[$number, $text] = my_explode(' ',$text,2);
return sprintf(_("%s $text"),$number);
}
function my_lang_log($text) {
global $language;
if (isset($language['healthy'])) $text = str_replace('healthy',$language['healthy'],$text);
if (isset($language['Exited'])) $text = str_replace('Exited',$language['Exited'],$text);
if (strpos($text,'ago')!==false) {
[$t1,$t2] = my_explode(') ',$text);
return $t1.'): '.my_lang_time($t2);
}
return _(_($text),2);
}
foreach ($containers as $ct) {
$name = $ct['Name'];
$id = $ct['Id'];
$info = &$allInfo[$name];
$running = $info['running'] ? 1 : 0;
$paused = $info['paused'] ? 1 : 0;
$is_autostart = $info['autostart'] ? 'true':'false';
$updateStatus = substr($ct['NetworkMode'],-4)==':???' ? 2 : ($info['updated']=='true' ? 0 : ($info['updated']=='false' ? 1 : 3));
$template = $info['template']??'';
$shell = $info['shell']??'';
$webGui = html_entity_decode($info['url']??'');
$support = html_entity_decode($info['Support']??'');
$project = html_entity_decode($info['Project']??'');
$registry = html_entity_decode($info['registry']??'');
$donateLink = html_entity_decode($info['DonateLink']??'');
$readme = html_entity_decode($info['ReadMe']??'');
$menu = sprintf("onclick=\"addDockerContainerContext('%s','%s','%s',%s,%s,%s,%s,'%s','%s','%s','%s','%s','%s', '%s','%s')\"", addslashes($name), addslashes($ct['ImageId']), addslashes($template), $running, $paused, $updateStatus, $is_autostart, addslashes($webGui), $shell, $id, addslashes($support), addslashes($project),addslashes($registry),addslashes($donateLink),addslashes($readme));
$docker[] = "docker.push({name:'$name',id:'$id',state:$running,pause:$paused,update:$updateStatus});";
$shape = $running ? ($paused ? 'pause' : 'play') : 'square';
$status = $running ? ($paused ? 'paused' : 'started') : 'stopped';
$color = $status=='started' ? 'green-text' : ($status=='paused' ? 'orange-text' : 'red-text');
$update = $updateStatus==1 ? 'blue-text' : '';
$icon = $info['icon'] ?: '/plugins/dynamix.docker.manager/images/question.png';
$image = substr($icon,-4)=='.png' ? "<img src='$icon?".filemtime("$docroot{$info['icon']}")."' class='img' onerror=this.src='/plugins/dynamix.docker.manager/images/question.png';>" : (substr($icon,0,5)=='icon-' ? "<i class='$icon img'></i>" : "<i class='fa fa-$icon img'></i>");
$wait = var_split($autostart[array_search($name,$names)]??'',1);
$ports = [];
foreach ($ct['Ports'] as $port) {
$intern = $running ? ($ct['NetworkMode']=='host' ? $host : _var($port,'IP')) : $null;
$extern = $running ? (_var($port,'NAT') ? $host : $intern) : $null;
$ports[] = sprintf('%s:%s/%s<i class="fa fa-arrows-h" style="margin:0 6px"></i>%s:%s', $intern, _var($port,'PrivatePort'), strtoupper(_var($port,'Type')), $extern, _var($port,'PublicPort'));
}
$paths = [];
$ct['Volumes'] = is_array($ct['Volumes']) ? $ct['Volumes'] : [];
foreach ($ct['Volumes'] as $mount) {
[$host_path,$container_path,$access_mode] = my_explode(':',$mount,3);
$paths[] = sprintf('%s<i class="fa fa-%s" style="margin:0 6px"></i>%s', htmlspecialchars($container_path), $access_mode=='ro'?'long-arrow-left':'arrows-h', htmlspecialchars($host_path));
}
echo "<tr class='sortable'><td class='ct-name' style='width:220px;padding:8px'><i class='fa fa-arrows-v mover orange-text'></i>";
if ($template) {
$appname = "<a class='exec' onclick=\"editContainer('".addslashes(htmlspecialchars($name))."','".addslashes(htmlspecialchars($template))."')\">".htmlspecialchars($name)."</a>";
} else {
$appname = htmlspecialchars($name);
}
echo "<span class='outer'><span id='$id' $menu class='hand'>$image</span><span class='inner'><span class='appname $update'>$appname</span><br><i id='load-$id' class='fa fa-$shape $status $color'></i><span class='state'>"._($status)."</span></span></span>";
echo "<div class='advanced' style='margin-top:8px'>"._('Container ID').": $id<br>";
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']);
if ($registry) {
echo "<a href='".htmlspecialchars($registry)."' target='_blank'>".htmlspecialchars(compress($author,24))."</a>";
} else {
echo htmlspecialchars(compress($author,24));
}
echo "</div></td><td class='updatecolumn'>";
switch ($updateStatus) {
case 0:
echo "<span class='green-text' style='white-space:nowrap;'><i class='fa fa-check fa-fw'></i> "._('up-to-date')."</span>";
echo "<div class='advanced'><a class='exec' onclick=\"updateContainer('".addslashes(htmlspecialchars($name))."');\"><span style='white-space:nowrap;'><i class='fa fa-cloud-download fa-fw'></i> "._('force update')."</span></a></div>";
break;
case 1:
echo "<div class='advanced'><span class='orange-text' style='white-space:nowrap;'><i class='fa fa-flash fa-fw'></i> "._('update ready')."</span></div>";
echo "<a class='exec' onclick=\"updateContainer('".addslashes(htmlspecialchars($name))."');\"><span style='white-space:nowrap;'><i class='fa fa-cloud-download fa-fw'></i> "._('apply update')."</span></a>";
break;
case 2:
echo "<div class='advanced'><span class='orange-text' style='white-space:nowrap;'><i class='fa fa-flash fa-fw'></i> "._('rebuild ready')."</span></div>";
echo "<a class='exec'><span style='white-space:nowrap;'><i class='fa fa-recycle fa-fw'></i> "._('rebuilding')."</span></a>";
break;
default:
echo "<span class='orange-text' style='white-space:nowrap;'><i class='fa fa-unlink'></i> "._('not available')."</span>";
echo "<div class='advanced'><a class='exec' onclick=\"updateContainer('".addslashes(htmlspecialchars($name))."');\"><span style='white-space:nowrap;'><i class='fa fa-cloud-download fa-fw'></i> "._('force update')."</span></a></div>";
break;
}
echo "<div class='advanced'><i class='fa fa-info-circle fa-fw'></i> ".compress(_($version),12,0)."</div></td>";
echo "<td>{$ct['NetworkMode']}</td>";
echo "<td style='white-space:nowrap'><span class='docker_readmore'>".implode('<br>',$ports)."</span></td>";
echo "<td style='word-break:break-all'><span class='docker_readmore'>".implode('<br>',$paths)."</span></td>";
echo "<td class='advanced'><span class='cpu-$id'>0%</span><div class='usage-disk mm'><span id='cpu-$id' style='width:0'></span><span></span></div>";
echo "<br><span class='mem-$id'>0 / 0</span></td>";
echo "<td><input type='checkbox' id='$id-auto' class='autostart' container='".htmlspecialchars($name)."'".($info['autostart'] ? ' checked':'').">";
echo "<span id='$id-wait' style='float:right;display:none'>"._('wait')."<input class='wait' container='".htmlspecialchars($name)."' type='number' value='$wait' placeholder='0' title=\""._('seconds')."\"></span></td>";
echo "<td><div style='white-space:nowrap'>".htmlspecialchars(str_replace('Up',_('Uptime').':',my_lang_log($ct['Status'])))."<div style='margin-top:4px'>"._('Created').": ".htmlspecialchars(my_lang_time($ct['Created']))."</div></div></td></tr>";
}
foreach ($images as $image) {
if (count($image['usedBy'])) continue;
$id = $image['Id'];
$menu = sprintf("onclick=\"addDockerImageContext('%s','%s')\"", $id, implode(',',$image['Tags']));
echo "<tr class='advanced'><td style='width:220px;padding:8px'>";
echo "<span class='outer apps'><span id='$id' $menu class='hand'><img src='/webGui/images/disk.png' class='img'></span><span class='inner'>("._('orphan image').")<br><i class='fa fa-square stopped grey-text'></i><span class='state'>"._('stopped')."</span></span></span>";
echo "</td><td colspan='6'>"._('Image ID').": $id<br>";
echo implode(', ',$image['Tags']);
echo "</td><td>"._('Created')." ".htmlspecialchars(_($image['Created'],0))."</td></tr>";
}
echo "\0".implode($docker)."\0".(pgrep('rc.docker')!==false ? 1:0);
?>
@@ -0,0 +1,22 @@
<?PHP
/* Copyright 2005-2022, Lime Technology
* Copyright 2014-2022, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* Copyright 2012-2022, 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 = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
$ncsi = exec("wget --spider --no-check-certificate -nv -T10 -t1 https://www.msftncsi.com/ncsi.txt 2>&1|grep -o 'OK'")=='OK';
$DockerTemplates = new DockerTemplates();
if ($ncsi) $DockerTemplates->downloadTemplates();
$DockerTemplates->getAllInfo($ncsi,$ncsi);
?>
@@ -0,0 +1,73 @@
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2014-2023, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* 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 = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Secure.php";
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
// add translations
$_SERVER['REQUEST_URI'] = 'docker';
require_once "$docroot/webGui/include/Translations.php";
$DockerClient = new DockerClient();
$action = unscript(_var($_REQUEST,'action'));
$container = unbundle(_var($_REQUEST,'container'));
$name = unscript(_var($_REQUEST,'name'));
$image = unscript(_var($_REQUEST,'image'));
$arrResponse = ['error' => _('Missing parameters')];
switch ($action) {
case 'start':
if ($container) $arrResponse = ['success' => $DockerClient->startContainer($container)];
break;
case 'pause':
if ($container) $arrResponse = ['success' => $DockerClient->pauseContainer($container)];
break;
case 'stop':
if ($container) $arrResponse = ['success' => $DockerClient->stopContainer($container)];
break;
case 'resume':
if ($container) $arrResponse = ['success' => $DockerClient->resumeContainer($container)];
break;
case 'restart':
if ($container) $arrResponse = ['success' => $DockerClient->restartContainer($container)];
break;
case 'remove_container':
if ($container) $arrResponse = ['success' => $DockerClient->removeContainer($name, $container, 1)];
break;
case 'remove_image':
if ($image) $arrResponse = ['success' => $DockerClient->removeImage($image)];
break;
case 'remove_all':
if ($container && $image) {
// first: try to remove container
$ret = $DockerClient->removeContainer($name, $container, 2);
if ($ret === true) {
// next: try to remove image
$arrResponse = ['success' => $DockerClient->removeImage($image)];
} else {
// error: container failed to remove
$arrResponse = ['success' => $ret];
}
}
break;
default:
$arrResponse = ['error' => _('Unknown action')." '$action'"];
break;
}
header('Content-Type: application/json');
die(json_encode($arrResponse));
@@ -0,0 +1,533 @@
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2014-2023, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* 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.
*/
?>
<?
function addRoute($ct) {
// add static route(s) for remote WireGuard access
[$pid,$net] = array_pad(explode(' ',exec("docker inspect --format='{{.State.Pid}} {{.NetworkSettings.Networks}}' $ct")),2,'');
$net = substr($net,4,strpos($net,':')-4);
if (!$pid || $net != 'br0') return;
$thisip = ipaddr();
foreach (glob('/etc/wireguard/wg*.cfg') as $cfg) {
$network = exec("grep -Pom1 '^Network:0=\"\\K[^\"]+' $cfg");
if ($network) exec("nsenter -n -t $pid ip -4 route add $network via $thisip 2>/dev/null");
}
}
function xml_encode($string) {
return htmlspecialchars($string, ENT_XML1, 'UTF-8');
}
function xml_decode($string) {
return strval(html_entity_decode($string, ENT_XML1, 'UTF-8'));
}
function postToXML($post, $setOwnership=false) {
$dom = new domDocument;
$dom->appendChild($dom->createElement("Container"));
$xml = simplexml_import_dom($dom);
$xml['version'] = 2;
$xml->Name = xml_encode(preg_replace('/\s+/', '', $post['contName']));
$xml->Repository = xml_encode(trim($post['contRepository']));
$xml->Registry = xml_encode(trim($post['contRegistry']));
$xml->Network = xml_encode($post['contNetwork']);
$xml->MyIP = xml_encode($post['contMyIP']);
$xml->Shell = xml_encode($post['contShell']);
$xml->Privileged = strtolower($post['contPrivileged']??'')=='on' ? 'true' : 'false';
$xml->Support = xml_encode($post['contSupport']);
$xml->Project = xml_encode($post['contProject']);
$xml->Overview = xml_encode($post['contOverview']);
$xml->Category = xml_encode($post['contCategory']);
$xml->WebUI = xml_encode(trim($post['contWebUI']));
$xml->TemplateURL = xml_encode($post['contTemplateURL']);
$xml->Icon = xml_encode(trim($post['contIcon']));
$xml->ExtraParams = xml_encode($post['contExtraParams']);
$xml->PostArgs = xml_encode($post['contPostArgs']);
$xml->CPUset = xml_encode($post['contCPUset']);
$xml->DateInstalled = xml_encode(time());
$xml->DonateText = xml_encode($post['contDonateText']);
$xml->DonateLink = xml_encode($post['contDonateLink']);
$xml->Requires = xml_encode($post['contRequires']);
$size = is_array($post['confName']) ? count($post['confName']) : 0;
for ($i = 0; $i < $size; $i++) {
$Type = $post['confType'][$i];
$config = $xml->addChild('Config', xml_encode($post['confValue'][$i]));
$config['Name'] = xml_encode($post['confName'][$i]);
$config['Target'] = xml_encode($post['confTarget'][$i]);
$config['Default'] = xml_encode($post['confDefault'][$i]);
$config['Mode'] = xml_encode($post['confMode'][$i]);
$config['Description'] = xml_encode($post['confDescription'][$i]);
$config['Type'] = xml_encode($post['confType'][$i]);
$config['Display'] = xml_encode($post['confDisplay'][$i]);
$config['Required'] = xml_encode($post['confRequired'][$i]);
$config['Mask'] = xml_encode($post['confMask'][$i]);
}
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml->asXML());
return $dom->saveXML();
}
function xmlToVar($xml) {
global $subnet;
$xml = is_file($xml) ? simplexml_load_file($xml) : simplexml_load_string($xml);
$out = [];
$out['Name'] = preg_replace('/\s+/', '', xml_decode($xml->Name));
$out['Repository'] = xml_decode($xml->Repository);
$out['Registry'] = xml_decode($xml->Registry);
$out['Network'] = xml_decode($xml->Network);
$out['MyIP'] = xml_decode($xml->MyIP ?? '');
$out['Shell'] = xml_decode($xml->Shell ?? 'sh');
$out['Privileged'] = xml_decode($xml->Privileged);
$out['Support'] = xml_decode($xml->Support);
$out['Project'] = xml_decode($xml->Project);
$out['Overview'] = stripslashes(xml_decode($xml->Overview));
$out['Category'] = xml_decode($xml->Category);
$out['WebUI'] = xml_decode($xml->WebUI);
$out['TemplateURL'] = xml_decode($xml->TemplateURL);
$out['Icon'] = xml_decode($xml->Icon);
$out['ExtraParams'] = xml_decode($xml->ExtraParams);
$out['PostArgs'] = xml_decode($xml->PostArgs);
$out['CPUset'] = xml_decode($xml->CPUset);
$out['DonateText'] = xml_decode($xml->DonateText);
$out['DonateLink'] = xml_decode($xml->DonateLink);
$out['Requires'] = xml_decode($xml->Requires);
$out['Config'] = [];
if (isset($xml->Config)) {
foreach ($xml->Config as $config) {
$c = [];
$c['Value'] = strlen(xml_decode($config)) ? xml_decode($config) : xml_decode($config['Default']);
foreach ($config->attributes() as $key => $value) {
$value = xml_decode($value);
if ($key == 'Mode') {
switch (xml_decode($config['Type'])) {
case 'Path':
$value = in_array(strtolower($value),['rw','rw,slave','rw,shared','ro','ro,slave','ro,shared']) ? $value : "rw";
break;
case 'Port':
$value = in_array(strtolower($value),['tcp','udp']) ? $value : "tcp";
break;
}
}
$c[$key] = strip_tags(html_entity_decode($value));
}
$out['Config'][] = $c;
}
}
// some xml templates advertise as V2 but omit the new <Network> element
// check for and use the V1 <Networking> element when this occurs
if (empty($out['Network']) && isset($xml->Networking->Mode)) {
$out['Network'] = xml_decode($xml->Networking->Mode);
}
// check if network exists
if (!key_exists($out['Network'],$subnet)) $out['Network'] = 'none';
// V1 compatibility
if ($xml['version'] != '2') {
if (isset($xml->Description)) {
$out['Overview'] = stripslashes(xml_decode($xml->Description));
}
if (isset($xml->Networking->Publish->Port)) {
$portNum = 0;
foreach ($xml->Networking->Publish->Port as $port) {
if (empty(xml_decode($port->ContainerPort))) continue;
$portNum += 1;
$out['Config'][] = [
'Name' => "Host Port {$portNum}",
'Target' => xml_decode($port->ContainerPort),
'Default' => xml_decode($port->HostPort),
'Value' => xml_decode($port->HostPort),
'Mode' => xml_decode($port->Protocol) ? xml_decode($port->Protocol) : "tcp",
'Description' => '',
'Type' => 'Port',
'Display' => 'always',
'Required' => 'true',
'Mask' => 'false'
];
}
}
if (isset($xml->Data->Volume)) {
$volNum = 0;
foreach ($xml->Data->Volume as $vol) {
if (empty(xml_decode($vol->ContainerDir))) continue;
$volNum += 1;
$out['Config'][] = [
'Name' => "Host Path {$volNum}",
'Target' => xml_decode($vol->ContainerDir),
'Default' => xml_decode($vol->HostDir),
'Value' => xml_decode($vol->HostDir),
'Mode' => xml_decode($vol->Mode) ? xml_decode($vol->Mode) : "rw",
'Description' => '',
'Type' => 'Path',
'Display' => 'always',
'Required' => 'true',
'Mask' => 'false'
];
}
}
if (isset($xml->Environment->Variable)) {
$varNum = 0;
foreach ($xml->Environment->Variable as $varitem) {
if (empty(xml_decode($varitem->Name))) continue;
$varNum += 1;
$out['Config'][] = [
'Name' => "Key {$varNum}",
'Target' => xml_decode($varitem->Name),
'Default' => xml_decode($varitem->Value),
'Value' => xml_decode($varitem->Value),
'Mode' => '',
'Description' => '',
'Type' => 'Variable',
'Display' => 'always',
'Required' => 'false',
'Mask' => 'false'
];
}
}
if (isset($xml->Labels->Variable)) {
$varNum = 0;
foreach ($xml->Labels->Variable as $varitem) {
if (empty(xml_decode($varitem->Name))) continue;
$varNum += 1;
$out['Config'][] = [
'Name' => "Label {$varNum}",
'Target' => xml_decode($varitem->Name),
'Default' => xml_decode($varitem->Value),
'Value' => xml_decode($varitem->Value),
'Mode' => '',
'Description' => '',
'Type' => 'Label',
'Display' => 'always',
'Required' => 'false',
'Mask' => 'false'
];
}
}
}
xmlSecurity($out);
return $out;
}
function xmlSecurity(&$template) {
foreach ($template as &$element) {
if (is_array($element)) {
xmlSecurity($element);
} else {
if (is_string($element)) {
$tempElement = htmlspecialchars_decode($element);
$tempElement = str_replace("[","<",$tempElement);
$tempElement = str_replace("]",">",$tempElement);
if (preg_match('#<script(.*?)>(.*?)</script>#is',$tempElement) || preg_match('#<iframe(.*?)>(.*?)</iframe>#is',$tempElement) || (stripos($tempElement,"<link") !== false) ) {
$element = "REMOVED";
}
}
}
}
}
function xmlToCommand($xml, $create_paths=false) {
global $docroot, $var, $cfg, $driver;
$xml = xmlToVar($xml);
$cmdName = strlen($xml['Name']) ? '--name='.escapeshellarg($xml['Name']) : '';
$cmdPrivileged = strtolower($xml['Privileged'])=='true' ? '--privileged=true' : '';
$cmdNetwork = preg_match('/\-\-net(work)?=/',$xml['ExtraParams']) ? "" : '--net='.escapeshellarg(strtolower($xml['Network']));
$cmdMyIP = '';
foreach (explode(' ',str_replace(',',' ',$xml['MyIP'])) as $myIP) if ($myIP) $cmdMyIP .= (strpos($myIP,':')?'--ip6=':'--ip=').escapeshellarg($myIP).' ';
$cmdCPUset = strlen($xml['CPUset']) ? '--cpuset-cpus='.escapeshellarg($xml['CPUset']) : '';
$Volumes = [''];
$Ports = [''];
$Variables = [''];
$Labels = [''];
$Devices = [''];
// Bind Time
$Variables[] = 'TZ="'.$var['timeZone'].'"';
// Add HOST_OS variable
$Variables[] = 'HOST_OS="Unraid"';
// Add HOST_HOSTNAME variable
$Variables[] = 'HOST_HOSTNAME="'.$var['NAME'].'"';
// Add HOST_CONTAINERNAME variable
$Variables[] = 'HOST_CONTAINERNAME="'.$xml['Name'].'"';
// Docker labels for WebUI and Icon
$Labels[] = 'net.unraid.docker.managed=dockerman';
if (strlen($xml['WebUI'])) $Labels[] = 'net.unraid.docker.webui='.escapeshellarg($xml['WebUI']);
if (strlen($xml['Icon'])) $Labels[] = 'net.unraid.docker.icon='.escapeshellarg($xml['Icon']);
foreach ($xml['Config'] as $key => $config) {
$confType = strtolower(strval($config['Type']));
$hostConfig = strlen($config['Value']) ? $config['Value'] : $config['Default'];
$containerConfig = strval($config['Target']);
$Mode = strval($config['Mode']);
if ($confType != "device" && !strlen($containerConfig)) continue;
if ($confType == "path") {
$Volumes[] = escapeshellarg($hostConfig).':'.escapeshellarg($containerConfig).':'.escapeshellarg($Mode);
if (!file_exists($hostConfig) && $create_paths) {
@mkdir($hostConfig, 0777, true);
@chown($hostConfig, 99);
@chgrp($hostConfig, 100);
}
} elseif ($confType == 'port') {
switch ($driver[$xml['Network']]) {
case 'host':
case 'macvlan':
case 'ipvlan':
// Export ports as variable if network is set to host or macvlan or ipvlan
$Variables[] = strtoupper(escapeshellarg($Mode.'_PORT_'.$containerConfig).'='.escapeshellarg($hostConfig));
break;
case 'bridge':
// Export ports as port if network is set to (custom) bridge
$Ports[] = escapeshellarg($hostConfig.':'.$containerConfig.'/'.$Mode);
break;
case 'none':
// No export of ports if network is set to none
}
} elseif ($confType == "label") {
$Labels[] = escapeshellarg($containerConfig).'='.escapeshellarg($hostConfig);
} elseif ($confType == "variable") {
$Variables[] = escapeshellarg($containerConfig).'='.escapeshellarg($hostConfig);
} elseif ($confType == "device") {
$Devices[] = escapeshellarg($hostConfig);
}
}
$logSize = $logFile = '';
if (($cfg['DOCKER_LOG_ROTATION']??'')=='yes') {
$logSize = $cfg['DOCKER_LOG_SIZE'] ?? '10m';
$logSize = "--log-opt max-size='$logSize'";
$logFile = $cfg['DOCKER_LOG_FILES'] ?? '1';
$logFile = "--log-opt max-file='$logFile'";
}
$cmd = sprintf($docroot.'/plugins/dynamix.docker.manager/scripts/docker create %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s',
$cmdName, $cmdNetwork, $cmdMyIP, $cmdCPUset, $logSize, $logFile, $cmdPrivileged, implode(' -e ', $Variables), implode(' -l ', $Labels), implode(' -p ', $Ports), implode(' -v ', $Volumes), implode(' --device=', $Devices), $xml['ExtraParams'], escapeshellarg($xml['Repository']), $xml['PostArgs']);
return [preg_replace('/\s\s+/', ' ', $cmd), $xml['Name'], $xml['Repository']];
}
function stopContainer($name, $t=false, $echo=true) {
global $DockerClient;
$waitID = mt_rand();
if ($echo) {
echo "<p class=\"logLine\" id=\"logBody\"></p>";
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>"._('Stopping container').": ",addslashes(htmlspecialchars($name)),"</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">"._('Please wait')." </span></fieldset>');show_Wait($waitID);</script>\n";
@flush();
}
$retval = $DockerClient->stopContainer($name, $t);
$out = ($retval === true) ? _('Successfully stopped container')." '$name'" : _('Error').": ".$retval;
if ($echo) {
echo "<script>stop_Wait($waitID);addLog('<b>",addslashes(htmlspecialchars($out)),"</b>');</script>\n";
@flush();
}
}
function removeContainer($name, $cache=false, $echo=true) {
global $DockerClient;
$waitID = mt_rand();
if ($echo) {
echo "<p class=\"logLine\" id=\"logBody\"></p>";
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>"._('Removing container').": ",addslashes(htmlspecialchars($name)),"</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">"._('Please wait')." </span></fieldset>');show_Wait($waitID);</script>\n";
@flush();
}
$retval = $DockerClient->removeContainer($name, false, $cache);
$out = ($retval === true) ? _('Successfully removed container')." '$name'" : _('Error').": ".$retval;
if ($echo) {
echo "<script>stop_Wait($waitID);addLog('<b>",addslashes(htmlspecialchars($out)),"</b>');</script>\n";
@flush();
}
}
function removeImage($image, $echo=true) {
global $DockerClient;
$waitID = mt_rand();
if ($echo) {
echo "<p class=\"logLine\" id=\"logBody\"></p>";
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>"._('Removing orphan image').": ",addslashes(htmlspecialchars($image)),"</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">"._('Please wait')." </span></fieldset>');show_Wait($waitID);</script>\n";
@flush();
}
$retval = $DockerClient->removeImage($image);
$out = ($retval === true) ? _('Successfully removed orphan image')." '$image'" : _('Error').": ".$retval;
if ($echo) {
echo "<script>stop_Wait($waitID);addLog('<b>",addslashes(htmlspecialchars($out))."</b>');</script>\n";
@flush();
}
}
function pullImage($name, $image, $echo=true) {
global $DockerClient, $DockerTemplates, $DockerUpdate;
$waitID = mt_rand();
if (!preg_match("/:\S+$/", $image)) $image .= ":latest";
if ($echo) {
echo "<p class=\"logLine\" id=\"logBody\"></p>";
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>"._('Pulling image').": ",addslashes(htmlspecialchars($image)),"</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">"._('Please wait')." </span></fieldset>');show_Wait($waitID);</script>\n";
@flush();
}
$alltotals = [];
$laststatus = [];
$strError = '';
$DockerClient->pullImage($image, function ($line) use (&$alltotals, &$laststatus, &$waitID, &$strError, $image, $DockerClient, $DockerUpdate, $echo) {
$cnt = json_decode($line, true);
$id = $cnt['id'] ?? '';
$status = $cnt['status'] ?? '';
if (isset($cnt['error'])) $strError = $cnt['error'];
if ($waitID !== false) {
if ($echo) {
echo "<script>stop_Wait($waitID);</script>\n";
@flush();
}
$waitID = false;
}
if (empty($status)) return;
if (!empty($id)) {
if (!empty($cnt['progressDetail']) && !empty($cnt['progressDetail']['total'])) {
$alltotals[$id] = $cnt['progressDetail']['total'];
}
if (empty($laststatus[$id])) {
$laststatus[$id] = '';
}
switch ($status) {
case 'Waiting':
// Omit
break;
case 'Downloading':
if ($laststatus[$id] != $status) {
if ($echo) echo "<script>addToID('$id','",addslashes(htmlspecialchars($status)),"');</script>\n";
}
$total = $cnt['progressDetail']['total'];
$current = $cnt['progressDetail']['current'];
if ($total > 0) {
$percentage = round(($current / $total) * 100);
if ($echo) echo "<script>progress('$id',' ",$percentage,"% ",_('of')," ",$DockerClient->formatBytes($total),"');</script>\n";
} else {
// Docker must not know the total download size (http-chunked or something?)
// just show the current download progress without the percentage
$alltotals[$id] = $current;
if ($echo) echo "<script>progress('$id',' ",$DockerClient->formatBytes($current),"');</script>\n";
}
break;
default:
if ($laststatus[$id] == "Downloading") {
if ($echo) echo "<script>progress('$id',' 100% ",_('of')," ",$DockerClient->formatBytes($alltotals[$id]),"');</script>\n";
}
if ($laststatus[$id] != $status) {
if ($echo) echo "<script>addToID('",($id=='latest'?mt_rand():$id),"','",addslashes(htmlspecialchars($status)),"');</script>\n";
}
break;
}
$laststatus[$id] = $status;
} else {
if (strpos($status, 'Status: ') === 0) {
if ($echo) echo "<script>addLog('",addslashes(htmlspecialchars($status)),"');</script>\n";
}
if (strpos($status, 'Digest: ') === 0) {
$DockerUpdate->setUpdateStatus($image, substr($status, 8));
}
}
if ($echo) @flush();
});
if ($echo) {
echo "<script>addLog('<br><b>",_('TOTAL DATA PULLED'),":</b> ",$DockerClient->formatBytes(array_sum($alltotals)),"');</script>\n";
@flush();
}
if (!empty($strError)) {
if ($echo) {
echo "<script>addLog('<br><span class=\"error\"><b>",_('Error'),":</b> ",addslashes(htmlspecialchars($strError)),"</span>');</script>\n";
@flush();
}
return false;
}
return true;
}
function execCommand($command, $echo=true) {
$waitID = mt_rand();
if ($echo) {
[$cmd,$args] = explode(' ',$command,2);
echo '<p class="logLine" id="logBody"></p>';
echo '<script>addLog(\'<fieldset style="margin-top:1px;" class="CMD"><legend>',_('Command execution'),'</legend>';
echo basename($cmd),' ',str_replace(" -","<br>&nbsp;&nbsp;-",addslashes(htmlspecialchars($args))),'<br>';
echo '<span id="wait'.$waitID.'">',_('Please wait').' </span>';
echo '<p class="logLine" id="logBody"></p></fieldset>\');show_Wait('.$waitID.');</script>';
@flush();
}
$proc = popen("$command 2>&1",'r');
while ($out = fgets($proc)) {
$out = preg_replace("%[\t\n\x0B\f\r]+%", '', $out);
if ($echo) {
echo '<script>addLog("',htmlspecialchars($out),'");</script>';
@flush();
}
}
$retval = pclose($proc);
if ($echo) echo '<script>stop_Wait('.$waitID.');</script>';
$out = $retval ? _('The command failed').'.' : _('The command finished successfully').'!';
if ($echo) echo '<script>addLog(\'<br><b>',$out,'</b>\');</script>';
return $retval===0;
}
function getXmlVal($xml, $element, $attr=null, $pos=0) {
$xml = (is_file($xml)) ? simplexml_load_file($xml) : simplexml_load_string($xml);
$element = $xml->xpath("//$element")[$pos];
return isset($element) ? (isset($element[$attr]) ? strval($element[$attr]) : strval($element)) : "";
}
function setXmlVal(&$xml, $value, $el, $attr=null, $pos=0) {
$xml = (is_file($xml)) ? simplexml_load_file($xml) : simplexml_load_string($xml);
$element = $xml->xpath("//$el")[$pos];
if (!isset($element)) $element = $xml->addChild($el);
if ($attr) {
$element[$attr] = $value;
} else {
$element->{0} = $value;
}
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml->asXML());
$xml = $dom->saveXML();
}
function getAllocations() {
global $DockerClient, $host;
$ports = [];
foreach ($DockerClient->getDockerContainers() as $ct) {
$list = $port = [];
$nat = $ip = false;
$list['Name'] = $ct['Name'];
foreach ($ct['Ports'] as $tmp) {
$nat = $tmp['NAT'];
$ip = $tmp['IP'];
$port[] = $tmp['PublicPort'];
}
sort($port);
$ip = $ct['NetworkMode']=='host'||$nat ? $host : ($ip ?: DockerUtil::myIP($ct['Name']) ?: '0.0.0.0');
$list['Port'] = "<span class='net'>{$ct['NetworkMode']}</span><span class='ip'>$ip</span>".(implode(', ',array_unique($port)) ?: '???');
$ports[] = $list;
}
return $ports;
}
function getCurlHandle($url, $method='GET') {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
curl_setopt($ch, CURLOPT_TIMEOUT, 45);
curl_setopt($ch, CURLOPT_ENCODING, "");
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_REFERER, "");
if ($method === 'HEAD') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD');
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
}
return $ch;
}
?>
@@ -0,0 +1,44 @@
<?PHP
/* Copyright 2005-2018, Lime Technology
* Copyright 2012-2018, 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.
*/
?>
<?
function ports_only($key) {
return preg_match('/_(ETH|BR|BOND)[0-9]/',$key);
}
$ports = array_filter($_POST,'ports_only',ARRAY_FILTER_USE_KEY);
$purge = [];
foreach ($ports as $port => $val) {
$port = explode('_',$port,3)[2];
if (!in_array($port,$purge)) $purge[] = $port;
}
foreach ($purge as $port) {
switch (substr($port,0,2)) {
case 'ET':
$A1 = str_replace('ETH','BR',$port);
$A2 = str_replace('ETH','BOND',$port);
break;
case 'BR':
$A1 = str_replace('BR','ETH',$port);
$A2 = str_replace('BR','BOND',$port);
break;
case 'BO':
$A1 = str_replace('BOND','BR',$port);
$A2 = str_replace('BOND','ETH',$port);
break;
}
unset($keys["DOCKER_AUTO_$A1"], $keys["DOCKER_AUTO_$A2"]);
unset($keys["DOCKER_DHCP_$A1"], $keys["DOCKER_DHCP6_$A1"], $keys["DOCKER_DHCP_$A2"], $keys["DOCKER_DHCP6_$A2"]);
unset($keys["DOCKER_SUBNET_$A1"], $keys["DOCKER_SUBNET6_$A1"], $keys["DOCKER_SUBNET_$A2"], $keys["DOCKER_SUBNET6_$A2"]);
unset($keys["DOCKER_GATEWAY_$A1"], $keys["DOCKER_GATEWAY6_$A1"], $keys["DOCKER_GATEWAY_$A2"], $keys["DOCKER_GATEWAY6_$A2"]);
unset($keys["DOCKER_RANGE_$A1"], $keys["DOCKER_RANGE6_$A1"], $keys["DOCKER_RANGE_$A2"], $keys["DOCKER_RANGE6_$A2"]);
}
?>
@@ -0,0 +1,69 @@
<?PHP
/* Copyright 2005-2021, Lime Technology
* Copyright 2014-2021, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* Copyright 2012-2021, 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 = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
$autostart_file = $dockerManPaths['autostart-file'];
$template_repos = $dockerManPaths['template-repos'];
$user_prefs = $dockerManPaths['user-prefs'];
switch ($_POST['action']) {
case 'autostart':
// update container autostart setting
$container = urldecode(($_POST['container']));
$wait = $_POST['wait'];
$item = rtrim("$container $wait");
$autostart = @file($autostart_file, FILE_IGNORE_NEW_LINES) ?: [];
$key = array_search($item, $autostart);
if ($_POST['auto']=='true') {
if ($key===false) $autostart[] = $item;
} else {
unset($autostart[$key]);
}
if ($autostart) {
if (file_exists($user_prefs)) {
$prefs = parse_ini_file($user_prefs); $sort = [];
foreach ($autostart as $ct) $sort[] = array_search(var_split($ct),$prefs) ?? 999;
array_multisort($sort,$autostart);
} else {
natcasesort($autostart);
}
file_put_contents($autostart_file, implode("\n", $autostart)."\n");
} else @unlink($autostart_file);
break;
case 'wait':
// update wait period used after container autostart
$container = urldecode(($_POST['container']));
$wait = $_POST['wait'];
$item = rtrim("$container $wait");
$autostart = file($autostart_file, FILE_IGNORE_NEW_LINES) ?: [];
$names = array_map('var_split', $autostart);
$autostart[array_search($container,$names)] = $item;
file_put_contents($autostart_file, implode("\n", $autostart)."\n");
break;
case 'templates':
// update template
readfile("$docroot/update.htm");
file_put_contents($template_repos, $_POST['template_repos']);
$DockerTemplates = new DockerTemplates();
$DockerTemplates->downloadTemplates();
break;
case 'exist':
// docker file or folder exists?
$file = $_POST['name'];
if (substr($file,0,5)=='/mnt/') echo file_exists($file) ? 0 : 1;
break;
}
?>
@@ -0,0 +1,45 @@
<?PHP
/* Copyright 2005-2018, Lime Technology
* Copyright 2015-2018, Derek Macias, Eric Schultz, Jon Panozzo.
* Copyright 2012-2018, 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 = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
$autostart_file = $dockerManPaths['autostart-file'];
$user_prefs = $dockerManPaths['user-prefs'];
if (isset($_POST['reset'])) {
@unlink($user_prefs);
if (file_exists($autostart_file)) {
$allAutoStart = file($autostart_file, FILE_IGNORE_NEW_LINES);
natcasesort($allAutoStart);
file_put_contents($autostart_file, implode(PHP_EOL, $allAutoStart).PHP_EOL);
}
} else {
$names = explode(';',$_POST['names']);
$index = explode(';',$_POST['index']);
$save = []; $i = 0;
foreach ($names as $name) if ($name) $save[] = $index[$i++]."=\"".$name."\""; else $i++;
file_put_contents($user_prefs, implode("\n",$save)."\n");
// sort containers for start-up
if (file_exists($autostart_file)) {
$prefs = parse_ini_file($user_prefs); $sort = [];
$allAutoStart = file($autostart_file, FILE_IGNORE_NEW_LINES);
foreach ($allAutoStart as $ct) $sort[] = array_search(explode(' ',$ct)[0],$prefs) ?? 999;
array_multisort($sort,SORT_NUMERIC,$allAutoStart);
file_put_contents($autostart_file, implode(PHP_EOL, $allAutoStart).PHP_EOL);
}
}
?>
@@ -0,0 +1,155 @@
var pathNum = 2;
var portNum = 0;
var varNum = 0;
var currentPath = "/mnt/";
if (!String.prototype.format) {
String.prototype.format = function() {
var args = arguments;
return this.replace(/{(\d+)}/g, function(match, number) {
return typeof args[number] != 'undefined' ? args[number] : match;
});
};
}
function rmTemplate(tmpl) {
var name = tmpl.split(/[\/]+/).pop();
swal({title:"Are you sure?",text:"Remove template: "+name,type:"warning",html:true,showCancelButton:true},function(){$("#rmTemplate").val(tmpl);$("#formTemplate").submit();});
}
function toggleBrowser(N) {
var el = $('#fileTree' + N);
if (el.is(':visible')) {
hideBrowser(N);
} else {
$( el ).fileTree({
root: currentPath,
filter: 'HIDE_FILES_FILTER'
},
function(file) {},
function(folder) {
$("#hostPath" + N).val(folder);
});
$( el ).slideDown('fast');
}
}
function hideBrowser(N) {
$("#fileTree" + N).slideUp('fast', function () {
$(this).html("");
});
}
function addPort(frm) {
portNum++;
var hostPort = $("#hostPort1");
var containerPort = $("#containerPort1");
var portProtocol = $("#portProtocol1");
var select = "";
if (portProtocol.val() == "udp"){
select = "selected";
}
var row = [
'<tr id="portNum{0}" style="display: none;">',
'<td>',
'<input type="number" min="1" max="65535" name="containerPort[]" value="{2}" class="textPort" title="Set the port your app uses inside the container.">',
'</td>',
'<td>',
'<input type="number" min="1" max="65535" name="hostPort[]" value="{1}" class="textPort" title="Set the port you use to interact with the app.">',
'</td>',
'<td>',
'<select name="portProtocol[]">',
'<option value="tcp">tcp</option>',
'<option value="udp" {3}>udp</option>',
'</select>',
'</td>',
'<td>',
'<input type="button" value="Remove" onclick="removePort({0});">',
'</td>',
'</tr>'
].join('').format(portNum, hostPort.val(), containerPort.val(), select);
$(row).appendTo('#portRows').fadeIn("fast");
hostPort.val('');
containerPort.val('');
portProtocol.val('tcp');
}
function removePort(rnum) {
$('#portNum' + rnum).fadeOut("fast", function() { $(this).remove(); });
}
function addPath(frm) {
pathNum++;
var hostPath = $("#hostPath1");
var containerPath = $("#containerPath1");
var hostWritable = $("#hostWritable1");
var select = "";
if (hostWritable.val() == "ro"){
select = "selected";
}
var row = [
'<tr id="pathNum{0}" style="display: none;">',
'<td>',
'<input type="text" name="containerPath[]" value="{2}" class="textPath" onclick="hideBrowser({0});" title="The directory your app uses inside the container. Ex: /config">',
'</td>',
'<td>',
'<input type="text" id="hostPath{0}" name="hostPath[]" value="{1}" class="textPath" onclick="toggleBrowser({0});" title="The directory in your array the app have access to. Ex: /mnt/user/Movies"/>',
'<div id="fileTree{0}" class="fileTree"></div>',
'</td>',
'<td>',
'<select name="hostWritable[]">',
'<option value="rw">Read/Write</option>',
'<option value="ro" {3}>Read Only</option>',
'</select>',
'</td>',
'<td>',
'<input type="button" value="Remove" onclick="removePath({0});"></td></tr>',
'</td>',
'</tr>'
].join('').format(pathNum, hostPath.val(), containerPath.val(), select);
$(row).appendTo('#pathRows tbody').fadeIn("fast");
hostPath.val('');
containerPath.val('');
hostWritable.val('rw');
}
function removePath(rnum) {
$('#pathNum' + rnum).fadeOut("fast", function() { $(this).remove(); });
}
function addEnv(frm) {
varNum++;
var VariableName = $("#VariableName1");
var VariableValue = $("#VariableValue1");
var row = [
'<tr id="varNum{0}" style="display: none;">',
'<td>',
'<input type="text" name="VariableName[]" value="{1}" class="textEnv">',
'</td>',
'<td>',
'<input type="text" name="VariableValue[]" value="{2}" class="textEnv">',
'<input type="button" value="Remove" onclick="removeEnv({0});">',
'</td>',
'</tr>'
].join('').format(varNum, VariableName.val(), VariableValue.val());
$(row).appendTo('#envRows tbody').fadeIn("fast");
VariableName.val('');
VariableValue.val('');
}
function removeEnv(rnum) {
$('#varNum' + rnum).fadeOut("fast", function() { $(this).remove(); });
}
function toggleMode(){
$("#toggleMode").toggleClass("fa-toggle-off fa-toggle-on");
$(".additionalFields").slideToggle();
}
@@ -0,0 +1,191 @@
var eventURL = '/plugins/dynamix.docker.manager/include/Events.php';
function addDockerContainerContext(container, image, template, started, paused, update, autostart, webui, shell, id, Support, Project, Registry, donateLink, ReadMe) {
var opts = [];
context.settings({right:false,above:false});
if (started && !paused) {
if (webui !== '' && webui != '#') opts.push({text:_('WebUI'), icon:'fa-globe', href:webui, target:'_blank'});
opts.push({text:_('Console'), icon:'fa-terminal', action:function(e){e.preventDefault(); openTerminal('docker',container,shell);}});
opts.push({divider:true});
}
if (update==1) {
opts.push({text:_('Update'), icon:'fa-cloud-download', action:function(e){e.preventDefault(); updateContainer(container);}});
opts.push({divider:true});
}
if (started) {
if (paused) {
opts.push({text:_('Resume'), icon:'fa-play', action:function(e){e.preventDefault(); eventControl({action:'resume', container:id}, 'loadlist');}});
} else {
opts.push({text:_('Stop'), icon:'fa-stop', action:function(e){e.preventDefault(); eventControl({action:'stop', container:id}, 'loadlist');}});
opts.push({text:_('Pause'), icon:'fa-pause', action:function(e){e.preventDefault(); eventControl({action:'pause', container:id}, 'loadlist');}});
}
opts.push({text:_('Restart'), icon:'fa-refresh', action:function(e){e.preventDefault(); eventControl({action:'restart', container:id}, 'loadlist');}});
} else {
opts.push({text:_('Start'), icon:'fa-play', action:function(e){e.preventDefault(); eventControl({action:'start', container:id}, 'loadlist');}});
}
opts.push({divider:true});
opts.push({text:_('Logs'), icon:'fa-navicon', action:function(e){e.preventDefault(); openTerminal('docker',container,'.log');}});
if (template) {
opts.push({text:_('Edit'), icon:'fa-wrench', action:function(e){e.preventDefault(); editContainer(container, template);}});
}
opts.push({text:_('Remove'), icon:'fa-trash', action:function(e){e.preventDefault(); rmContainer(container, image, id);}});
if (ReadMe||Project||Support||Registry) {
opts.push({divider:true});
}
if (ReadMe) {
opts.push({text:_('Read Me First'), icon:'fa-book', href:ReadMe, target:'_blank'});
}
if (Project) {
opts.push({text:_('Project Page'), icon:'fa-life-ring', href:Project, target:'_blank'});
}
if (Support) {
opts.push({text:_('Support'), icon:'fa-question', href:Support, target:'_blank'});
}
if (Registry) {
opts.push({text:_('More Info'),icon:'fa-info-circle', href:Registry, target:'_blank'});
}
if (donateLink) {
opts.push({divider:true});
opts.push({text:_('Donate'),icon:'fa-external-link', href:donateLink,target:'_blank'});
}
context.attach('#'+id, opts);
}
function addDockerImageContext(image, imageTag) {
var opts = [];
opts.push({text:_('Remove'), icon:'fa-trash', action:function(e){e.preventDefault(); rmImage(image, imageTag);}});
context.attach('#'+image, opts);
}
function popupWithIframe(title, cmd, reload, func) {
pauseEvents();
$('#iframe-popup').html('<iframe id="myIframe" frameborder="0" scrolling="yes" width="100%" height="99%"></iframe>');
$('#iframe-popup').dialog({
autoOpen:true,
title:title,
draggable:true,
width: Math.min(Math.max(window.innerWidth/2,900),1600),
height: Math.max(window.innerHeight*3/5,600),
resizable:true,
modal:true,
show:{effect:'fade', duration:250},
hide:{effect:'fade', duration:250},
open:function(ev, ui) {
$('#myIframe').attr('src', cmd);
},
close:function(event, ui) {
if (reload && !$('#myIframe').contents().find('#canvas').length) {
if (func) setTimeout(func+'()',0); else location = window.location.href;
} else {
resumeEvents();
}
}
});
$(".ui-dialog .ui-dialog-titlebar").addClass('menu');
$('.ui-dialog .ui-dialog-titlebar-close').text('X').prop('title',_('Close'));
$(".ui-dialog .ui-dialog-title").css({'text-align':'center','width':'100%'});
$(".ui-dialog .ui-dialog-content").css({'padding-top':'15px','vertical-align':'bottom'});
}
function execUpContainer(container) {
var title = _('Updating the container')+': '+container;
var cmd = '/plugins/dynamix.docker.manager/include/CreateDocker.php?updateContainer=true&ct[]='+encodeURIComponent(container);
popupWithIframe(title, cmd, true, 'loadlist');
}
function addContainer() {
var path = location.pathname;
var x = path.indexOf('?');
if (x!=-1) path = path.substring(0,x);
location = path+'/AddContainer';
}
function editContainer(container, template) {
var path = location.pathname;
var x = path.indexOf('?');
if (x!=-1) path = path.substring(0, x);
location = path+'/UpdateContainer?xmlTemplate=edit:'+template;
}
function updateContainer(container) {
swal({
title:_('Are you sure?'),text:_('Update container')+': '+container, type:'warning',html:true,showCancelButton:true,closeOnConfirm:false,confirmButtonText:_('Yes, update it!'),cancelButtonText:_('Cancel')
},function(){
openDocker('update_container '+encodeURIComponent(container),_('Updating the container'),'','loadlist');
});
}
function rmContainer(container, image, id) {
var body = _('Remove container')+': '+container+'<br><br><label><input id="removeimagechk" type="checkbox" checked style="display:inline;width:unset;height:unset;margin-top:unset;margin-bottom:unset">'+_('also remove image')+'</label>';
$('input[type=button]').prop('disabled',true);
swal({
title:_('Are you sure?'),text:body,type:'warning',html:true,showCancelButton:true,confirmButtonText:_('Yes, delete it!'),cancelButtonText:_('Cancel'),showLoaderOnConfirm:true
},function(c){
if (!c) {setTimeout(loadlist); return;}
$('div.spinner.fixed').show('slow');
if ($('#removeimagechk').prop('checked')) {
eventControl({action:'remove_all', container:id, name:container, image:image},'loadlist');
} else {
eventControl({action:'remove_container', container:id, name:container},'loadlist');
}
});
}
function rmImage(image, imageName) {
var body = _('Remove image')+': '+$('<textarea />').html(imageName).text();
$('input[type=button]').prop('disabled',true);
swal({
title:_('Are you sure?'),text:body,type:'warning',html:true,showCancelButton:true,confirmButtonText:_('Yes, delete it!'),cancelButtonText:_('Cancel'),showLoaderOnConfirm:true
},function(c){
if (!c) {setTimeout(loadlist,0); return;}
$('div.spinner.fixed').show('slow');
eventControl({action:'remove_image', image:image},'loadlist');
});
}
function eventControl(params, spin) {
if (spin) $('#'+params['container']).parent().find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
$.post(eventURL, params, function(data) {
$('div.spinner.fixed').hide('slow');
if (data.success === true) {
if (spin) setTimeout(spin+'()',500); else location=window.location.href;
} else {
setTimeout(function(){
swal({
title:_('Execution error'),text:data.success,type:'error',html:true,confirmButtonText:_('Ok')
},function(){
if (spin) setTimeout(spin+'()',500); else location=window.location.href;
});
},100);
}
},'json');
}
function startAll() {
$('input[type=button]').prop('disabled',true);
for (var i=0,ct; ct=docker[i]; i++) if (ct.state==0) $('#'+ct.id).parent().find('i').removeClass('fa-square').addClass('fa-refresh fa-spin');
$.post('/plugins/dynamix.docker.manager/include/ContainerManager.php',{action:'start'},function(){loadlist();});
}
function stopAll() {
$('input[type=button]').prop('disabled',true);
for (var i=0,ct; ct=docker[i]; i++) if (ct.state==1) $('#'+ct.id).parent().find('i').removeClass('fa-play fa-pause').addClass('fa-refresh fa-spin');
$.post('/plugins/dynamix.docker.manager/include/ContainerManager.php',{action:'stop'},function(){loadlist();});
}
function pauseAll() {
$('input[type=button]').prop('disabled',true);
for (var i=0,ct; ct=docker[i]; i++) if (ct.state==1 && ct.pause==0) $('#'+ct.id).parent().find('i').removeClass('fa-play').addClass('fa-refresh fa-spin');
$.post('/plugins/dynamix.docker.manager/include/ContainerManager.php',{action:'pause'},function(){loadlist();});
}
function resumeAll() {
$('input[type=button]').prop('disabled',true);
for (var i=0,ct; ct=docker[i]; i++) if (ct.state==1 && ct.pause==1) $('#'+ct.id).parent().find('i').removeClass('fa-pause').addClass('fa-refresh fa-spin');
$.post('/plugins/dynamix.docker.manager/include/ContainerManager.php',{action:'unpause'},function(){loadlist();});
}
function checkAll() {
$('input[type=button]').prop('disabled',true);
$('.updatecolumn').html('<span style="color:#267CA8"><i class="fa fa-refresh fa-spin"></i> '+_('checking')+'...</span>');
$.post('/plugins/dynamix.docker.manager/include/DockerUpdate.php',{},function(){loadlist();});
}
function updateAll() {
$('input[type=button]').prop('disabled',true);
var ct = [];
for (var i=0,d; d=docker[i]; i++) if (d.update==1) ct.push(encodeURIComponent(d.name));
openDocker('update_container '+ct.join('*'),_('Updating all Containers'),'','loadlist');
}
function rebuildAll() {
$('input[type=button]').prop('disabled',true);
$('div.spinner.fixed').show('slow');
var ct = [];
for (var i=0,d; d=docker[i]; i++) if (d.update==2) ct.push(encodeURIComponent(d.name));
$.get('/plugins/dynamix.docker.manager/include/CreateDocker.php',{updateContainer:true,mute:true,ct},function(){loadlist();});
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,548 @@
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">
<meta name="format-detection" content="telephone=no">
<meta name="viewport" content="width=1300">
<meta name="robots" content="noindex, nofollow">
<meta name="referrer" content="same-origin">
<style>
/************************
/
/ Fonts
/
/************************/
@font-face{font-family:clear-sans;font-weight:normal;font-style:normal; src:url('/webGui/styles/clear-sans.woff?v=20220513') format('woff')}
@font-face{font-family:clear-sans;font-weight:bold;font-style:normal; src:url('/webGui/styles/clear-sans-bold.woff?v=20220513') format('woff')}
@font-face{font-family:clear-sans;font-weight:normal;font-style:italic; src:url('/webGui/styles/clear-sans-italic.woff?v=20220513') format('woff')}
@font-face{font-family:clear-sans;font-weight:bold;font-style:italic; src:url('/webGui/styles/clear-sans-bold-italic.woff?v=20220513') format('woff')}
@font-face{font-family:bitstream;font-weight:normal;font-style:normal; src:url('/webGui/styles/bitstream.woff?v=20220513') format('woff')}
@font-face{font-family:bitstream;font-weight:bold;font-style:normal; src:url('/webGui/styles/bitstream-bold.woff?v=20220513') format('woff')}
@font-face{font-family:bitstream;font-weight:normal;font-style:italic; src:url('/webGui/styles/bitstream-italic.woff?v=20220513') format('woff')}
@font-face{font-family:bitstream;font-weight:bold;font-style:italic; src:url('/webGui/styles/bitstream-bold-italic.woff?v=20220513') format('woff')}
html{font-family:clear-sans;height:100%}
body{font-size:1.3rem;padding:0;margin:0;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
.logLine{color:#1c1c1c;font-family:bitstream;font-size:1.2rem;line-height:1.6rem;margin:0 8px;padding:0;text-align:left}
.logLine.spacing{margin:10px}
input[type=button],input[type=reset],input[type=submit],button,button[type=button],a.button{font-family:clear-sans;font-size:1.1rem;text-transform:uppercase;margin:0;padding:9px 18px;text-decoration:none;white-space:nowrap;cursor:pointer;outline:none;border-radius:4px;border:0;color:#f15a2c;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%}
input:hover[type=button],input:hover[type=reset],input:hover[type=submit],button:hover,button:hover[type=button],a.button:hover{color:#f2f2f2;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f));background:linear-gradient(90deg,#e22828 0,#ff8c2f)}
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%}
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%}
span.system{color:#00529B;background-color:#BDE5F8;display:block;width:100%}
span.array{color:#4F8A10;background-color:#DFF2BF;display:block;width:100%}
span.login{color:#D63301;background-color:#FFCCBA;display:block;width:100%}
span.label{padding:4px 8px;margin-right:10px;border-radius:4px;display:inline;width:auto}
legend{font-size:1.1rem;font-weight:bold}
#content{margin:10;padding:0}
</style>
<script>
/*
This script block has the following license.
Original: https://github.com/drudru/ansi_up Version: 5.0.1
Modified by: https://github.com/nysos3 for use in unRaid
(The MIT License)
Copyright (c) 2011 Dru Nelson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
return cooked;
};
var PacketKind;
(function (PacketKind) {
PacketKind[PacketKind["EOS"] = 0] = "EOS";
PacketKind[PacketKind["Text"] = 1] = "Text";
PacketKind[PacketKind["Incomplete"] = 2] = "Incomplete";
PacketKind[PacketKind["ESC"] = 3] = "ESC";
PacketKind[PacketKind["Unknown"] = 4] = "Unknown";
PacketKind[PacketKind["SGR"] = 5] = "SGR";
PacketKind[PacketKind["OSCURL"] = 6] = "OSCURL";
})(PacketKind || (PacketKind = {}));
var AnsiUp = (function() {
function AnsiUp() {
this.VERSION = "5.0.1";
this.setup_palettes();
this._use_classes = false;
this.bold = false;
this.fg = this.bg = null;
this._buffer = '';
this._url_whitelist = { 'http': 1, 'https': 1 };
}
Object.defineProperty(AnsiUp.prototype, "use_classes", {
get: function() {
return this._use_classes;
},
set: function(arg) {
this._use_classes = arg;
},
enumerable: false,
configurable: true
});
Object.defineProperty(AnsiUp.prototype, "url_whitelist", {
get: function() {
return this._url_whitelist;
},
set: function(arg) {
this._url_whitelist = arg;
},
enumerable: false,
configurable: true
});
AnsiUp.prototype.setup_palettes = function() {
var _this = this;
this.ansi_colors = [
[
{ rgb: [0, 0, 0], class_name: "ansi-black" },
{ rgb: [187, 0, 0], class_name: "ansi-red" },
{ rgb: [0, 187, 0], class_name: "ansi-green" },
{ rgb: [187, 187, 0], class_name: "ansi-yellow" },
{ rgb: [0, 0, 187], class_name: "ansi-blue" },
{ rgb: [187, 0, 187], class_name: "ansi-magenta" },
{ rgb: [0, 187, 187], class_name: "ansi-cyan" },
{ rgb: [255, 255, 255], class_name: "ansi-white" }
],
[
{ rgb: [85, 85, 85], class_name: "ansi-bright-black" },
{ rgb: [255, 85, 85], class_name: "ansi-bright-red" },
{ rgb: [0, 255, 0], class_name: "ansi-bright-green" },
{ rgb: [255, 255, 85], class_name: "ansi-bright-yellow" },
{ rgb: [85, 85, 255], class_name: "ansi-bright-blue" },
{ rgb: [255, 85, 255], class_name: "ansi-bright-magenta" },
{ rgb: [85, 255, 255], class_name: "ansi-bright-cyan" },
{ rgb: [255, 255, 255], class_name: "ansi-bright-white" }
]
];
this.palette_256 = [];
this.ansi_colors.forEach(function(palette) {
palette.forEach(function(rec) {
_this.palette_256.push(rec);
});
});
var levels = [0, 95, 135, 175, 215, 255];
for (var r = 0; r < 6; ++r) {
for (var g = 0; g < 6; ++g) {
for (var b = 0; b < 6; ++b) {
var col = { rgb: [levels[r], levels[g], levels[b]], class_name: 'truecolor' };
this.palette_256.push(col);
}
}
}
var grey_level = 8;
for (var i = 0; i < 24; ++i, grey_level += 10) {
var gry = { rgb: [grey_level, grey_level, grey_level], class_name: 'truecolor' };
this.palette_256.push(gry);
}
};
AnsiUp.prototype.escape_txt_for_html = function(txt) {
return txt.replace(/[&<>"']/gm, function(str) {
if (str === "&")
return "&amp;";
if (str === "<")
return "&lt;";
if (str === ">")
return "&gt;";
if (str === "\"")
return "&quot;";
if (str === "'")
return "&#x27;";
});
};
AnsiUp.prototype.append_buffer = function(txt) {
var str = this._buffer + txt;
this._buffer = str;
};
AnsiUp.prototype.get_next_packet = function() {
var pkt = {
kind: PacketKind.EOS,
text: '',
url: ''
};
var len = this._buffer.length;
if (len == 0)
return pkt;
var pos = this._buffer.indexOf("\x1B");
if (pos == -1) {
pkt.kind = PacketKind.Text;
pkt.text = this._buffer;
this._buffer = '';
return pkt;
}
if (pos > 0) {
pkt.kind = PacketKind.Text;
pkt.text = this._buffer.slice(0, pos);
this._buffer = this._buffer.slice(pos);
return pkt;
}
if (pos == 0) {
if (len == 1) {
pkt.kind = PacketKind.Incomplete;
return pkt;
}
var next_char = this._buffer.charAt(1);
if ((next_char != '[') && (next_char != ']')) {
pkt.kind = PacketKind.ESC;
pkt.text = this._buffer.slice(0, 1);
this._buffer = this._buffer.slice(1);
return pkt;
}
if (next_char == '[') {
if (!this._csi_regex) {
this._csi_regex = rgx(__makeTemplateObject(["\n ^ # beginning of line\n #\n # First attempt\n (?: # legal sequence\n \u001B[ # CSI\n ([<-?]?) # private-mode char\n ([d;]*) # any digits or semicolons\n ([ -/]? # an intermediate modifier\n [@-~]) # the command\n )\n | # alternate (second attempt)\n (?: # illegal sequence\n \u001B[ # CSI\n [ -~]* # anything legal\n ([\0-\u001F:]) # anything illegal\n )\n "], ["\n ^ # beginning of line\n #\n # First attempt\n (?: # legal sequence\n \\x1b\\[ # CSI\n ([\\x3c-\\x3f]?) # private-mode char\n ([\\d;]*) # any digits or semicolons\n ([\\x20-\\x2f]? # an intermediate modifier\n [\\x40-\\x7e]) # the command\n )\n | # alternate (second attempt)\n (?: # illegal sequence\n \\x1b\\[ # CSI\n [\\x20-\\x7e]* # anything legal\n ([\\x00-\\x1f:]) # anything illegal\n )\n "]));
}
var match = this._buffer.match(this._csi_regex);
if (match === null) {
pkt.kind = PacketKind.Incomplete;
return pkt;
}
if (match[4]) {
pkt.kind = PacketKind.ESC;
pkt.text = this._buffer.slice(0, 1);
this._buffer = this._buffer.slice(1);
return pkt;
}
if ((match[1] != '') || (match[3] != 'm'))
pkt.kind = PacketKind.Unknown;
else
pkt.kind = PacketKind.SGR;
pkt.text = match[2];
var rpos = match[0].length;
this._buffer = this._buffer.slice(rpos);
return pkt;
}
if (next_char == ']') {
if (len < 4) {
pkt.kind = PacketKind.Incomplete;
return pkt;
}
if ((this._buffer.charAt(2) != '8')
|| (this._buffer.charAt(3) != ';')) {
pkt.kind = PacketKind.ESC;
pkt.text = this._buffer.slice(0, 1);
this._buffer = this._buffer.slice(1);
return pkt;
}
if (!this._osc_st) {
this._osc_st = rgxG(__makeTemplateObject(["\n (?: # legal sequence\n (\u001B\\) # ESC | # alternate\n (\u0007) # BEL (what xterm did)\n )\n | # alternate (second attempt)\n ( # illegal sequence\n [\0-\u0006] # anything illegal\n | # alternate\n [\b-\u001A] # anything illegal\n | # alternate\n [\u001C-\u001F] # anything illegal\n )\n "], ["\n (?: # legal sequence\n (\\x1b\\\\) # ESC \\\n | # alternate\n (\\x07) # BEL (what xterm did)\n )\n | # alternate (second attempt)\n ( # illegal sequence\n [\\x00-\\x06] # anything illegal\n | # alternate\n [\\x08-\\x1a] # anything illegal\n | # alternate\n [\\x1c-\\x1f] # anything illegal\n )\n "]));
}
this._osc_st.lastIndex = 0;
{
var match_1 = this._osc_st.exec(this._buffer);
if (match_1 === null) {
pkt.kind = PacketKind.Incomplete;
return pkt;
}
if (match_1[3]) {
pkt.kind = PacketKind.ESC;
pkt.text = this._buffer.slice(0, 1);
this._buffer = this._buffer.slice(1);
return pkt;
}
}
{
var match_2 = this._osc_st.exec(this._buffer);
if (match_2 === null) {
pkt.kind = PacketKind.Incomplete;
return pkt;
}
if (match_2[3]) {
pkt.kind = PacketKind.ESC;
pkt.text = this._buffer.slice(0, 1);
this._buffer = this._buffer.slice(1);
return pkt;
}
}
if (!this._osc_regex) {
this._osc_regex = rgx(__makeTemplateObject(["\n ^ # beginning of line\n #\n \u001B]8; # OSC Hyperlink\n [ -:<-~]* # params (excluding ;)\n ; # end of params\n ([!-~]{0,512}) # URL capture\n (?: # ST\n (?:\u001B\\) # ESC | # alternate\n (?:\u0007) # BEL (what xterm did)\n )\n ([ -~]+) # TEXT capture\n \u001B]8;; # OSC Hyperlink End\n (?: # ST\n (?:\u001B\\) # ESC | # alternate\n (?:\u0007) # BEL (what xterm did)\n )\n "], ["\n ^ # beginning of line\n #\n \\x1b\\]8; # OSC Hyperlink\n [\\x20-\\x3a\\x3c-\\x7e]* # params (excluding ;)\n ; # end of params\n ([\\x21-\\x7e]{0,512}) # URL capture\n (?: # ST\n (?:\\x1b\\\\) # ESC \\\n | # alternate\n (?:\\x07) # BEL (what xterm did)\n )\n ([\\x20-\\x7e]+) # TEXT capture\n \\x1b\\]8;; # OSC Hyperlink End\n (?: # ST\n (?:\\x1b\\\\) # ESC \\\n | # alternate\n (?:\\x07) # BEL (what xterm did)\n )\n "]));
}
var match = this._buffer.match(this._osc_regex);
if (match === null) {
pkt.kind = PacketKind.ESC;
pkt.text = this._buffer.slice(0, 1);
this._buffer = this._buffer.slice(1);
return pkt;
}
pkt.kind = PacketKind.OSCURL;
pkt.url = match[1];
pkt.text = match[2];
var rpos = match[0].length;
this._buffer = this._buffer.slice(rpos);
return pkt;
}
}
};
AnsiUp.prototype.ansi_to_html = function(txt) {
this.append_buffer(txt);
var blocks = [];
while (true) {
var packet = this.get_next_packet();
if ((packet.kind == PacketKind.EOS)
|| (packet.kind == PacketKind.Incomplete))
break;
if ((packet.kind == PacketKind.ESC)
|| (packet.kind == PacketKind.Unknown))
continue;
if (packet.kind == PacketKind.Text)
blocks.push(this.transform_to_html(this.with_state(packet)));
else if (packet.kind == PacketKind.SGR)
this.process_ansi(packet);
else if (packet.kind == PacketKind.OSCURL)
blocks.push(this.process_hyperlink(packet));
}
return blocks.join("");
};
AnsiUp.prototype.with_state = function(pkt) {
return { bold: this.bold, fg: this.fg, bg: this.bg, text: pkt.text };
};
AnsiUp.prototype.process_ansi = function(pkt) {
var sgr_cmds = pkt.text.split(';');
while (sgr_cmds.length > 0) {
var sgr_cmd_str = sgr_cmds.shift();
var num = parseInt(sgr_cmd_str, 10);
if (isNaN(num) || num === 0) {
this.fg = this.bg = null;
this.bold = false;
}
else if (num === 1) {
this.bold = true;
}
else if (num === 22) {
this.bold = false;
}
else if (num === 39) {
this.fg = null;
}
else if (num === 49) {
this.bg = null;
}
else if ((num >= 30) && (num < 38)) {
this.fg = this.ansi_colors[0][(num - 30)];
}
else if ((num >= 40) && (num < 48)) {
this.bg = this.ansi_colors[0][(num - 40)];
}
else if ((num >= 90) && (num < 98)) {
this.fg = this.ansi_colors[1][(num - 90)];
}
else if ((num >= 100) && (num < 108)) {
this.bg = this.ansi_colors[1][(num - 100)];
}
else if (num === 38 || num === 48) {
if (sgr_cmds.length > 0) {
var is_foreground = (num === 38);
var mode_cmd = sgr_cmds.shift();
if (mode_cmd === '5' && sgr_cmds.length > 0) {
var palette_index = parseInt(sgr_cmds.shift(), 10);
if (palette_index >= 0 && palette_index <= 255) {
if (is_foreground)
this.fg = this.palette_256[palette_index];
else
this.bg = this.palette_256[palette_index];
}
}
if (mode_cmd === '2' && sgr_cmds.length > 2) {
var r = parseInt(sgr_cmds.shift(), 10);
var g = parseInt(sgr_cmds.shift(), 10);
var b = parseInt(sgr_cmds.shift(), 10);
if ((r >= 0 && r <= 255) && (g >= 0 && g <= 255) && (b >= 0 && b <= 255)) {
var c = { rgb: [r, g, b], class_name: 'truecolor' };
if (is_foreground)
this.fg = c;
else
this.bg = c;
}
}
}
}
}
};
AnsiUp.prototype.transform_to_html = function(fragment) {
var txt = fragment.text;
if (txt.length === 0)
return txt;
// txt = this.escape_txt_for_html(txt);
if (!fragment.bold && fragment.fg === null && fragment.bg === null)
return txt;
var styles = [];
var classes = [];
var fg = fragment.fg;
var bg = fragment.bg;
if (fragment.bold)
styles.push('font-weight:bold');
if (!this._use_classes) {
if (fg)
styles.push("color:rgb(" + fg.rgb.join(',') + ")");
if (bg)
styles.push("background-color:rgb(" + bg.rgb + ")");
}
else {
if (fg) {
if (fg.class_name !== 'truecolor') {
classes.push(fg.class_name + "-fg");
}
else {
styles.push("color:rgb(" + fg.rgb.join(',') + ")");
}
}
if (bg) {
if (bg.class_name !== 'truecolor') {
classes.push(bg.class_name + "-bg");
}
else {
styles.push("background-color:rgb(" + bg.rgb.join(',') + ")");
}
}
}
var class_string = '';
var style_string = '';
if (classes.length)
class_string = " class=\"" + classes.join(' ') + "\"";
if (styles.length)
style_string = " style=\"" + styles.join(';') + "\"";
return "<span" + style_string + class_string + ">" + txt + "</span>";
};
;
AnsiUp.prototype.process_hyperlink = function(pkt) {
var parts = pkt.url.split(':');
if (parts.length < 1)
return '';
if (!this._url_whitelist[parts[0]])
return '';
// var result = "<a href=\"" + this.escape_txt_for_html(pkt.url) + "\">" + this.escape_txt_for_html(pkt.text) + "</a>";
var result = "<a href=\"" + this.escape_txt_for_html(pkt.url) + "\">" + pkt.text + "</a>";
return result;
};
return AnsiUp;
}());
function rgx(tmplObj) {
var subst = [];
for (var _i = 1; _i < arguments.length; _i++) {
subst[_i - 1] = arguments[_i];
}
var regexText = tmplObj.raw[0];
var wsrgx = /^\s+|\s+\n|\s*#[\s\S]*?\n|\n/gm;
var txt2 = regexText.replace(wsrgx, '');
return new RegExp(txt2);
}
function rgxG(tmplObj) {
var subst = [];
for (var _i = 1; _i < arguments.length; _i++) {
subst[_i - 1] = arguments[_i];
}
var regexText = tmplObj.raw[0];
var wsrgx = /^\s+|\s+\n|\s*#[\s\S]*?\n|\n/gm;
var txt2 = regexText.replace(wsrgx, '');
return new RegExp(txt2, 'g');
}
</script>
<script>
var dots = [];
var span = [];
var ansi_up = new AnsiUp;
function show_Wait(id) {
span[id] = document.getElementById("wait" + id);
dots[id] = setInterval(function() {
if (((span[id].innerHTML+='.').match(/\./g)||[]).length > 9) {
span[id].innerHTML = span[id].innerHTML.replace(/\.+$/,'');
}
}, 500);
}
function stop_Wait(id) {
span[id].innerHTML = '';
clearInterval( dots[id] );
}
function addLog(logLine) {
var elms = document.getElementsByClassName('logLine');
logLine = ansi_up.ansi_to_html(logLine);
if (elms.length) {
var el = elms[elms.length-1];
el.innerHTML += logLine + "<br>";
}
window.scrollTo(0, document.body.scrollHeight);
}
function loadLog(container, since) {
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState === 4 && httpRequest.status === 200) {
parseScript(httpRequest.responseText);
}
};
httpRequest.open('GET', location.protocol+'//'+location.host+location.pathname+'?action=log&container='+encodeURIComponent(container)+'&since='+encodeURIComponent(since));
httpRequest.send();
}
function parseScript(_source) {
var source = _source;
var scripts = [];
while(source.indexOf("<script") > -1 || source.indexOf("</script") > -1) {
var s = source.indexOf("<script");
var s_e = source.indexOf(">", s);
var e = source.indexOf("</script", s);
var e_e = source.indexOf(">", e);
scripts.push(source.substring(s_e+1, e));
source = source.substring(0, s) + source.substring(e_e+1);
}
for (var i=0; i<scripts.length; i++) {
try {
eval(scripts[i]);
} catch(ex) {}
}
return source;
}
function progress(id, prog) {
var elms = document.getElementsByClassName(id+'_progress');
if (elms.length) {
elms[elms.length-1].textContent = prog;
}
}
function addToID(id, m) {
var elms = document.getElementById(id);
if (elms === null) {
addLog('<span id=\"'+id+'\">IMAGE ID ['+id+']: <span class=\"content\">'+m+'</span><span class=\"'+id+'_progress\"></span>. </span>');
} else {
var elms_content = elms.getElementsByClassName("content");
if (!elms_content.length || elms_content[elms_content.length-1].textContent != m) {
elms.innerHTML += '<span class=\"content\">'+m+'</span><span class=\"'+id+'_progress\"></span>. ';
}
}
}
</script>
</head>
<body>
<div id="content"><p class="logLine" id="logBody"></p></div>
</body>
</html>
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
while :; do
curl -sfd "$(docker stats --no-stream --format='{{.ID}};{{.CPUPerc}};{{.MemUsage}}')" --unix-socket /var/run/nginx.socket http://localhost/pub/dockerload?buffer_length=0 >/dev/null 2>&1
done
@@ -0,0 +1,80 @@
#!/usr/bin/php -q
<?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 = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
extract(parse_plugin_cfg('dynamix',true));
// add translations
$_SERVER['REQUEST_URI'] = 'docker';
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";
$unit = ['B','kB','MB','GB','TB','PB','EB','ZB','YB'];
$list = [];
function write(...$messages){
$com = curl_init();
curl_setopt_array($com,[
CURLOPT_URL => 'http://localhost/pub/plugins?buffer_length=1',
CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
CURLOPT_POST => 1,
CURLOPT_RETURNTRANSFER => true
]);
foreach ($messages as $message) {
curl_setopt($com, CURLOPT_POSTFIELDS, $message);
curl_exec($com);
}
curl_close($com);
}
function autoscale($value) {
global $unit;
$size = count($unit);
$base = $value ? floor(log($value, 1000)) : 0;
if ($base>=$size) $base = $size-1;
$value /= pow(1000, $base);
$decimals = $base ? ($value>=100 ? 0 : ($value>=10 ? 1 : (round($value*100)%100===0 ? 0 : 2))) : 0;
return number_format($value, $decimals, '.', $value>9999 ? ',' : '').' '.$unit[$base];
}
function align($text, $w=25) {
return $text.str_repeat('&nbsp;',$w-min(strlen($text),$w-1));
}
function gap($text) {
return preg_replace('/([kMGTPEZY]?B)$/'," $1",$text);
}
function byteval($data) {
global $unit;
[$value,$base] = explode(' ',gap($data));
return $value*pow(1000,array_search($base,$unit));
}
exec("docker ps -sa --format='{{.Names}}|{{.Size}}'",$container);
foreach ($container as $ct) {
[$name,$size] = explode('|',$ct);
[$writable,$dummy,$total] = explode(' ',str_replace(['(',')'],'',$size));
$list[] = ['name' => $name, 'total' => byteval($total), 'writable' => byteval($writable), 'log' => (exec("docker inspect --format='{{.LogPath}}' $name|xargs du -b 2>/dev/null |cut -f1")) ?: "0"];
}
$sum = ['total' => 0, 'writable' => 0, 'log' => 0];
array_multisort(array_column($list,'total'),SORT_DESC,$list); // sort on container size
$i = 0;
write(align(_('Name'),48).align(_('Container')).align(_('Writable'))._('Log')."\n");
foreach ($list as $ct) {
write(($i++==0 ? '<hr>':'').align($ct['name'],48).align(autoscale($ct['total'])).align(autoscale($ct['writable'])).autoscale($ct['log'])."\n");
$sum['total'] += $ct['total'];
$sum['writable'] += $ct['writable'];
$sum['log'] += $ct['log'];
}
write("<hr>".align(_('Total size'),48).align(autoscale($sum['total'])).align(autoscale($sum['writable'])).autoscale($sum['log'])."\n",'_DONE_','');
?>
+3
View File
@@ -0,0 +1,3 @@
#!/bin/bash
# Just a wrapper.
exec /usr/bin/docker "$@"
@@ -0,0 +1,2 @@
#!/bin/bash
# Invoked after docker image loopback mounted but before docker is started
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
# delete the docker image file or folder
if [[ -f /boot/config/docker.cfg ]]; then
rm -f /var/local/emhttp/plugins/dynamix.docker.manager/docker.json
. /boot/config/docker.cfg
if [[ -f $DOCKER_IMAGE_FILE ]]; then
echo "Deleting $DOCKER_IMAGE_FILE ..."
rm -f "$DOCKER_IMAGE_FILE"
elif [[ -d $DOCKER_IMAGE_FILE ]]; then
echo "Deleting $DOCKER_IMAGE_FILE ..."
rm -rf "$DOCKER_IMAGE_FILE"
fi
fi
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2015-2023, Lime Technology
* Copyright 2015-2016, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* 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.
*/
?>
<?
$cfgfile = "/boot/config/docker.cfg";
$cfg_defaults = [
"DOCKER_ENABLED" => "no",
"DOCKER_IMAGE_FILE" => "/mnt/user/system/docker/docker.img",
"DOCKER_IMAGE_SIZE" => "20",
"DOCKER_APP_CONFIG_PATH" => "/mnt/user/appdata/",
"DOCKER_APP_UNRAID_PATH" => "",
"DOCKER_READMORE" => "yes"
];
$cfg_new = $cfg_defaults;
if (file_exists($cfgfile)) {
$cfg_old = parse_ini_file($cfgfile);
if (!empty($cfg_old)) {
$cfg_new = array_merge($cfg_defaults, $cfg_old);
if (empty(array_diff($cfg_new, $cfg_old))) unset($cfg_new);
}
}
if (isset($cfg_new)) {
$tmp = '';
foreach ($cfg_new as $key => $value) $tmp .= "$key=\"$value\"\n";
file_put_contents($cfgfile, $tmp);
}
?>
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2023, Lime Technology
* Copyright 2014-2023, Guilherme Jardim, Eric Schultz, Jon Panozzo.
* 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 = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
extract(parse_plugin_cfg('dynamix', true));
// Multi-language support
$_SERVER['REQUEST_URI'] = "scripts";
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";
exec("pgrep docker", $pid);
if (count($pid) == 1) exit(0);
$DockerClient = new DockerClient();
$DockerTemplates = new DockerTemplates();
foreach ($argv as $arg) {
switch ($arg) {
case '-v' : $DockerTemplates->verbose = true; break;
case 'check': $check = true; break;
case 'nonotify': $nonotify = true; break;}
}
if (!isset($check)) {
echo " Updating templates... ";
$DockerTemplates->downloadTemplates();
echo " Updating info... ";
$DockerTemplates->getAllInfo(true);
echo " Done.";
} else {
$notify = "$docroot/webGui/scripts/notify";
$var = @parse_ini_file("/var/local/emhttp/var.ini") ?: [];
$server = strtoupper(_var($var,'NAME','tower'));
$output = _var($notify,'docker_notify');
$info = $DockerTemplates->getAllInfo(true);
foreach ($DockerClient->getDockerContainers() as $ct) {
$name = $ct['Name'];
$image = $ct['Image'];
if ($info[$name]['updated'] == "false") {
$updateStatus = is_file($dockerManPaths['update-status']) ? json_decode(file_get_contents($dockerManPaths['update-status']),true) : [];
$new = str_replace('sha256:', '', $updateStatus[$image]['remote']);
$new = substr($new, 0, 4).'..'.substr($new, -4, 4);
if ( ! isset($nonotify) ) {
$event = str_replace("&apos;","'",_("Docker")." - $name [$new]");
$subject = str_replace("&apos;","'",sprintf(_("Notice [%s] - Version update %s"),$server,$new));
$description = str_replace("&apos;","'",sprintf(_("A new version of %s is available"),$name));
exec("$notify -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Docker' -x");
}
}
}
}
exit(0);
?>
@@ -0,0 +1,30 @@
#!/usr/bin/php -q
<?PHP
function startsWith($haystack, $needle) {
if (!is_string($haystack) || !is_string($needle)) return false;
return $needle === "" || strripos($haystack, $needle, -strlen($haystack)) !== false;
}
$xmlFiles = glob("/boot/config/plugins/dockerMan/templates-user/*.xml");
foreach ($xmlFiles as $file) {
unset($changeFlag);
$xml = @simplexml_load_file($file);
if (!$xml) continue;
foreach ($xml->Config as $id => $config) {
if (startsWith((string)$config->attributes()->Description,"Container ".(string)$config->attributes()->Type)) {
$config->attributes()->Description = "";
$changeFlag = true;
}
}
if (isset($changeFlag)) {
copy($file,"$file.bak");
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml->asXML());
file_put_contents($file,$dom->saveXML());
}
}
?>
@@ -0,0 +1,197 @@
#!/usr/bin/php -q
<?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 = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Wrappers.php";
extract(parse_plugin_cfg('dynamix',true));
// add translations
$_SERVER['REQUEST_URI'] = '';
$login_locale = _var($display,'locale');
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
$var = parse_ini_file('/var/local/emhttp/var.ini');
$DockerClient = new DockerClient();
$DockerUpdate = new DockerUpdate();
$DockerTemplates = new DockerTemplates();
$custom = DockerUtil::custom();
$subnet = DockerUtil::network($custom);
$cpus = DockerUtil::cpus();
function write(...$messages){
$com = curl_init();
curl_setopt_array($com,[
CURLOPT_URL => 'http://localhost/pub/docker?buffer_length=1',
CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
CURLOPT_POST => 1,
CURLOPT_RETURNTRANSFER => true
]);
foreach ($messages as $message) {
curl_setopt($com, CURLOPT_POSTFIELDS, $message);
curl_exec($com);
}
curl_close($com);
}
function stopContainer_nchan($name) {
global $DockerClient;
$waitID = mt_rand();
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>"._('Stopping container').": ".htmlspecialchars($name)."</legend><p class='logLine'></p><span id='wait-$waitID'>"._('Please wait')." </span></fieldset>","show_Wait\0$waitID");
$retval = $DockerClient->stopContainer($name);
$out = ($retval === true) ? _('Successfully stopped container').": $name" : _('Error').": ".$retval;
write("stop_Wait\0$waitID","addLog\0<b>".htmlspecialchars($out)."</b>");
}
function removeContainer_nchan($name) {
global $DockerClient;
$waitID = mt_rand();
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>"._('Removing container').": ".htmlspecialchars($name)."</legend><p class='logLine'></p><span id='wait-$waitID'>"._('Please wait')." </span></fieldset>","show_Wait\0$waitID");
$retval = $DockerClient->removeContainer($name);
$out = ($retval === true) ? _('Successfully removed container').": $name" : _('Error').": ".$retval;
write("stop_Wait\0$waitID","addLog\0<b>".htmlspecialchars($out)."</b>");
}
function removeImage_nchan($image) {
global $DockerClient;
$waitID = mt_rand();
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>"._('Removing orphan image').": ".htmlspecialchars($image)."</legend><p class='logLine'></p><span id='wait-$waitID'>"._('Please wait')." </span></fieldset>","show_Wait\0$waitID");
$retval = $DockerClient->removeImage($image);
$out = ($retval === true) ? _('Successfully removed orphan image').": $image" : _('Error').": ".$retval;
write("stop_Wait\0$waitID","addLog\0<b>".htmlspecialchars($out)."</b>");
}
function pullImage_nchan($name, $image) {
global $DockerClient, $DockerTemplates, $DockerUpdate;
$waitID = mt_rand();
if (!preg_match("/:\S+$/", $image)) $image .= ":latest";
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>"._('Pulling image').": ".htmlspecialchars($image)."</legend><p class='logLine'></p><span id='wait-$waitID'>"._('Please wait')." </span></fieldset>","show_Wait\0$waitID");
$alltotals = [];
$laststatus = [];
$strError = '';
$DockerClient->pullImage($image, function ($line) use (&$alltotals, &$laststatus, &$waitID, &$strError, $image, $DockerClient, $DockerUpdate) {
$cnt = json_decode($line, true);
$id = $cnt['id'] ?? '';
$status = $cnt['status'] ?? '';
if (isset($cnt['error'])) $strError = $cnt['error'];
if ($waitID !== false) {
write("stop_Wait\0$waitID");
$waitID = false;
}
if (empty($status)) return;
if (!empty($id)) {
if (!empty($cnt['progressDetail']) && !empty($cnt['progressDetail']['total'])) {
$alltotals[$id] = $cnt['progressDetail']['total'];
}
if (empty($laststatus[$id])) {
$laststatus[$id] = '';
}
switch ($status) {
case 'Waiting':
// Omit
break;
case 'Downloading':
if ($laststatus[$id] != $status) {
write("addToID\0$id\0".htmlspecialchars($status));
}
$total = $cnt['progressDetail']['total'];
$current = $cnt['progressDetail']['current'];
if ($total > 0) {
$percentage = round(($current / $total) * 100);
write("progress\0$id\0 ".$percentage."% "._('of')." ".$DockerClient->formatBytes($total));
} else {
// Docker must not know the total download size (http-chunked or something?)
// just show the current download progress without the percentage
$alltotals[$id] = $current;
write("progress\0$id\0".$DockerClient->formatBytes($current));
}
break;
default:
if ($laststatus[$id] == "Downloading") {
write("progress\0$id\0 100% "._('of')." ".$DockerClient->formatBytes($alltotals[$id]));
}
if ($laststatus[$id] != $status) {
write("addToID\0".($id=='latest'?mt_rand():$id)."\0".htmlspecialchars($status));
}
break;
}
$laststatus[$id] = $status;
} else {
if (strpos($status, 'Status: ') === 0) {
write("addLog\0".htmlspecialchars($status));
}
if (strpos($status, 'Digest: ') === 0) {
$DockerUpdate->setUpdateStatus($image, substr($status,8));
}
}
});
write("addLog\0<br><b>"._('TOTAL DATA PULLED').":</b> ".$DockerClient->formatBytes(array_sum($alltotals)));
if (!empty($strError)) {
write("addLog\0<br><span class='error'><b>"._('Error').":</b> ".htmlspecialchars($strError)."</span>");
return false;
}
return true;
}
function execCommand_nchan($command) {
$waitID = mt_rand();
[$cmd,$args] = explode(' ',$command,2);
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>"._('Command execution')."</legend>".basename($cmd).' '.str_replace(" -","<br>&nbsp;&nbsp;-",htmlspecialchars($args))."<br><span id='wait-$waitID'>"._('Please wait')." </span><p class='logLine'></p></fieldset>","show_Wait\0$waitID");
$proc = popen("$command 2>&1",'r');
while ($out = fgets($proc)) {
$out = preg_replace("%[\t\n\x0B\f\r]+%", '',$out);
write("addLog\0".htmlspecialchars($out));
}
$retval = pclose($proc);
$out = $retval ? _('The command failed').'.' : _('The command finished successfully').'!';
write("stop_Wait\0$waitID","addLog\0<br><b>$out</b>");
return $retval===0;
}
$style = ["<style>"];
$style[] = ".logLine{font-family:bitstream!important;font-size:1.2rem!important;margin:0;padding:0}";
$style[] = "fieldset.docker{border:solid thin;margin-top:8px}";
$style[] = "legend{font-size:1.1rem!important;font-weight:bold}";
$style[] = "</style>";
write(implode($style)."<p class='logLine'></p>");
foreach (explode('*',rawurldecode($argv[1])) as $value) {
$tmpl = $DockerTemplates->getUserTemplate($value);
if (!$tmpl) {
write("addLog\0"._('Configuration not found').". "._('Was this container created using this plugin')."?");
continue;
}
$xml = file_get_contents($tmpl);
[$cmd, $Name, $Repository] = xmlToCommand($tmpl);
$Registry = getXmlVal($xml, "Registry");
$oldImageID = $DockerClient->getImageID($Repository);
// pull image
if (!pullImage_nchan($Name, $Repository)) continue;
$oldContainerInfo = $DockerClient->getContainerDetails($Name);
// determine if the container is still running
$startContainer = false;
if (!empty($oldContainerInfo) && !empty($oldContainerInfo['State']) && !empty($oldContainerInfo['State']['Running'])) {
// since container was already running, put it back it to a running state after update
$cmd = str_replace('/docker create ', '/docker run -d ', $cmd);
$startContainer = true;
// attempt graceful stop of container first
stopContainer_nchan($Name);
}
// force kill container if still running after 10 seconds
if (empty($_GET['communityApplications'])) removeContainer_nchan($Name);
execCommand_nchan($cmd);
if ($startContainer) addRoute($Name); // add route for remote WireGuard access
$DockerClient->flushCaches();
$newImageID = $DockerClient->getImageID($Repository);
// remove old orphan image since it's no longer used by this container
if ($oldImageID && $oldImageID != $newImageID) removeImage_nchan($oldImageID);
}
write('_DONE_','');
?>
@@ -0,0 +1,62 @@
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button{
font-family:clear-sans;font-size:1.1rem;font-weight:bold;letter-spacing:2px;text-transform:uppercase;margin:10px 12px 10px 0;padding:9px 18px;text-decoration:none;white-space:nowrap;cursor:pointer;outline:none;border-radius:4px;border:0;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%
}
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button:hover{
color:#f2f2f2;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f));background:linear-gradient(90deg,#e22828 0,#ff8c2f)
}
.ui-dropdownchecklist .ui-state-default{
background:#f2f2f2;
border:none;
box-shadow:none;
outline:none;
cursor:pointer;
height:2.2rem;
line-height:2.2rem;
}
.ui-dropdownchecklist-group{
font-weight:normal;
font-style:italic;
padding:1px 9px 1px 8px;
}
.ui-dropdownchecklist-selector{
border:1px solid #1c1c1c;
display:inline-block;
cursor:pointer;
padding:1px 9px 1px 8px;
}
.ui-dropdownchecklist-selector-wrapper{
vertical-align:middle;
font-size:0;
}
.ui-widget-header{
border:none;
background:#e3e3e3;
color:#1c1c1c;
font-weight:bold;
}
.ui-widget-content{
border:1px solid #1c1c1c;
background:#f2f2f2;
color:#1c1c1c;
.ui-state-active{
background:#e8e8e8;
}
.ui-dropdownchecklist-dropcontainer{
background:#f2f2f2;
border:1px solid #1c1c1c;
}
.ui-state-disabled{
color:#1c1c1c;border-color:#a2a2a2;background:#e8e8e8;opacity:0.5;
}
.ui-dropdownchecklist-indent{
padding-left:7px;
}
.ui-dropdownchecklist-text{
color:#1c1c1c;
font-size:1.3rem;
}
.ui-dropdownchecklist .ui-widget-content .ui-state-default{
background:#f2f2f2;
border:0px;
}
@@ -0,0 +1,62 @@
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button{
font-family:clear-sans;font-size:1.1rem;font-weight:bold;letter-spacing:2px;text-transform:uppercase;margin:10px 12px 10px 0;padding:9px 18px;text-decoration:none;white-space:nowrap;cursor:pointer;outline:none;border-radius:4px;border:0;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%
}
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button:hover{
color:#f2f2f2;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f));background:linear-gradient(90deg,#e22828 0,#ff8c2f)
}
.ui-dropdownchecklist .ui-state-default{
background:#1c1c1c;
border:none;
box-shadow:none;
outline:none;
cursor:pointer;
height:2.2rem;
line-height:2.2rem;
}
.ui-dropdownchecklist-group{
font-weight:normal;
font-style:italic;
padding:1px 9px 1px 8px;
}
.ui-dropdownchecklist-selector{
border:1px solid #e5e5e5;
display:inline-block;
cursor:pointer;
padding:1px 9px 1px 8px;
}
.ui-dropdownchecklist-selector-wrapper{
vertical-align:middle;
font-size:0;
}
.ui-widget-header{
border:none;
background:#2b2b2b;
color:#f2f2f2;
font-weight:bold;
}
.ui-widget-content{
border:1px solid #e5e5e5;
background:#1c1c1c;
color:#f2f2f2;
.ui-state-active{
background:#262626;
}
.ui-dropdownchecklist-dropcontainer{
background:#1c1c1c;
border:1px solid #e5e5e5;
}
.ui-state-disabled{
color:#f2f2f2;border-color:#6c6c6c;background:#262626;opacity:0.5;
}
.ui-dropdownchecklist-indent{
padding-left:7px;
}
.ui-dropdownchecklist-text{
color:#f2f2f2;
font-size:1.3rem;
}
.ui-dropdownchecklist .ui-widget-content .ui-state-default{
background:#1c1c1c;
border:0px;
}
@@ -0,0 +1,62 @@
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button{
font-family:clear-sans;font-size:1.1rem;font-weight:bold;letter-spacing:2px;text-transform:uppercase;margin:10px 12px 10px 0;padding:9px 18px;text-decoration:none;white-space:nowrap;cursor:pointer;outline:none;border-radius:4px;border:0;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%
}
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button:hover{
color:#f2f2f2;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f));background:linear-gradient(90deg,#e22828 0,#ff8c2f)
}
.ui-dropdownchecklist .ui-state-default{
background:#1c1c1c;
border:none;
box-shadow:none;
outline:none;
cursor:pointer;
height:2.2rem;
line-height:2.2rem;
}
.ui-dropdownchecklist-group{
font-weight:normal;
font-style:italic;
padding:1px 9px 1px 8px;
}
.ui-dropdownchecklist-selector{
border:1px solid #e5e5e5;
display:inline-block;
cursor:pointer;
padding:1px 9px 1px 8px;
}
.ui-dropdownchecklist-selector-wrapper{
vertical-align:middle;
font-size:0;
}
.ui-widget-header{
border:none;
background:#2b2b2b;
color:#f2f2f2;
font-weight:bold;
}
.ui-widget-content{
border:1px solid #e5e5e5;
background:#1c1c1c;
color:#f2f2f2;
.ui-state-active{
background:#262626;
}
.ui-dropdownchecklist-dropcontainer{
background:#1c1c1c;
border:1px solid #e5e5e5;
}
.ui-state-disabled{
color:#f2f2f2;border-color:#6c6c6c;background:#262626;opacity:0.5;
}
.ui-dropdownchecklist-indent{
padding-left:7px;
}
.ui-dropdownchecklist-text{
color:#f2f2f2;
font-size:1.3rem;
}
.ui-dropdownchecklist .ui-widget-content .ui-state-default{
background:#1c1c1c;
border:0px;
}
@@ -0,0 +1,62 @@
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button{
font-family:clear-sans;font-size:1.1rem;font-weight:bold;letter-spacing:2px;text-transform:uppercase;margin:10px 12px 10px 0;padding:9px 18px;text-decoration:none;white-space:nowrap;cursor:pointer;outline:none;border-radius:4px;border:0;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%
}
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button:hover{
color:#f2f2f2;background:-webkit-gradient(linear,left top,right top,from(#e22828),to(#ff8c2f));background:linear-gradient(90deg,#e22828 0,#ff8c2f)
}
.ui-dropdownchecklist .ui-state-default{
background:#f2f2f2;
border:none;
box-shadow:none;
outline:none;
cursor:pointer;
height:2.2rem;
line-height:2.2rem;
}
.ui-dropdownchecklist-group{
font-weight:normal;
font-style:italic;
padding:1px 9px 1px 8px;
}
.ui-dropdownchecklist-selector{
border:1px solid #1c1c1c;
display:inline-block;
cursor:pointer;
padding:1px 9px 1px 8px;
}
.ui-dropdownchecklist-selector-wrapper{
vertical-align:middle;
font-size:0;
}
.ui-widget-header{
border:none;
background:#e3e3e3;
color:#1c1c1c;
font-weight:bold;
}
.ui-widget-content{
border:1px solid #1c1c1c;
background:#f2f2f2;
color:#1c1c1c;
.ui-state-active{
background:#e8e8e8;
}
.ui-dropdownchecklist-dropcontainer{
background:#f2f2f2;
border:1px solid #1c1c1c;
}
.ui-state-disabled{
color:#1c1c1c;border-color:#a2a2a2;background:#e8e8e8;opacity:0.5;
}
.ui-dropdownchecklist-indent{
padding-left:7px;
}
.ui-dropdownchecklist-text{
color:#1c1c1c;
font-size:1.3rem;
}
.ui-dropdownchecklist .ui-widget-content .ui-state-default{
background:#f2f2f2;
border:0px;
}