summaryrefslogtreecommitdiffhomepage
path: root/modules/luci-mod-status/htdocs/luci-static/resources
diff options
context:
space:
mode:
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.js105
-rw-r--r--modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js309
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('-');