mirror of
https://github.com/unraid/webgui.git
synced 2026-02-05 16:39:01 -06:00
Plugin system update
This commit is contained in:
@@ -36,7 +36,7 @@ const args = {};
|
||||
|
||||
function openInstall(cmd,title,plg) {
|
||||
if (cmd == null) {
|
||||
openPlugin(args.cmd,args.title,args.plg);
|
||||
openPlugin(args.cmd,args.title,args.plg,null,1);
|
||||
return;
|
||||
}
|
||||
args.cmd = cmd;
|
||||
@@ -69,14 +69,22 @@ function multiRemove() {
|
||||
if ($('input.remove:checked').length > 1) $('#removeall').show(); else $('#removeall').hide();
|
||||
}
|
||||
function updateList() {
|
||||
let list = [];
|
||||
$('input.update').each(function(){list.push($(this).attr('data'));});
|
||||
openPlugin("multiplugin update "+list.join('*'),"_(Update All Plugins)_",":return");
|
||||
var plugin = [];
|
||||
$('input.update').each(function(){plugin.push($(this).attr('data'));});
|
||||
var plugins = plugin.join('*');
|
||||
$('#updateall').hide();
|
||||
$.get('/plugins/dynamix.plugin.manager/include/ShowPlugins.php',{cmd:'pending',plugin:plugins},function() {
|
||||
openPlugin("multiplugin update "+plugins,"_(Update All Plugins)_",":return","loadlist",1);
|
||||
});
|
||||
}
|
||||
function removeList() {
|
||||
let list = [];
|
||||
$('input.remove:checked').each(function(){list.push($(this).attr('data'));});
|
||||
openPlugin("multiplugin remove "+list.join('*'),"_(Remove Selected Plugins)_","","refresh");
|
||||
var plugin = [];
|
||||
$('input.remove:checked').each(function(){plugin.push($(this).attr('data'));});
|
||||
var plugins = plugin.join('*');
|
||||
$('#removeall').hide();
|
||||
$.get('/plugins/dynamix.plugin.manager/include/ShowPlugins.php',{cmd:'pending',plugin:plugins},function() {
|
||||
openPlugin("multiplugin remove "+plugins,"_(Remove Selected Plugins)_","","refresh",1);
|
||||
});
|
||||
}
|
||||
function updateInfo(data) {
|
||||
var updates = data.split('\n');
|
||||
@@ -85,6 +93,8 @@ function updateInfo(data) {
|
||||
for (var i=0,field; field=fields[i]; i++) {
|
||||
var row = field.split('::');
|
||||
$('#'+row[0]).attr('data',row[1]).html(row[2]);
|
||||
var removeButton = $('input[data="'+row[0].substr(4).replace(/-/g,'.')+'.plg'+'"]');
|
||||
if (row[2].indexOf('hourglass') >= 0) removeButton.hide(); else removeButton.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,10 +133,10 @@ function loadlist(id,check) {
|
||||
}
|
||||
} else {
|
||||
updateInfo(data[0]);
|
||||
if (data[1] > 1) $('#updateall').show(); else $('#updateall').hide();
|
||||
}
|
||||
$('#plugin_table').trigger('update');
|
||||
$('#checkall').find('input').prop('disabled',false);
|
||||
if (data[1] > 1) $('#updateall').show(); else $('#updateall').hide();
|
||||
});
|
||||
}
|
||||
$(function() {
|
||||
|
||||
@@ -41,7 +41,11 @@ function make_link($method, $arg, $extra='') {
|
||||
} else {
|
||||
$cmd = "plugin $method $arg".($extra?" $extra":"");
|
||||
}
|
||||
return "$check<input type='button' id='$id' data='$arg' class='$method' value=\""._(ucfirst($method))."\" onclick='openInstall(\"$cmd\",\""._(ucwords($method)." Plugin")."\",\"$plg\");'$disabled>";
|
||||
if (is_file("/tmp/plugins/pluginPending/$arg") && !$check) {
|
||||
return "<span class='orange-text'><i class='fa fa-hourglass-o fa-fw'></i> "._('pending')."</span>";
|
||||
} else {
|
||||
return "$check<input type='button' id='$id' data='$arg' class='$method' value=\""._(ucfirst($method))."\" onclick='openInstall(\"$cmd\",\""._(ucwords($method)." Plugin")."\",\"$plg\");'$disabled>";
|
||||
}
|
||||
}
|
||||
|
||||
// trying our best to find an icon
|
||||
|
||||
@@ -39,6 +39,12 @@ if ($cmd=='alert') {
|
||||
die();
|
||||
}
|
||||
|
||||
if ($cmd=='pending') {
|
||||
// prepare pending status for multi operations
|
||||
foreach (explode('*',$_GET['plugin']) as $plugin) file_put_contents("/tmp/plugins/pluginPending/$plugin",'multi');
|
||||
die();
|
||||
}
|
||||
|
||||
if ($audit) {
|
||||
[$plg,$action] = my_explode(':',$audit);
|
||||
switch ($action) {
|
||||
|
||||
@@ -27,32 +27,47 @@ function searchLink(&$db,$url) {
|
||||
if ($url) for ($i = 0; $i < count($db); $i++) if ($db[$i]['PluginURL']==$url) return $db[$i]['Support'];
|
||||
}
|
||||
|
||||
$method = $argv[1];
|
||||
$name = $argv[2];
|
||||
$error = $argv[3];
|
||||
$plugin = "/boot/config/plugins/$name";
|
||||
$type = $argv[1]; // plugin or language
|
||||
$method = $argv[2]; // install, update, remove, check
|
||||
$name = $argv[3]; // plugin name (*.plg) or language name (*.xml)
|
||||
$error = $argv[4]; // error code (empty if none)
|
||||
|
||||
if ($method == 'install' or $method == 'update') {
|
||||
// check if method successfully completed
|
||||
if ($error) die;
|
||||
// update support link in plugin file
|
||||
$info = readJson('/tmp/community.applications/tempFiles/templates.json');
|
||||
// find matching support link
|
||||
$url = plugin('pluginURL', $plugin);
|
||||
if ($support = searchLink($info, $url) ?: searchLink($info, newurl($url))) {
|
||||
// update incorrect or missing support links
|
||||
if (plugin('support', $plugin) != $support) {
|
||||
$xml = @simplexml_load_file($plugin);
|
||||
if ($xml->xpath('//PLUGIN/@support')[0]) {
|
||||
// support link exists, update it
|
||||
$xml->xpath('//PLUGIN/@support')[0] = $support;
|
||||
} else {
|
||||
// support link is missing, add it
|
||||
$xml->addAttribute('support', $support);
|
||||
$plugin = "/boot/config/plugins/$name";
|
||||
$pending = "/tmp/plugins/pluginPending";
|
||||
|
||||
switch ($type) {
|
||||
case 'plugin':
|
||||
switch ($method) {
|
||||
case 'install':
|
||||
case 'update':
|
||||
// abort if method was unsuccessful
|
||||
if ($error) break;
|
||||
// update support link in plugin file
|
||||
$info = readJson('/tmp/community.applications/tempFiles/templates.json');
|
||||
// find matching support link
|
||||
$url = plugin('pluginURL', $plugin);
|
||||
if ($support = searchLink($info, $url) ?: searchLink($info, newurl($url))) {
|
||||
// update incorrect or missing support links
|
||||
if (plugin('support', $plugin) != $support) {
|
||||
$xml = @simplexml_load_file($plugin);
|
||||
if ($xml->xpath('//PLUGIN/@support')[0]) {
|
||||
// support link exists, update it
|
||||
$xml->xpath('//PLUGIN/@support')[0] = $support;
|
||||
} else {
|
||||
// support link is missing, add it
|
||||
$xml->addAttribute('support', $support);
|
||||
}
|
||||
echo "Updating support link\n";
|
||||
file_put_contents($plugin, $xml->saveXML());
|
||||
}
|
||||
echo "Updating support link\n";
|
||||
file_put_contents($plugin, $xml->saveXML());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'language':
|
||||
// nothing defined
|
||||
break;
|
||||
}
|
||||
|
||||
// unset pending status
|
||||
if ($method != 'check') @unlink("$pending/$name");
|
||||
?>
|
||||
|
||||
@@ -15,20 +15,41 @@
|
||||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: "/usr/local/emhttp";
|
||||
require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php";
|
||||
|
||||
$method = $argv[1];
|
||||
$plugin = $argv[2];
|
||||
$type = $argv[1]; // plugin or language
|
||||
$method = $argv[2]; // install, update, remove, check
|
||||
$name = $argv[3]; // plugin name (*.plg) or language name (*.xml)
|
||||
|
||||
// validate plugin update (not applicable to OS updates)
|
||||
if ($method == 'check' and $plugin != 'unRAIDServer.plg') {
|
||||
$old_plugin = "/boot/config/plugins/$plugin";
|
||||
$new_plugin = "/tmp/plugins/$plugin";
|
||||
if (plugin('version', $new_plugin) > plugin('version', $old_plugin)) {
|
||||
echo "Validating $plugin update\n";
|
||||
if (($status = plugin('validate', $new_plugin)) != 'valid') {
|
||||
echo "$status\n";
|
||||
// restore original plugin and undo update
|
||||
copy($old_plugin, $new_plugin);
|
||||
$plugin = "/boot/config/plugins/$name";
|
||||
$pending = "/tmp/plugins/pluginPending";
|
||||
|
||||
// set pending status
|
||||
if (!is_dir($pending)) mkdir($pending);
|
||||
if ($method != 'check') file_put_contents("$pending/$name",$method);
|
||||
|
||||
switch ($type) {
|
||||
case 'plugin':
|
||||
switch ($method) {
|
||||
case 'update':
|
||||
// implicit validation on plugin update
|
||||
if (@readlink("/var/log/plugins/$name")) plugin('check', $name);
|
||||
break;
|
||||
case 'check':
|
||||
// validate plugin update (not applicable to OS updates)
|
||||
if ($name == 'unRAIDServer.plg') break;
|
||||
$new_plugin = "/tmp/plugins/$name";
|
||||
if (plugin('version', $new_plugin) > plugin('version', $plugin)) {
|
||||
echo "Validating $name update\n";
|
||||
if (($status = plugin('validate', $new_plugin)) != 'valid') {
|
||||
echo "$status\n";
|
||||
// restore original plugin and undo update
|
||||
copy($plugin, $new_plugin);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'language':
|
||||
// nothing defined
|
||||
break;
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -79,6 +79,34 @@ function write($message) {
|
||||
}
|
||||
}
|
||||
|
||||
// Run hooked scripts before correct execution of "method"
|
||||
// method = install, update, remove, check
|
||||
// hook programs receives three parameters: type=language and method and language-name
|
||||
//
|
||||
function pre_hooks() {
|
||||
global $method, $name;
|
||||
$language = pathinfo($name)['extension'] == 'xml' ? $name : "lang-$name.xml";
|
||||
$hooks = "/usr/local/emhttp/plugins/dynamix.plugin.manager/pre-hooks";
|
||||
foreach (glob("$hooks/*") as $hook) if (is_executable($hook)) {
|
||||
write("Executing hook script: ".basename($hook)."\n");
|
||||
run("$hook language $method $language");
|
||||
}
|
||||
}
|
||||
|
||||
// Run hooked scripts after successful or failed completion of "method"
|
||||
// method = install, update, remove, check
|
||||
// hook programs receives four parameters: type=language and method and language-name and error (empty if none)
|
||||
//
|
||||
function post_hooks($error='') {
|
||||
global $method, $name;
|
||||
$language = pathinfo($name)['extension'] == 'xml' ? $name : "lang-$name.xml";
|
||||
$hooks = "/usr/local/emhttp/plugins/dynamix.plugin.manager/post-hooks";
|
||||
foreach (glob("$hooks/*") as $hook) if (is_executable($hook)) {
|
||||
write("Executing hook script: ".basename($hook)."\n");
|
||||
run("$hook language $method $language $error");
|
||||
}
|
||||
}
|
||||
|
||||
// Download a file from a URL.
|
||||
// Returns TRUE if success else FALSE and fills in error.
|
||||
//
|
||||
@@ -262,8 +290,12 @@ if ($method == 'install') {
|
||||
$link_file = "$plugins/$name";
|
||||
$lang_file = "$boot/$name";
|
||||
@unlink($link_file);
|
||||
// run hook scripts for pre processing
|
||||
pre_hooks();
|
||||
if (language('install', $xml_file, $error) === false) {
|
||||
write("language: $error\n");
|
||||
// run hook scripts for post processing
|
||||
post_hooks($error);
|
||||
exit(1);
|
||||
}
|
||||
$lang = language('Language', $xml_file, $error) ?: substr($name,0,-4);
|
||||
@@ -271,6 +303,8 @@ if ($method == 'install') {
|
||||
symlink($lang_file, $link_file);
|
||||
write("language: $lang language pack installed\n");
|
||||
logger("language: $lang language pack installed");
|
||||
// run hook scripts for post processing
|
||||
post_hooks();
|
||||
done();
|
||||
}
|
||||
|
||||
@@ -296,12 +330,18 @@ if ($method == 'check') {
|
||||
@unlink($xml_file);
|
||||
exit(1);
|
||||
}
|
||||
// run hook scripts for pre processing
|
||||
pre_hooks();
|
||||
$version = language('Version', $xml_file, $error);
|
||||
if ($version === false) {
|
||||
write("language: $error\n");
|
||||
// run hook scripts for post processing
|
||||
post_hooks($error);
|
||||
exit(1);
|
||||
}
|
||||
write("$version\n");
|
||||
// run hook scripts for post processing
|
||||
post_hooks();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -333,14 +373,20 @@ if ($method == 'update') {
|
||||
// install the updated plugin
|
||||
@unlink("$boot/dynamix/lang-$name.zip");
|
||||
@unlink($link_file);
|
||||
// run hook scripts for pre processing
|
||||
pre_hooks();
|
||||
if (language('install', $xml_file, $error) === false) {
|
||||
write("language: $error\n");
|
||||
// run hook scripts for post processing
|
||||
post_hooks($error);
|
||||
exit(1);
|
||||
}
|
||||
copy($xml_file, $lang_file);
|
||||
symlink($lang_file, $link_file);
|
||||
write("language: $lang language pack updated\n");
|
||||
logger("language: $lang language pack updated");
|
||||
// run hook scripts for post processing
|
||||
post_hooks();
|
||||
done();
|
||||
}
|
||||
|
||||
@@ -356,12 +402,18 @@ if ($method == 'remove') {
|
||||
exit(1);
|
||||
}
|
||||
$lang = language('Language', $lang_file, $error) ?: $name;
|
||||
// run hook scripts for pre processing
|
||||
pre_hooks();
|
||||
if (language('remove', $lang_file, $error) === false) {
|
||||
write("language: $error\n");
|
||||
// run hook scripts for post processing
|
||||
post_hooks($error);
|
||||
exit(1);
|
||||
}
|
||||
write("language: $lang language pack removed\n");
|
||||
logger("language: $lang language pack removed");
|
||||
// run hook scripts for post processing
|
||||
post_hooks();
|
||||
done();
|
||||
}
|
||||
|
||||
|
||||
@@ -200,27 +200,27 @@ function run($command) {
|
||||
|
||||
// Run hooked scripts before correct execution of "method"
|
||||
// method = install, update, remove, check
|
||||
// hook programs receives two parameters: method and plugin-name
|
||||
// hook programs receives three parameters: type=plugin and method and plugin-name
|
||||
//
|
||||
function pre_hooks() {
|
||||
global $method, $plugin;
|
||||
$hooks = "/usr/local/emhttp/plugins/dynamix.plugin.manager/pre-hooks";
|
||||
foreach (glob("$hooks/*") as $hook) if (is_executable($hook)) {
|
||||
write("Executing hook script: ".basename($hook)."\n");
|
||||
run("$hook $method $plugin");
|
||||
run("$hook plugin $method $plugin");
|
||||
}
|
||||
}
|
||||
|
||||
// Run hooked scripts after successful or failed completion of "method"
|
||||
// method = install, update, remove, check
|
||||
// hook programs receives three parameters: method and plugin-name and error (empty if none)
|
||||
// hook programs receives four parameters: type=plugin and method and plugin-name and error (empty if none)
|
||||
//
|
||||
function post_hooks($error='') {
|
||||
global $method, $plugin;
|
||||
$hooks = "/usr/local/emhttp/plugins/dynamix.plugin.manager/post-hooks";
|
||||
foreach (glob("$hooks/*") as $hook) if (is_executable($hook)) {
|
||||
write("Executing hook script: ".basename($hook)."\n");
|
||||
run("$hook $method $plugin $error");
|
||||
run("$hook plugin $method $plugin $error");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -244,21 +244,29 @@ function openTerminal(tag,name,more) {
|
||||
var socket = ['ttyd','syslog'].includes(tag) ? '/webterminal/'+tag+'/' : '/logterminal/'+name+(more=='.log'?more:'')+'/';
|
||||
$.get('/webGui/include/OpenTerminal.php',{tag:tag,name:name,more:more},function(){tty_window.location=socket; tty_window.focus();});
|
||||
}
|
||||
function bannerAlert(text,cmd,plg,func) {
|
||||
function bannerAlert(text,cmd,plg,func,start) {
|
||||
$.post('/webGui/include/StartCommand.php',{cmd:cmd,pid:1},function(pid) {
|
||||
if (pid == 0) {
|
||||
if ($(".upgrade_notice").hasClass('done') || timers.bannerAlert==null) {
|
||||
if ($(".upgrade_notice").hasClass('done') || timers.bannerAlert == null) {
|
||||
forcedBanner = false;
|
||||
if ($.cookie('addAlert') != null) {
|
||||
removeBannerWarning($.cookie('addAlert'));
|
||||
$.removeCookie('addAlert');
|
||||
}
|
||||
$(".upgrade_notice").removeClass('alert done');
|
||||
if (plg != null) setTimeout((func||'loadlist')+'("'+plg+'")',250);
|
||||
timers.callback = null;
|
||||
if (plg != null) {
|
||||
if ($.cookie('addAlert-page') == null || $.cookie('addAlert-page') == '<?=$task?>') {
|
||||
setTimeout((func||'loadlist')+'("'+plg+'")',250);
|
||||
} else if ('Plugins' == '<?=$task?>') {
|
||||
setTimeout(refresh);
|
||||
}
|
||||
$.removeCookie('addAlert-page');
|
||||
}
|
||||
} else {
|
||||
$(".upgrade_notice").removeClass('alert').addClass('done');
|
||||
timers.bannerAlert = null;
|
||||
setTimeout(function(){bannerAlert(text,cmd,plg,func);},1000);
|
||||
setTimeout(function(){bannerAlert(text,cmd,plg,func,start);},1000);
|
||||
}
|
||||
} else {
|
||||
$.cookie('addAlert',addBannerWarning(text,true,true,true));
|
||||
@@ -266,12 +274,14 @@ function bannerAlert(text,cmd,plg,func) {
|
||||
$.cookie('addAlert-cmd',cmd);
|
||||
$.cookie('addAlert-plg',plg);
|
||||
$.cookie('addAlert-func',func);
|
||||
timers.bannerAlert = setTimeout(function(){bannerAlert(text,cmd,plg,func);},1000);
|
||||
if ($.cookie('addAlert-page') == null) $.cookie('addAlert-page','<?=$task?>');
|
||||
timers.bannerAlert = setTimeout(function(){bannerAlert(text,cmd,plg,func,start);},1000);
|
||||
if (start == 1 && timers.callback == null && plg != null) timers.callback = setTimeout((func||'loadlist')+'("'+plg+'")',250);
|
||||
}
|
||||
});
|
||||
}
|
||||
function openPlugin(cmd,title,plg,func) {
|
||||
$.post('/webGui/include/StartCommand.php',{cmd:cmd+' nchan'},function(pid) {
|
||||
function openPlugin(cmd,title,plg,func,start=0) {
|
||||
$.post('/webGui/include/StartCommand.php',{cmd:cmd+' nchan',start:start},function(pid) {
|
||||
if (pid==0) {
|
||||
$(".upgrade_notice").addClass('alert');
|
||||
return;
|
||||
@@ -281,13 +291,13 @@ function openPlugin(cmd,title,plg,func) {
|
||||
nchan_plugins.stop();
|
||||
$('div.spinner.fixed').hide();
|
||||
$('.sweet-alert').hide('fast').removeClass('nchan');
|
||||
setTimeout(function(){bannerAlert("<?=_('Attention - operation continues in background')?> ["+pid.toString().padStart(8,'0')+"]<i class='fa fa-bomb fa-fw' title=\"<?=_('Abort background process')?>\" onclick='abortOperation("+pid+")'></i>",cmd,plg,func);});
|
||||
setTimeout(function(){bannerAlert("<?=_('Attention - operation continues in background')?> ["+pid.toString().padStart(8,'0')+"]<i class='fa fa-bomb fa-fw' title=\"<?=_('Abort background process')?>\" onclick='abortOperation("+pid+")'></i>",cmd,plg,func,start);});
|
||||
});
|
||||
$('.sweet-alert').addClass('nchan');
|
||||
});
|
||||
}
|
||||
function abortOperation(pid) {
|
||||
swal({title:"<?=_('Abort background operation')?>",text:"<?=_('This may leave the system unstable')?>!",html:true,type:'warning',showCancelButton:true,confirmButtonText:"<?=_('Proceed')?>",cancelButtonText:"<?=_('Cancel')?>"},function(){
|
||||
swal({title:"<?=_('Abort background operation')?>",text:"<?=_('This may leave an unknown state')?>",html:true,type:'warning',showCancelButton:true,confirmButtonText:"<?=_('Proceed')?>",cancelButtonText:"<?=_('Cancel')?>"},function(){
|
||||
$.post('/webGui/include/StartCommand.php',{kill:pid},function() {
|
||||
clearTimeout(timers.bannerAlert);
|
||||
timers.bannerAlert = null;
|
||||
@@ -886,7 +896,7 @@ $(function() {
|
||||
var top = ($.cookie('top')||0) - $('.tabs').offset().top - 75;
|
||||
if (top>0) {$('html,body').scrollTop(top);}
|
||||
$.removeCookie('top');
|
||||
if ($.cookie('addAlert')!=null) bannerAlert(addAlert.text,addAlert.cmd,addAlert.plg,addAlert.func);
|
||||
if ($.cookie('addAlert') != null) bannerAlert(addAlert.text,addAlert.cmd,addAlert.plg,addAlert.func);
|
||||
<?if ($safemode):?>
|
||||
showNotice("<?=_('System running in')?> <b><?=('safe mode')?></b>");
|
||||
<?else:?>
|
||||
|
||||
@@ -18,11 +18,12 @@ function pgrep($proc) {
|
||||
return exec("pgrep -f $proc");
|
||||
}
|
||||
|
||||
if (!empty($_POST['kill']) && $_POST['kill'] > 1) {
|
||||
if (isset($_POST['kill']) && $_POST['kill'] > 1) {
|
||||
exec("kill ".$_POST['kill']);
|
||||
die;
|
||||
}
|
||||
|
||||
$start = isset($_POST['start']) && $_POST['start'] == 1;
|
||||
[$command,$args] = explode(' ',unscript($_POST['cmd']??''),2);
|
||||
|
||||
// find absolute path of command
|
||||
@@ -35,11 +36,11 @@ if ($command && strncmp($name,$path,strlen($path))===0) {
|
||||
if (isset($_POST['pid'])) {
|
||||
// return running pid
|
||||
$pid = pgrep($name);
|
||||
} elseif (!pgrep($name)) {
|
||||
// only execute when command and valid path is given and command not already running
|
||||
} elseif ($start or !pgrep($name)) {
|
||||
// start command in background and return pid
|
||||
exec("echo \"$name $args\" | at -M now >/dev/null 2>&1");
|
||||
usleep(5000);
|
||||
$pid = pgrep($name); // started
|
||||
$pid = pgrep($name);
|
||||
}
|
||||
}
|
||||
echo $pid;
|
||||
|
||||
Reference in New Issue
Block a user