diff options
Diffstat (limited to 'modules/luci-mod-status/htdocs/luci-static/resources')
-rw-r--r-- | modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/40_dhcp.js | 105 | ||||
-rw-r--r-- | modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js | 309 |
2 files changed, 306 insertions, 108 deletions
diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/40_dhcp.js b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/40_dhcp.js index 603c0d93aa..36452cd062 100644 --- a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/40_dhcp.js +++ b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/40_dhcp.js @@ -1,7 +1,9 @@ 'use strict'; 'require baseclass'; 'require rpc'; +'require uci'; 'require network'; +'require validation'; var callLuciDHCPLeases = rpc.declare({ object: 'luci-rpc', @@ -12,29 +14,85 @@ var callLuciDHCPLeases = rpc.declare({ return baseclass.extend({ title: '', + isMACStatic: {}, + isDUIDStatic: {}, + load: function() { return Promise.all([ callLuciDHCPLeases(), - network.getHostHints() + network.getHostHints(), + uci.load('dhcp') ]); }, + handleCreateStaticLease: function(lease, ev) { + ev.currentTarget.classList.add('spinning'); + ev.currentTarget.disabled = true; + ev.currentTarget.blur(); + + var cfg = uci.add('dhcp', 'host'); + uci.set('dhcp', cfg, 'name', lease.hostname || '-'); + uci.set('dhcp', cfg, 'ip', lease.ipaddr); + uci.set('dhcp', cfg, 'mac', lease.macaddr.toUpperCase()); + + return uci.save() + .then(L.bind(L.ui.changes.init, L.ui.changes)) + .then(L.bind(L.ui.changes.displayChanges, L.ui.changes)); + }, + + handleCreateStaticLease6: function(lease, ev) { + ev.currentTarget.classList.add('spinning'); + ev.currentTarget.disabled = true; + ev.currentTarget.blur(); + + var cfg = uci.add('dhcp', 'host'), + ip6arr = lease.ip6addrs[0] ? validation.parseIPv6(lease.ip6addrs[0]) : null; + + uci.set('dhcp', cfg, 'name', lease.hostname || '-'); + uci.set('dhcp', cfg, 'duid', lease.duid.toUpperCase()); + uci.set('dhcp', cfg, 'mac', lease.macaddr); + if (ip6arr) + uci.set('dhcp', cfg, 'hostid', (ip6arr[6] * 0xFFFF + ip6arr[7]).toString(16)); + + return uci.save() + .then(L.bind(L.ui.changes.init, L.ui.changes)) + .then(L.bind(L.ui.changes.displayChanges, L.ui.changes)); + }, + renderLeases: function(data) { var leases = Array.isArray(data[0].dhcp_leases) ? data[0].dhcp_leases : [], leases6 = Array.isArray(data[0].dhcp6_leases) ? data[0].dhcp6_leases : [], - machints = data[1].getMACHints(false); + machints = data[1].getMACHints(false), + hosts = uci.sections('dhcp', 'host'); + + for (var i = 0; i < hosts.length; i++) { + var host = hosts[i]; + + if (host.mac) { + var macs = L.toArray(host.mac); + for (var j = 0; j < macs.length; j++) { + var mac = macs[j].toUpperCase(); + this.isMACStatic[mac] = true; + } + } + if (host.duid) { + var duid = host.duid.toUpperCase(); + this.isDUIDStatic[duid] = true; + } + }; var table = E('div', { 'class': 'table lases' }, [ E('div', { 'class': 'tr table-titles' }, [ E('div', { 'class': 'th' }, _('Hostname')), E('div', { 'class': 'th' }, _('IPv4-Address')), E('div', { 'class': 'th' }, _('MAC-Address')), - E('div', { 'class': 'th' }, _('Lease time remaining')) + E('div', { 'class': 'th' }, _('Lease time remaining')), + E('div', { 'class': 'th cbi-section-actions' }, _('Static Lease')) ]) ]); - cbi_update_table(table, leases.map(function(lease) { - var exp; + cbi_update_table(table, leases.map(L.bind(function(lease) { + var exp, rows; if (lease.expires === false) exp = E('em', _('unlimited')); @@ -43,25 +101,37 @@ return baseclass.extend({ else exp = '%t'.format(lease.expires); - return [ + rows = [ lease.hostname || '-', lease.ipaddr, lease.macaddr, exp ]; - }), E('em', _('There are no active leases'))); + + if (lease.macaddr != null) { + var mac = lease.macaddr.toUpperCase(); + rows.push(E('button', { + 'class': 'cbi-button cbi-button-apply', + 'click': L.bind(this.handleCreateStaticLease, this, lease), + 'disabled': this.isMACStatic[mac] + }, [ _('Set Static') ])); + } + + return rows; + }, this)), E('em', _('There are no active leases'))); var table6 = E('div', { 'class': 'table leases6' }, [ E('div', { 'class': 'tr table-titles' }, [ E('div', { 'class': 'th' }, _('Host')), E('div', { 'class': 'th' }, _('IPv6-Address')), E('div', { 'class': 'th' }, _('DUID')), - E('div', { 'class': 'th' }, _('Lease time remaining')) + E('div', { 'class': 'th' }, _('Lease time remaining')), + E('div', { 'class': 'th cbi-section-actions' }, _('Static Lease')) ]) ]); - cbi_update_table(table6, leases6.map(function(lease) { - var exp; + cbi_update_table(table6, leases6.map(L.bind(function(lease) { + var exp, rows; if (lease.expires === false) exp = E('em', _('unlimited')); @@ -80,13 +150,24 @@ return baseclass.extend({ else if (hint) host = hint[1]; - return [ + rows = [ host || '-', lease.ip6addrs ? lease.ip6addrs.join(' ') : lease.ip6addr, lease.duid, exp ]; - }), E('em', _('There are no active leases'))); + + if (lease.duid != null) { + var duid = lease.duid.toUpperCase(); + rows.push(E('button', { + 'class': 'cbi-button cbi-button-apply', + 'click': L.bind(this.handleCreateStaticLease6, this, lease), + 'disabled': this.isDUIDStatic[duid] + }, [ _('Set Static') ])); + } + + return rows; + }, this)), E('em', _('There are no active leases'))); return E([ E('h3', _('Active DHCP Leases')), diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js index deb6f8609f..1019365836 100644 --- a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js +++ b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js @@ -2,120 +2,210 @@ 'require baseclass'; 'require dom'; 'require network'; +'require uci'; +'require fs'; 'require rpc'; -var callSessionAccess = rpc.declare({ - object: 'session', - method: 'access', - params: [ 'scope', 'object', 'function' ], - expect: { 'access': false } -}); - -function renderbox(radio, networks) { - var chan = null, - freq = null, - rate = null, - badges = []; - - for (var i = 0; i < networks.length; i++) { - var net = networks[i], - is_assoc = (net.getBSSID() != '00:00:00:00:00:00' && net.getChannel() && !net.isDisabled()), - quality = net.getSignalPercent(); - - var icon; - if (net.isDisabled()) - icon = L.resource('icons/signal-none.png'); - else if (quality <= 0) - icon = L.resource('icons/signal-0.png'); - else if (quality < 25) - icon = L.resource('icons/signal-0-25.png'); - else if (quality < 50) - icon = L.resource('icons/signal-25-50.png'); - else if (quality < 75) - icon = L.resource('icons/signal-50-75.png'); - else - icon = L.resource('icons/signal-75-100.png'); - - var badge = renderBadge( - icon, - '%s: %d dBm / %s: %d%%'.format(_('Signal'), net.getSignal(), _('Quality'), quality), - _('SSID'), net.getActiveSSID() || '?', - _('Mode'), net.getActiveMode(), - _('BSSID'), is_assoc ? (net.getActiveBSSID() || '-') : null, - _('Encryption'), is_assoc ? net.getActiveEncryption() : null, - _('Associations'), is_assoc ? (net.assoclist.length || '-') : null, - null, is_assoc ? null : E('em', net.isDisabled() ? _('Wireless is disabled') : _('Wireless is not associated'))); - - badges.push(badge); - - chan = (chan != null) ? chan : net.getChannel(); - freq = (freq != null) ? freq : net.getFrequency(); - rate = (rate != null) ? rate : net.getBitRate(); - } +return baseclass.extend({ + title: _('Wireless'), - return E('div', { class: 'ifacebox' }, [ - E('div', { class: 'ifacebox-head center ' + (radio.isUp() ? 'active' : '') }, - E('strong', radio.getName())), - E('div', { class: 'ifacebox-body left' }, [ - L.itemlist(E('span'), [ - _('Type'), radio.getI18n().replace(/^Generic | Wireless Controller .+$/g, ''), - _('Channel'), chan ? '%d (%.3f %s)'.format(chan, freq, _('GHz')) : '-', - _('Bitrate'), rate ? '%d %s'.format(rate, _('Mbit/s')) : '-' - ]), - E('div', {}, badges) - ]) - ]); -} - -function wifirate(rt) { - var s = '%.1f\xa0%s, %d\xa0%s'.format(rt.rate / 1000, _('Mbit/s'), rt.mhz, _('MHz')), - ht = rt.ht, vht = rt.vht, - mhz = rt.mhz, nss = rt.nss, - mcs = rt.mcs, sgi = rt.short_gi; - - if (ht || vht) { - if (vht) s += ', VHT-MCS\xa0%d'.format(mcs); - if (nss) s += ', VHT-NSS\xa0%d'.format(nss); - if (ht) s += ', MCS\xa0%s'.format(mcs); - if (sgi) s += ', ' + _('Short GI').replace(/ /g, '\xa0'); - } + WPSTranslateTbl: { + Disabled: _('Disabled'), + Active: _('Active'), + 'Timed-out': _('Timed-out'), + Overlap: _('Overlap'), + Unknown: _('Unknown') + }, - return s; -} + callSessionAccess: rpc.declare({ + object: 'session', + method: 'access', + params: [ 'scope', 'object', 'function' ], + expect: { 'access': false } + }), + + wifirate: function(rt) { + var s = '%.1f\xa0%s, %d\xa0%s'.format(rt.rate / 1000, _('Mbit/s'), rt.mhz, _('MHz')), + ht = rt.ht, vht = rt.vht, + mhz = rt.mhz, nss = rt.nss, + mcs = rt.mcs, sgi = rt.short_gi; + + if (ht || vht) { + if (vht) s += ', VHT-MCS\xa0%d'.format(mcs); + if (nss) s += ', VHT-NSS\xa0%d'.format(nss); + if (ht) s += ', MCS\xa0%s'.format(mcs); + if (sgi) s += ', ' + _('Short GI').replace(/ /g, '\xa0'); + } + + return s; + }, -return baseclass.extend({ - title: _('Wireless'), + handleDelClient: function(wifinet, mac, ev, cmd) { + var exec = cmd || 'disconnect'; - handleDelClient: function(wifinet, mac, ev) { dom.parent(ev.currentTarget, '.tr').style.opacity = 0.5; ev.currentTarget.classList.add('spinning'); ev.currentTarget.disabled = true; ev.currentTarget.blur(); + if (exec == 'addlist') { + var macs = [ mac ] + + for (var mac in this.iface_maclist) { + macs.push(mac) + } + + uci.set('wireless', wifinet.sid, 'maclist', macs); + + return uci.save() + .then(L.bind(L.ui.changes.init, L.ui.changes)) + .then(L.bind(L.ui.changes.displayChanges, L.ui.changes)); + } + wifinet.disconnectClient(mac, true, 5, 60000); }, + handleGetWPSStatus: function(wifinet) { + return rpc.declare({ + object: 'hostapd.%s'.format(wifinet), + method: 'wps_status', + })() + }, + + handleCallWPS: function(wifinet, ev) { + ev.currentTarget.classList.add('spinning'); + ev.currentTarget.disabled = true; + ev.currentTarget.blur(); + + return rpc.declare({ + object: 'hostapd.%s'.format(wifinet), + method: 'wps_start', + })(); + }, + + handleCancelWPS: function(wifinet, ev) { + ev.currentTarget.classList.add('spinning'); + ev.currentTarget.disabled = true; + ev.currentTarget.blur(); + + return rpc.declare({ + object: 'hostapd.%s'.format(wifinet), + method: 'wps_cancel', + })(); + }, + + renderbox: function(radio, networks) { + var chan = null, + freq = null, + rate = null, + badges = []; + + for (var i = 0; i < networks.length; i++) { + var net = networks[i], + is_assoc = (net.getBSSID() != '00:00:00:00:00:00' && net.getChannel() && !net.isDisabled()), + quality = net.getSignalPercent(); + + var icon; + if (net.isDisabled()) + icon = L.resource('icons/signal-none.png'); + else if (quality <= 0) + icon = L.resource('icons/signal-0.png'); + else if (quality < 25) + icon = L.resource('icons/signal-0-25.png'); + else if (quality < 50) + icon = L.resource('icons/signal-25-50.png'); + else if (quality < 75) + icon = L.resource('icons/signal-50-75.png'); + else + icon = L.resource('icons/signal-75-100.png'); + + var WPS_button; + + if (this.isWPSEnabled[net.sid]) { + if (net.wps_status == 'Active') { + WPS_button = E('button', { + 'class' : 'cbi-button cbi-button-remove', + 'click': L.bind(this.handleCancelWPS, this, net.getIfname()), + }, [ _('Stop WPS') ]) + } else { + WPS_button = E('button', { + 'class' : 'cbi-button cbi-button-apply', + 'click': L.bind(this.handleCallWPS, this, net.getIfname()), + }, [ _('Start WPS') ]) + } + } + + var badge = renderBadge( + icon, + '%s: %d dBm / %s: %d%%'.format(_('Signal'), net.getSignal(), _('Quality'), quality), + _('SSID'), net.getActiveSSID() || '?', + _('Mode'), net.getActiveMode(), + _('BSSID'), is_assoc ? (net.getActiveBSSID() || '-') : null, + _('Encryption'), is_assoc ? net.getActiveEncryption() : null, + _('Associations'), is_assoc ? (net.assoclist.length || '-') : null, + null, is_assoc ? null : E('em', net.isDisabled() ? _('Wireless is disabled') : _('Wireless is not associated')), + _('WPS status'), this.WPSTranslateTbl[net.wps_status], + '', WPS_button + ); + + badges.push(badge); + + chan = (chan != null) ? chan : net.getChannel(); + freq = (freq != null) ? freq : net.getFrequency(); + rate = (rate != null) ? rate : net.getBitRate(); + } + + return E('div', { class: 'ifacebox' }, [ + E('div', { class: 'ifacebox-head center ' + (radio.isUp() ? 'active' : '') }, + E('strong', radio.getName())), + E('div', { class: 'ifacebox-body left' }, [ + L.itemlist(E('span'), [ + _('Type'), radio.getI18n().replace(/^Generic | Wireless Controller .+$/g, ''), + _('Channel'), chan ? '%d (%.3f %s)'.format(chan, freq, _('GHz')) : '-', + _('Bitrate'), rate ? '%d %s'.format(rate, _('Mbit/s')) : '-', + ]), + E('div', {}, badges) + ]) + ]); + }, + + isWPSEnabled: {}, + load: function() { return Promise.all([ network.getWifiDevices(), network.getWifiNetworks(), network.getHostHints(), - callSessionAccess('access-group', 'luci-mod-status-index-wifi', 'read'), - callSessionAccess('access-group', 'luci-mod-status-index-wifi', 'write') - ]).then(function(radios_networks_hints) { - var tasks = []; - - for (var i = 0; i < radios_networks_hints[1].length; i++) - tasks.push(L.resolveDefault(radios_networks_hints[1][i].getAssocList(), []).then(L.bind(function(net, list) { + this.callSessionAccess('access-group', 'luci-mod-status-index-wifi', 'read'), + this.callSessionAccess('access-group', 'luci-mod-status-index-wifi', 'write'), + uci.load('wireless') + ]).then(L.bind(function(data) { + var tasks = [], + radios_networks_hints = data[1], + hasWPS = L.hasSystemFeature('hostapd', 'wps'); + + for (var i = 0; i < radios_networks_hints.length; i++) { + tasks.push(L.resolveDefault(radios_networks_hints[i].getAssocList(), []).then(L.bind(function(net, list) { net.assoclist = list.sort(function(a, b) { return a.mac > b.mac }); - }, this, radios_networks_hints[1][i]))); + }, this, radios_networks_hints[i]))); + + if (hasWPS && uci.get('wireless', radios_networks_hints[i].sid, 'wps_pushbutton') == '1') { + this.isWPSEnabled[radios_networks_hints[i].sid] = true; + tasks.push(L.resolveDefault(this.handleGetWPSStatus(radios_networks_hints[i].getIfname()), null) + .then(L.bind(function(net, data) { + net.wps_status = data ? data.pbc_status : _('No Data'); + }, this, radios_networks_hints[i]))); + } + } return Promise.all(tasks).then(function() { - return radios_networks_hints; + return data; }); - }); + }, this)); }, + isDeviceAdded: {}, + render: function(data) { var seen = {}, radios = data[0], @@ -127,7 +217,7 @@ return baseclass.extend({ var table = E('div', { 'class': 'network-status-table' }); for (var i = 0; i < radios.sort(function(a, b) { a.getName() > b.getName() }).length; i++) - table.appendChild(renderbox(radios[i], + table.appendChild(this.renderbox(radios[i], networks.filter(function(net) { return net.getWifiDeviceName() == radios[i].getName() }))); if (!table.lastElementChild) @@ -146,6 +236,17 @@ return baseclass.extend({ var rows = []; for (var i = 0; i < networks.length; i++) { + var macfilter = uci.get('wireless', networks[i].sid, 'macfilter'); + + if (macfilter != null && macfilter != 'disable') { + this.isDeviceAdded = {}; + var macs = L.toArray(uci.get('wireless', networks[i].sid, 'maclist')); + for (var j = 0; j < macs.length; j++) { + var mac = macs[j].toUpperCase(); + this.isDeviceAdded[mac] = true; + } + } + for (var k = 0; k < networks[i].assoclist.length; k++) { var bss = networks[i].assoclist[k], name = hosthints.getHostnameByMACAddr(bss.mac), @@ -215,9 +316,9 @@ return baseclass.extend({ ]) ]), E('span', {}, [ - E('span', wifirate(bss.rx)), + E('span', this.wifirate(bss.rx)), E('br'), - E('span', wifirate(bss.tx)) + E('span', this.wifirate(bss.tx)) ]) ]; @@ -225,10 +326,26 @@ return baseclass.extend({ if (assoclist.firstElementChild.childNodes.length < 6) assoclist.firstElementChild.appendChild(E('div', { 'class': 'th cbi-section-actions' })); - row.push(E('button', { - 'class': 'cbi-button cbi-button-remove', - 'click': L.bind(this.handleDelClient, this, networks[i], bss.mac) - }, [ _('Disconnect') ])); + if (macfilter != null && macfilter != 'disable' && !this.isDeviceAdded[bss.mac]) { + row.push(new L.ui.ComboButton('button', { + 'addlist': macfilter == 'allow' ? _('Add to Whitelist') : _('Add to Blacklist'), + 'disconnect': _('Disconnect') + }, { + 'click': L.bind(this.handleDelClient, this, networks[i], bss.mac), + 'sort': [ 'disconnect', 'addlist' ], + 'classes': { + 'addlist': 'btn cbi-button cbi-button-remove', + 'disconnect': 'btn cbi-button cbi-button-remove' + } + }).render() + ) + } + else { + row.push(E('button', { + 'class': 'cbi-button cbi-button-remove', + 'click': L.bind(this.handleDelClient, this, networks[i], bss.mac) + }, [ _('Disconnect') ])); + } } else { row.push('-'); |