diff --git a/common.js b/common.js index 63f1ff21..29055817 100644 --- a/common.js +++ b/common.js @@ -130,5 +130,6 @@ module.exports.objKeysToLower = function (obj) { // Validation methods module.exports.validateString = function(str, minlen, maxlen) { return ((str != null) && (typeof str == 'string') && ((minlen == null) || (str.length >= minlen)) && ((maxlen == null) || (str.length <= maxlen))); } module.exports.validateInt = function(int, minval, maxval) { return ((int != null) && (typeof int == 'number') && ((minval == null) || (int >= minval)) && ((maxval == null) || (int <= maxval))); } -module.exports.validateArray = function(array, minlen, maxlen) { return ((array != null) && Array.isArray(array) && ((minlen == null) || (array.length >= minlen)) && ((maxlen == null) || (array.length <= maxlen))); } +module.exports.validateArray = function (array, minlen, maxlen) { return ((array != null) && Array.isArray(array) && ((minlen == null) || (array.length >= minlen)) && ((maxlen == null) || (array.length <= maxlen))); } +module.exports.validateStrArray = function (array, minlen, maxlen) { if (((array != null) && Array.isArray(array)) == false) return false; for (var i in array) { if ((typeof array[i] != 'string') && ((minlen == null) || (array[i].length >= minlen)) && ((maxlen == null) || (array[i].length <= maxlen))) return false; } return true; } module.exports.validateObject = function(obj) { return ((obj != null) && (typeof obj == 'object')); } diff --git a/meshuser.js b/meshuser.js index d02f9e0a..00df9846 100644 --- a/meshuser.js +++ b/meshuser.js @@ -976,6 +976,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { if ((command.intelamt.user != null) && (command.intelamt.pass != undefined) && ((command.intelamt.user != node.intelamt.user) || (command.intelamt.pass != node.intelamt.pass))) { change = 1; node.intelamt.user = command.intelamt.user; node.intelamt.pass = command.intelamt.pass; changes.push('Intel AMT credentials'); } if (command.intelamt.tls && (command.intelamt.tls != node.intelamt.tls)) { change = 1; node.intelamt.tls = command.intelamt.tls; changes.push('Intel AMT TLS'); } } + if (command.tags) { // Node grouping tag, this is a array of strings that can't be empty and can't contain a comma + var ok = true; + if (obj.common.validateString(command.tags, 0, 4096) == true) { command.tags = command.tags.split(','); } + if (obj.common.validateStrArray(command.tags, 1, 256) == true) { var groupTags = command.tags; for (var i in groupTags) { groupTags[i] = groupTags[i].trim(); if ((groupTags[i] == '') || (groupTags[i].indexOf(',') >= 0)) { ok = false; } } } + if (ok == true) { groupTags.sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); node.tags = groupTags; change = 1; } + } else if ((command.tags === '') && node.tags) { delete node.tags; change = 1; } if (change == 1) { // Save the node diff --git a/package.json b/package.json index 54588d96..f5b27324 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.1.7-a", + "version": "0.1.7-b", "keywords": [ "Remote Management", "Intel AMT", diff --git a/views/default.handlebars b/views/default.handlebars index 0a663b67..accf94cd 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -155,6 +155,7 @@ + @@ -1284,6 +1285,7 @@ node.iploc = message.event.node.iploc; node.wifiloc = message.event.node.wifiloc; node.gpsloc = message.event.node.gpsloc; + node.tags = message.event.node.tags; node.userloc = message.event.node.userloc; if (message.event.node.agent != null) { if (node.agent == null) node.agent = {}; @@ -1476,17 +1478,16 @@ var deviceHeaders = {}; var oldviewmode = 0; function updateDevices() { - var r = '', c = 0, current = null, count = 0, displayedMeshes = {}, view = Q('viewselect').value; + var r = '', c = 0, current = null, count = 0, displayedMeshes = {}, view = Q('viewselect').value, groups = {}, groupCount = {}; QV('xdevices', view < 4); QV('xdevicesmap', view == 4); QV('devListToolbar', view < 3); QV('kvmListToolbar', view == 3); QV('devMapToolbar', view == 4); - QV('devListToolbarSort', view < 4); QV('devListToolbarSize', view == 3); QV('NoMeshesPanel', meshcount == 0); QV('devListToolbarView', (meshcount != 0) && (nodes.length > 0)); - QV('devListToolbarSort', (meshcount != 0) && (nodes.length > 0)); + QV('devListToolbarSort', (meshcount != 0) && (nodes.length > 0) && (view < 4)); if ((meshcount == 0) || (nodes.length == 0)) { view = 1; } if (view == 4) { setTimeout( function() { if (xxmap.map != null) { xxmap.map.updateSize(); } }, 200); @@ -1572,10 +1573,41 @@ } } + // If we are displaying devices by group, put the device in the right group. + if ((sort == 3) && (r != '')) { + if (nodes[i].tags) { + for (var j in nodes[i].tags) { + var tag = nodes[i].tags[j]; + if (groups[tag] == null) { groups[tag] = r; groupCount[tag] = 1; } else { groups[tag] += r; groupCount[tag] += 1; } + if (view == 3) break; + } + } + r = ''; + } + deviceHeaderTotal++; if (typeof deviceHeaderCount[nodes[i].state] == 'undefined') { deviceHeaderCount[nodes[i].state] = 1; } else { deviceHeaderCount[nodes[i].state]++; } } + // If displaying devices by groups, sort the group names and display the devices. + if (sort == 3) { + var groupNames = []; + for (var i in groups) { groupNames.push(i); } + groupNames.sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); + for (var j in groupNames) { + var i = groupNames[j]; r += '