gui plugin updates partial
This commit is contained in:
parent
a98340cdc7
commit
f1ea4ae1b8
23
db.js
23
db.js
|
@ -442,6 +442,9 @@ module.exports.CreateDB = function (parent, func) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Setup plugin info collection
|
||||||
|
obj.pluginsfile = db.collection('plugins');
|
||||||
|
|
||||||
setupFunctions(func); // Completed setup of MongoDB
|
setupFunctions(func); // Completed setup of MongoDB
|
||||||
});
|
});
|
||||||
|
@ -543,6 +546,9 @@ module.exports.CreateDB = function (parent, func) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Setup plugin info collection
|
||||||
|
obj.pluginsfile = db.collection('plugins');
|
||||||
|
|
||||||
setupFunctions(func); // Completed setup of MongoJS
|
setupFunctions(func); // Completed setup of MongoJS
|
||||||
} else {
|
} else {
|
||||||
|
@ -604,6 +610,10 @@ module.exports.CreateDB = function (parent, func) {
|
||||||
obj.serverstatsfile.ensureIndex({ fieldName: 'time', expireAfterSeconds: 60 * 60 * 24 * 30 }); // Limit the server stats log to 30 days (Seconds * Minutes * Hours * Days)
|
obj.serverstatsfile.ensureIndex({ fieldName: 'time', expireAfterSeconds: 60 * 60 * 24 * 30 }); // Limit the server stats log to 30 days (Seconds * Minutes * Hours * Days)
|
||||||
obj.serverstatsfile.ensureIndex({ fieldName: 'expire', expireAfterSeconds: 0 }); // Auto-expire events
|
obj.serverstatsfile.ensureIndex({ fieldName: 'expire', expireAfterSeconds: 0 }); // Auto-expire events
|
||||||
|
|
||||||
|
// Setup plugin info collection
|
||||||
|
obj.pluginsfile = new Datastore({ filename: parent.getConfigFilePath('meshcentral-plugins.db'), autoload: true });
|
||||||
|
obj.pluginsfile.persistence.setAutocompactionInterval(36000);
|
||||||
|
|
||||||
setupFunctions(func); // Completed setup of NeDB
|
setupFunctions(func); // Completed setup of NeDB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -741,6 +751,19 @@ module.exports.CreateDB = function (parent, func) {
|
||||||
func(r);
|
func(r);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add a plugin
|
||||||
|
obj.addPlugin = function (plugin) { obj.pluginsfile.insertOne(plugin); };
|
||||||
|
|
||||||
|
// Get all plugins
|
||||||
|
obj.getPlugins = function (func) { obj.pluginsfile.find().sort({ name: 1 }).toArray(func); };
|
||||||
|
|
||||||
|
// Get plugin
|
||||||
|
obj.getPlugin = function (id, func) { obj.pluginsfile.find({ _id: id }).sort({ name: 1 }).toArray(func); };
|
||||||
|
|
||||||
|
// Delete plugin
|
||||||
|
obj.deletePlugin = function (id) { obj.pluginsfile.deleteOne({ _id: id }); };
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Database actions on the main collection (NeDB and MongoJS)
|
// Database actions on the main collection (NeDB and MongoJS)
|
||||||
obj.Set = function (data, func) {
|
obj.Set = function (data, func) {
|
||||||
|
|
19
meshuser.js
19
meshuser.js
|
@ -3102,6 +3102,25 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'plugins': {
|
||||||
|
if ((user.siteadmin & 0xFFFFFFFF) == 0 || parent.parent.pluginHandler == null) break; // must be full admin, plugins enabled
|
||||||
|
parent.db.getPlugins(function(err, docs) {
|
||||||
|
try { ws.send(JSON.stringify({ action: 'updatePluginList', list: docs, result: err })); } catch (ex) { }
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'addplugin': {
|
||||||
|
// @Ylianst - Do we need a new permission here?
|
||||||
|
if ((user.siteadmin & 0xFFFFFFFF) == 0 || parent.parent.pluginHandler == null) break; // must be full admin, plugins enabled
|
||||||
|
parent.parent.pluginHandler.addPlugin(command.url);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'removeplugin': {
|
||||||
|
// @Ylianst - Do we need a new permission here?
|
||||||
|
if ((user.siteadmin & 0xFFFFFFFF) == 0 || parent.parent.pluginHandler == null) break; // must be full admin, plugins enabled
|
||||||
|
parent.parent.pluginHandler.removePlugin(command.id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'plugin': {
|
case 'plugin': {
|
||||||
if (parent.parent.pluginHandler == null) break; // If the plugin's are not supported, reject this command.
|
if (parent.parent.pluginHandler == null) break; // If the plugin's are not supported, reject this command.
|
||||||
command.userid = user._id;
|
command.userid = user._id;
|
||||||
|
|
|
@ -71,6 +71,13 @@ module.exports.pluginHandler = function (parent) {
|
||||||
for (const i of pages) { i.style.display = 'none'; }
|
for (const i of pages) { i.style.display = 'none'; }
|
||||||
QV(id, true);
|
QV(id, true);
|
||||||
};
|
};
|
||||||
|
obj.addPluginEx = function() {
|
||||||
|
meshserver.send({ action: 'addplugin', url: Q('pluginurlinput').value});
|
||||||
|
};
|
||||||
|
obj.addPluginDlg = function() {
|
||||||
|
setDialogMode(2, "Plugin URL", 3, obj.addPluginEx, '<input type=text id=pluginurlinput style=width:100% />');
|
||||||
|
focusTextBox('pluginurlinput');
|
||||||
|
};
|
||||||
return obj; };`;
|
return obj; };`;
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
@ -152,7 +159,88 @@ module.exports.pluginHandler = function (parent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return panel;
|
return panel;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
obj.isValidConfig = function(conf, url) { // check for the required attributes
|
||||||
|
var isValid = true;
|
||||||
|
if (!(
|
||||||
|
typeof conf.name == 'string'
|
||||||
|
&& typeof conf.version == 'string'
|
||||||
|
&& typeof conf.author == 'string'
|
||||||
|
&& typeof conf.description == 'string'
|
||||||
|
&& typeof conf.hasAdminPanel == 'boolean'
|
||||||
|
&& typeof conf.homepage == 'string'
|
||||||
|
&& typeof conf.changelogUrl == 'string'
|
||||||
|
&& typeof conf.configUrl == 'string'
|
||||||
|
&& typeof conf.repository == 'object'
|
||||||
|
&& typeof conf.repository.type == 'string'
|
||||||
|
&& typeof conf.repository.url == 'string'
|
||||||
|
&& typeof conf.meshCentralCompat == 'string'
|
||||||
|
// && conf.configUrl == url // make sure we're loading a plugin from its desired config
|
||||||
|
)) isValid = false;
|
||||||
|
// more checks here?
|
||||||
|
return isValid;
|
||||||
|
};
|
||||||
|
|
||||||
|
obj.addPlugin = function(url) {
|
||||||
|
var https = require('https');
|
||||||
|
//var pit = obj.path.join(obj.pluginPath, )
|
||||||
|
|
||||||
|
https.get(url, function(res) {
|
||||||
|
var configStr = '';
|
||||||
|
res.on('data', function(chunk){
|
||||||
|
configStr += chunk;
|
||||||
|
});
|
||||||
|
res.on('end', function(){
|
||||||
|
if (configStr[0] == '{') {
|
||||||
|
try {
|
||||||
|
var pluginConfig = JSON.parse(configStr);
|
||||||
|
if (obj.isValidConfig(pluginConfig, url)) {
|
||||||
|
// add to database
|
||||||
|
// we met the requirements of a valid config, but in case there's extra, let's rebuild for what we need
|
||||||
|
parent.db.addPlugin({
|
||||||
|
"name": pluginConfig.name,
|
||||||
|
"version": pluginConfig.version,
|
||||||
|
"description": pluginConfig.description,
|
||||||
|
"hasAdminPanel": pluginConfig.hasAdminPanel,
|
||||||
|
"homepage": pluginConfig.homepage,
|
||||||
|
"changelogUrl": pluginConfig.changelogUrl,
|
||||||
|
"configUrl": pluginConfig.configUrl,
|
||||||
|
"repository": {
|
||||||
|
"type": pluginConfig.repository.type,
|
||||||
|
"url": pluginConfig.repository.url
|
||||||
|
},
|
||||||
|
"meshCentralCompat": pluginConfig.meshCentralCompat,
|
||||||
|
"status": 0 // 0: disabled, 1: enabled
|
||||||
|
});
|
||||||
|
parent.db.getPlugins(function(err, docs){
|
||||||
|
var targets = ['*', 'server-users'];
|
||||||
|
parent.DispatchEvent(targets, obj, { action: 'updatePluginList', list: docs });
|
||||||
|
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// @TODO return error to user
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) { console.log('Error processing addPlugin request. Check that you have valid JSON.'); }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}).on('error', function(e) {
|
||||||
|
console.log("Got error: " + e.message);
|
||||||
|
});
|
||||||
|
/* const file = fs.createWriteStream("file.jpg");
|
||||||
|
const request = http.get("http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg", function(response) {
|
||||||
|
response.pipe(file);
|
||||||
|
}); */
|
||||||
|
};
|
||||||
|
|
||||||
|
obj.getPlugins = function() {
|
||||||
|
var p = parent.db.getPlugins();
|
||||||
|
if (typeof p == 'undefined' || p.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
Binary file not shown.
After Width: | Height: | Size: 656 B |
|
@ -2563,4 +2563,51 @@ a {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background-color: #DDD;
|
background-color: #DDD;
|
||||||
|
}
|
||||||
|
|
||||||
|
#p7tbl {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
#p7tbl th, #p7tbl td {
|
||||||
|
text-align: left;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#p7tbl tr:nth-child(n+2):nth-child(odd) {
|
||||||
|
background-color: #cfeeff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#p7tbl .chName {
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#p7tbl .chDescription {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#p7tbl .chSite {
|
||||||
|
width: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#p7tbl .chVersion {
|
||||||
|
width: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#p7tbl .chStatus {
|
||||||
|
width: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#p7tbl .chAction {
|
||||||
|
width: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#addPlugin {
|
||||||
|
background-image: url(../images/plus32.png);
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
float: right;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 12px;
|
||||||
}
|
}
|
|
@ -88,6 +88,9 @@
|
||||||
<div id=LeftMenuMyServer tabindex=0 class="lbbutton" style="display:none" title="My Server" onclick=go(6,event) onkeypress="if (event.key=='Enter') { go(6); }">
|
<div id=LeftMenuMyServer tabindex=0 class="lbbutton" style="display:none" title="My Server" onclick=go(6,event) onkeypress="if (event.key=='Enter') { go(6); }">
|
||||||
<div class="lb6"></div>
|
<div class="lb6"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id=LeftMenuMyPlugins tabindex=0 class="lbbutton" style="display:none" title="My Plugins" onclick=go(7,event) onkeypress="if (event.key=='Enter') { go(7); }">
|
||||||
|
<div class="lb7"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id=topbar class=noselect>
|
<div id=topbar class=noselect>
|
||||||
<div>
|
<div>
|
||||||
|
@ -109,6 +112,7 @@
|
||||||
<td tabindex=0 id=MainMenuMyFiles class="topbar_td style3x" onclick=go(5,event) onkeypress="if (event.key == 'Enter') go(5)">My Files</td>
|
<td tabindex=0 id=MainMenuMyFiles class="topbar_td style3x" onclick=go(5,event) onkeypress="if (event.key == 'Enter') go(5)">My Files</td>
|
||||||
<td tabindex=0 id=MainMenuMyUsers class="topbar_td style3x" onclick=go(4,event) onkeypress="if (event.key == 'Enter') go(4)">My Users</td>
|
<td tabindex=0 id=MainMenuMyUsers class="topbar_td style3x" onclick=go(4,event) onkeypress="if (event.key == 'Enter') go(4)">My Users</td>
|
||||||
<td tabindex=0 id=MainMenuMyServer class="topbar_td style3x" onclick=go(6,event) onkeypress="if (event.key == 'Enter') go(6)">My Server</td>
|
<td tabindex=0 id=MainMenuMyServer class="topbar_td style3x" onclick=go(6,event) onkeypress="if (event.key == 'Enter') go(6)">My Server</td>
|
||||||
|
<td tabindex=0 id=MainMenuMyPlugins class="topbar_td style3x" onclick=go(7,event) onkeypress="if (event.key == 'Enter') go(7)">My Plugins</td>
|
||||||
<td class="topbar_td_end style3"> </td>
|
<td class="topbar_td_end style3"> </td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
@ -405,6 +409,13 @@
|
||||||
<div id="serverStatsTable"></div>
|
<div id="serverStatsTable"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id=p7 style="display:none">
|
||||||
|
<h1>My Plugins</h1>
|
||||||
|
<div id="addPlugin" onclick="return pluginHandler.addPluginDlg();"></div>
|
||||||
|
<table id="p7tbl">
|
||||||
|
<tr><th class="chName">Name</th><th class="chDescription">Description</th><th class="chSite">Link</th><th class="chVersion">Version</th><th class="chStatus">Status</th><th class="chAction">Action</th></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
<div id=p10 style="display:none">
|
<div id=p10 style="display:none">
|
||||||
<table style="width:100%" cellpadding="0" cellspacing="0">
|
<table style="width:100%" cellpadding="0" cellspacing="0">
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -1042,6 +1053,7 @@
|
||||||
var pluginHandlerBuilder = {{{pluginHandler}}};
|
var pluginHandlerBuilder = {{{pluginHandler}}};
|
||||||
var pluginHandler = null;
|
var pluginHandler = null;
|
||||||
if (pluginHandlerBuilder != null) { pluginHandler = new pluginHandlerBuilder(); }
|
if (pluginHandlerBuilder != null) { pluginHandler = new pluginHandlerBuilder(); }
|
||||||
|
var installedPluginList = null;
|
||||||
|
|
||||||
// Console Message Display Timers
|
// Console Message Display Timers
|
||||||
var p11DeskConsoleMsgTimer = null;
|
var p11DeskConsoleMsgTimer = null;
|
||||||
|
@ -1295,6 +1307,7 @@
|
||||||
// Fetch list of meshes, nodes, files
|
// Fetch list of meshes, nodes, files
|
||||||
meshserver.send({ action: 'meshes' });
|
meshserver.send({ action: 'meshes' });
|
||||||
meshserver.send({ action: 'nodes', id: '{{currentNode}}' });
|
meshserver.send({ action: 'nodes', id: '{{currentNode}}' });
|
||||||
|
meshserver.send({ action: 'plugins' });
|
||||||
if ('{{currentNode}}' == '') { meshserver.send({ action: 'files' }); }
|
if ('{{currentNode}}' == '') { meshserver.send({ action: 'files' }); }
|
||||||
if ('{{viewmode}}' == '') { go(1); }
|
if ('{{viewmode}}' == '') { go(1); }
|
||||||
authCookieRenewTimer = setInterval(function () { meshserver.send({ action: 'authcookie' }); }, 1800000); // Request a cookie refresh every 30 minutes.
|
authCookieRenewTimer = setInterval(function () { meshserver.send({ action: 'authcookie' }); }, 1800000); // Request a cookie refresh every 30 minutes.
|
||||||
|
@ -1337,6 +1350,7 @@
|
||||||
QV('p2ServerActionsVersion', siteRights & 16);
|
QV('p2ServerActionsVersion', siteRights & 16);
|
||||||
QV('MainMenuMyFiles', siteRights & 8);
|
QV('MainMenuMyFiles', siteRights & 8);
|
||||||
QV('LeftMenuMyFiles', siteRights & 8);
|
QV('LeftMenuMyFiles', siteRights & 8);
|
||||||
|
QV('MainMenuMyPlugins', (pluginHandler != null));
|
||||||
if (((siteRights & 8) == 0) && (xxcurrentView == 5)) { setDialogMode(0); go(1); }
|
if (((siteRights & 8) == 0) && (xxcurrentView == 5)) { setDialogMode(0); go(1); }
|
||||||
if (currentNode != null) { gotoDevice(currentNode._id, xxcurrentView, true); }
|
if (currentNode != null) { gotoDevice(currentNode._id, xxcurrentView, true); }
|
||||||
|
|
||||||
|
@ -2284,6 +2298,12 @@
|
||||||
//console.log(message.msg);
|
//console.log(message.msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'updatePluginList': {
|
||||||
|
// @Ylianst - Do we need a rights check here?
|
||||||
|
installedPluginList = message.event.list;
|
||||||
|
updatePluginList();
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
//console.log('Unknown message.event.action', message.event.action);
|
//console.log('Unknown message.event.action', message.event.action);
|
||||||
break;
|
break;
|
||||||
|
@ -2337,6 +2357,11 @@
|
||||||
QH('p0span', message.msg);
|
QH('p0span', message.msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'updatePluginList': {
|
||||||
|
installedPluginList = message.list;
|
||||||
|
updatePluginList();
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'plugin': {
|
case 'plugin': {
|
||||||
if ((pluginHandler == null) || (typeof message.plugin != 'string')) break;
|
if ((pluginHandler == null) || (typeof message.plugin != 'string')) break;
|
||||||
try { pluginHandler[message.plugin][message.method](server, message); } catch (e) { console.log('Error loading plugin handler ('+ e + ')'); }
|
try { pluginHandler[message.plugin][message.method](server, message); } catch (e) { console.log('Error loading plugin handler ('+ e + ')'); }
|
||||||
|
@ -9250,6 +9275,9 @@
|
||||||
|
|
||||||
// Remove top bar selection
|
// Remove top bar selection
|
||||||
var mainBarItems = ['MainMenuMyDevices', 'MainMenuMyAccount', 'MainMenuMyEvents', 'MainMenuMyFiles', 'MainMenuMyUsers', 'MainMenuMyServer'];
|
var mainBarItems = ['MainMenuMyDevices', 'MainMenuMyAccount', 'MainMenuMyEvents', 'MainMenuMyFiles', 'MainMenuMyUsers', 'MainMenuMyServer'];
|
||||||
|
if (pluginHandler != null) {
|
||||||
|
mainBarItems.push('MainMenuMyPlugins');
|
||||||
|
}
|
||||||
for (var i in mainBarItems) {
|
for (var i in mainBarItems) {
|
||||||
QC(mainBarItems[i]).remove('fullselect');
|
QC(mainBarItems[i]).remove('fullselect');
|
||||||
QC(mainBarItems[i]).remove('semiselect');
|
QC(mainBarItems[i]).remove('semiselect');
|
||||||
|
@ -9257,6 +9285,9 @@
|
||||||
|
|
||||||
// Remove left bar selection
|
// Remove left bar selection
|
||||||
var leftBarItems = ['LeftMenuMyDevices', 'LeftMenuMyAccount', 'LeftMenuMyEvents', 'LeftMenuMyFiles', 'LeftMenuMyUsers', 'LeftMenuMyServer'];
|
var leftBarItems = ['LeftMenuMyDevices', 'LeftMenuMyAccount', 'LeftMenuMyEvents', 'LeftMenuMyFiles', 'LeftMenuMyUsers', 'LeftMenuMyServer'];
|
||||||
|
if (pluginHandler != null) {
|
||||||
|
leftBarItems.push('LeftMenuMyPlugins');
|
||||||
|
}
|
||||||
for (var i in leftBarItems) {
|
for (var i in leftBarItems) {
|
||||||
QC(leftBarItems[i]).remove('lbbuttonsel');
|
QC(leftBarItems[i]).remove('lbbuttonsel');
|
||||||
QC(leftBarItems[i]).remove('lbbuttonsel2');
|
QC(leftBarItems[i]).remove('lbbuttonsel2');
|
||||||
|
@ -9289,7 +9320,11 @@
|
||||||
// My Server
|
// My Server
|
||||||
if ((x == 6) || (x == 115)) QC('MainMenuMyServer').add(mainMenuActiveClass);
|
if ((x == 6) || (x == 115)) QC('MainMenuMyServer').add(mainMenuActiveClass);
|
||||||
if ((x == 6) || (x == 115) || (x == 40)) QC('LeftMenuMyServer').add(leftMenuActiveClass);
|
if ((x == 6) || (x == 115) || (x == 40)) QC('LeftMenuMyServer').add(leftMenuActiveClass);
|
||||||
|
|
||||||
|
// My Plugins
|
||||||
|
if (x == 7) QC('MainMenuMyPlugins').add(mainMenuActiveClass);
|
||||||
|
if (x == 7) QC('LeftMenuMyPlugins').add(leftMenuActiveClass);
|
||||||
|
|
||||||
// column_l max-height
|
// column_l max-height
|
||||||
if (webPageStackMenu && (x >= 10)) { QC('column_l').add('room4submenu'); } else { QC('column_l').remove('room4submenu'); }
|
if (webPageStackMenu && (x >= 10)) { QC('column_l').add('room4submenu'); } else { QC('column_l').remove('room4submenu'); }
|
||||||
|
|
||||||
|
@ -9333,7 +9368,34 @@
|
||||||
document.title = decodeURIComponent('{{{extitle}}}');
|
document.title = decodeURIComponent('{{{extitle}}}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updatePluginList() {
|
||||||
|
if (installedPluginList.length) {
|
||||||
|
var tr = Q('p7tbl').querySelectorAll(".p7tblRow");
|
||||||
|
if (tr.length) {
|
||||||
|
for (const i in Object.values(tr)) {
|
||||||
|
tr[i].parentNode.removeChild(tr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var statusMap = {
|
||||||
|
0: 'Disabled',
|
||||||
|
1: 'Installed'
|
||||||
|
}
|
||||||
|
var tbl = Q('p7tbl');
|
||||||
|
installedPluginList.forEach(function(p){
|
||||||
|
if (p.hasAdminPanel == true) {
|
||||||
|
p.name = `<a onclick="return goPlugin('${p._id}');">${p.name}</a>`;
|
||||||
|
}
|
||||||
|
p.status = statusMap[p.status];
|
||||||
|
p.actions = 'TODO'; // Install / Upgrade / Disable / Delete
|
||||||
|
let tpl = `<td>${p.name}</td><td>${p.description}</td><td><a href="${p.homepage}" target="_blank">Homepage</a></td><td>${p.version}</td><td>${p.status}</td><td>${p.actions}</td>`;
|
||||||
|
let tr = tbl.insertRow(-1);
|
||||||
|
tr.innerHTML = tpl;
|
||||||
|
tr.classList.add('p7tblRow');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
// Generic methods
|
// Generic methods
|
||||||
function joinPaths() { var x = []; for (var i in arguments) { var w = arguments[i]; if ((w != null) && (w != '')) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } x.push(w); } } return x.join('/'); }
|
function joinPaths() { var x = []; for (var i in arguments) { var w = arguments[i]; if ((w != null) && (w != '')) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } x.push(w); } } return x.join('/'); }
|
||||||
function putstore(name, val) {
|
function putstore(name, val) {
|
||||||
|
|
Loading…
Reference in New Issue