diff --git a/plugins/dynamix.plugin.manager/Plugins.page b/plugins/dynamix.plugin.manager/Plugins.page
index 3f0717a0f..7e7c2f4c0 100644
--- a/plugins/dynamix.plugin.manager/Plugins.page
+++ b/plugins/dynamix.plugin.manager/Plugins.page
@@ -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() {
diff --git a/plugins/dynamix.plugin.manager/include/PluginHelpers.php b/plugins/dynamix.plugin.manager/include/PluginHelpers.php
index 6c95d4b5d..be936e54e 100644
--- a/plugins/dynamix.plugin.manager/include/PluginHelpers.php
+++ b/plugins/dynamix.plugin.manager/include/PluginHelpers.php
@@ -41,7 +41,11 @@ function make_link($method, $arg, $extra='') {
} else {
$cmd = "plugin $method $arg".($extra?" $extra":"");
}
- return "$check";
+ if (is_file("/tmp/plugins/pluginPending/$arg") && !$check) {
+ return " "._('pending')."";
+ } else {
+ return "$check";
+ }
}
// trying our best to find an icon
diff --git a/plugins/dynamix.plugin.manager/include/ShowPlugins.php b/plugins/dynamix.plugin.manager/include/ShowPlugins.php
index 82f0b5a6a..9e4b941cf 100644
--- a/plugins/dynamix.plugin.manager/include/ShowPlugins.php
+++ b/plugins/dynamix.plugin.manager/include/ShowPlugins.php
@@ -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) {
diff --git a/plugins/dynamix.plugin.manager/post-hooks/post_plugin_checks b/plugins/dynamix.plugin.manager/post-hooks/post_plugin_checks
index 8a84b8e44..5b48c7b2b 100755
--- a/plugins/dynamix.plugin.manager/post-hooks/post_plugin_checks
+++ b/plugins/dynamix.plugin.manager/post-hooks/post_plugin_checks
@@ -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");
?>
diff --git a/plugins/dynamix.plugin.manager/pre-hooks/pre_plugin_checks b/plugins/dynamix.plugin.manager/pre-hooks/pre_plugin_checks
index 997e1d0e6..dc8ee0bec 100755
--- a/plugins/dynamix.plugin.manager/pre-hooks/pre_plugin_checks
+++ b/plugins/dynamix.plugin.manager/pre-hooks/pre_plugin_checks
@@ -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;
}
?>
diff --git a/plugins/dynamix.plugin.manager/scripts/language b/plugins/dynamix.plugin.manager/scripts/language
index df6c7d5c2..a530caa06 100755
--- a/plugins/dynamix.plugin.manager/scripts/language
+++ b/plugins/dynamix.plugin.manager/scripts/language
@@ -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();
}
diff --git a/plugins/dynamix.plugin.manager/scripts/plugin b/plugins/dynamix.plugin.manager/scripts/plugin
index 1759f670b..120fe3f21 100755
--- a/plugins/dynamix.plugin.manager/scripts/plugin
+++ b/plugins/dynamix.plugin.manager/scripts/plugin
@@ -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");
}
}
diff --git a/plugins/dynamix/include/DefaultPageLayout.php b/plugins/dynamix/include/DefaultPageLayout.php
index 43d0a474e..08eed2101 100644
--- a/plugins/dynamix/include/DefaultPageLayout.php
+++ b/plugins/dynamix/include/DefaultPageLayout.php
@@ -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')+"]\" onclick='abortOperation("+pid+")'>",cmd,plg,func);});
+ setTimeout(function(){bannerAlert("=_('Attention - operation continues in background')?> ["+pid.toString().padStart(8,'0')+"]\" onclick='abortOperation("+pid+")'>",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);
showNotice("=_('System running in')?> =('safe mode')?>");
diff --git a/plugins/dynamix/include/StartCommand.php b/plugins/dynamix/include/StartCommand.php
index 430903f78..831c280db 100644
--- a/plugins/dynamix/include/StartCommand.php
+++ b/plugins/dynamix/include/StartCommand.php
@@ -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;