summaryrefslogtreecommitdiffhomepage
path: root/modules/luci-mod-system
diff options
context:
space:
mode:
Diffstat (limited to 'modules/luci-mod-system')
-rw-r--r--modules/luci-mod-system/Makefile6
-rw-r--r--modules/luci-mod-system/htdocs/luci-static/resources/view/system/crontab.js9
-rw-r--r--modules/luci-mod-system/htdocs/luci-static/resources/view/system/dropbear.js3
-rw-r--r--modules/luci-mod-system/htdocs/luci-static/resources/view/system/flash.js16
-rw-r--r--modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/default-on.js21
-rw-r--r--modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/heartbeat.js10
-rw-r--r--modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/netdev.js27
-rw-r--r--modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/none.js10
-rw-r--r--modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/timer.js19
-rw-r--r--modules/luci-mod-system/htdocs/luci-static/resources/view/system/leds.js181
-rw-r--r--modules/luci-mod-system/htdocs/luci-static/resources/view/system/mounts.js6
-rw-r--r--modules/luci-mod-system/htdocs/luci-static/resources/view/system/password.js12
-rw-r--r--modules/luci-mod-system/htdocs/luci-static/resources/view/system/reboot.js3
-rw-r--r--modules/luci-mod-system/htdocs/luci-static/resources/view/system/sshkeys.js22
-rw-r--r--modules/luci-mod-system/htdocs/luci-static/resources/view/system/startup.js36
-rw-r--r--modules/luci-mod-system/htdocs/luci-static/resources/view/system/system.js54
-rw-r--r--modules/luci-mod-system/luasrc/controller/admin/system.lua31
-rw-r--r--modules/luci-mod-system/root/usr/share/luci/menu.d/luci-mod-system.json136
-rw-r--r--modules/luci-mod-system/root/usr/share/rpcd/acl.d/luci-mod-system.json167
19 files changed, 548 insertions, 221 deletions
diff --git a/modules/luci-mod-system/Makefile b/modules/luci-mod-system/Makefile
index a6d5a7a456..f0ca7987ed 100644
--- a/modules/luci-mod-system/Makefile
+++ b/modules/luci-mod-system/Makefile
@@ -7,7 +7,11 @@
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI Administration - Global System Settings
-LUCI_DEPENDS:=+luci-base
+LUCI_DEPENDS:=+luci-base \
+ +kmod-ledtrig-default-on \
+ +kmod-ledtrig-heartbeat \
+ +kmod-ledtrig-netdev \
+ +kmod-ledtrig-timer
PKG_LICENSE:=Apache-2.0
diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/crontab.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/crontab.js
index c2ab770c51..939d41b0ec 100644
--- a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/crontab.js
+++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/crontab.js
@@ -1,8 +1,11 @@
'use strict';
+'require view';
'require fs';
'require ui';
-return L.view.extend({
+var isReadonlyView = !L.hasViewPermission() || null;
+
+return view.extend({
load: function() {
return L.resolveDefault(fs.read('/etc/crontabs/root'), '');
},
@@ -21,10 +24,10 @@ return L.view.extend({
render: function(crontab) {
return E([
E('h2', _('Scheduled Tasks')),
- E('p', {},
+ E('p', { 'class': 'cbi-section-descr' },
_('This is the system crontab in which scheduled tasks can be defined.') +
_('<br/>Note: you need to manually restart the cron service if the crontab file was empty before editing.')),
- E('p', {}, E('textarea', { 'style': 'width:100%', 'rows': 10 }, [ crontab != null ? crontab : '' ]))
+ E('p', {}, E('textarea', { 'style': 'width:100%', 'rows': 10, 'disabled': isReadonlyView }, [ crontab != null ? crontab : '' ]))
]);
},
diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/dropbear.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/dropbear.js
index 7a8b1428d5..6ee801abc2 100644
--- a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/dropbear.js
+++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/dropbear.js
@@ -1,8 +1,9 @@
'use strict';
+'require view';
'require form';
'require tools.widgets as widgets';
-return L.view.extend({
+return view.extend({
render: function() {
var m, s, o;
diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/flash.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/flash.js
index 05e1434f15..4a1058d0d9 100644
--- a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/flash.js
+++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/flash.js
@@ -1,9 +1,13 @@
'use strict';
+'require view';
+'require dom';
'require form';
'require rpc';
'require fs';
'require ui';
+var isReadonlyView = !L.hasViewPermission();
+
var callSystemValidateFirmwareImage = rpc.declare({
object: 'system',
method: 'validate_firmware_image',
@@ -60,7 +64,7 @@ function findStorageSize(procmtd, procpart) {
var mapdata = { actions: {}, config: {} };
-return L.view.extend({
+return view.extend({
load: function() {
var tasks = [
L.resolveDefault(fs.stat('/lib/upgrade/platform.sh'), {}),
@@ -76,7 +80,7 @@ return L.view.extend({
handleBackup: function(ev) {
var form = E('form', {
method: 'post',
- action: '/cgi-bin/cgi-backup',
+ action: L.env.cgi_base + '/cgi-backup',
enctype: 'application/x-www-form-urlencoded'
}, E('input', { type: 'hidden', name: 'sessionid', value: rpc.getSessionID() }));
@@ -166,10 +170,10 @@ return L.view.extend({
},
handleBlock: function(hostname, ev) {
- var mtdblock = L.dom.parent(ev.target, '.cbi-section').querySelector('[data-name="mtdselect"] select').value;
+ var mtdblock = dom.parent(ev.target, '.cbi-section').querySelector('[data-name="mtdselect"] select').value;
var form = E('form', {
'method': 'post',
- 'action': '/cgi-bin/cgi-download',
+ 'action': L.env.cgi_base + '/cgi-download',
'enctype': 'application/x-www-form-urlencoded'
}, [
E('input', { 'type': 'hidden', 'name': 'sessionid', 'value': rpc.getSessionID() }),
@@ -351,6 +355,7 @@ return L.view.extend({
m = new form.JSONMap(mapdata, _('Flash operations'));
m.tabbed = true;
+ m.readonly = isReadonlyView;
s = m.section(form.NamedSection, 'actions', _('Actions'));
@@ -420,7 +425,8 @@ return L.view.extend({
node.appendChild(E('div', { 'class': 'cbi-page-actions' }, [
E('button', {
'class': 'cbi-button cbi-button-save',
- 'click': ui.createHandlerFn(view, 'handleBackupSave', this.map)
+ 'click': ui.createHandlerFn(view, 'handleBackupSave', this.map),
+ 'disabled': isReadonlyView || null
}, [ _('Save') ])
]));
diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/default-on.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/default-on.js
new file mode 100644
index 0000000000..ba7b00ede3
--- /dev/null
+++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/default-on.js
@@ -0,0 +1,21 @@
+'use strict';
+'require baseclass';
+'require form';
+
+return baseclass.extend({
+ trigger: _('Always on (kernel: default-on)'),
+ kernel: true,
+ addFormOptions(s){
+ var o;
+
+ o = s.option(form.Flag, 'default', _('Default state'));
+ o.rmempty = false;
+ o.depends('trigger', 'default-on');
+ o.textvalue = function(section_id) {
+ var cval = this.cfgvalue(section_id);
+ if (cval == null)
+ cval = this.default;
+ return (cval == this.enabled) ? _('On') : _('Off');
+ };
+ }
+});
diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/heartbeat.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/heartbeat.js
new file mode 100644
index 0000000000..9050063bf2
--- /dev/null
+++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/heartbeat.js
@@ -0,0 +1,10 @@
+'use strict';
+'require baseclass';
+
+return baseclass.extend({
+ trigger: _('Heartbeat interval (kernel: heartbeat)'),
+ kernel: true,
+ addFormOptions(s){
+ var o;
+ }
+});
diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/netdev.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/netdev.js
new file mode 100644
index 0000000000..692c887ea4
--- /dev/null
+++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/netdev.js
@@ -0,0 +1,27 @@
+'use strict';
+'require baseclass';
+'require form';
+'require tools.widgets as widgets';
+
+return baseclass.extend({
+ trigger: _("Network device activity (kernel: netdev)"),
+ kernel: true,
+ addFormOptions(s){
+ var o;
+
+ o = s.option(widgets.DeviceSelect, '_net_dev', _('Device'));
+ o.rmempty = true;
+ o.ucioption = 'dev';
+ o.modalonly = true;
+ o.noaliases = true;
+ o.depends('trigger', 'netdev');
+
+ o = s.option(form.MultiValue, 'mode', _('Trigger Mode'));
+ o.rmempty = true;
+ o.modalonly = true;
+ o.depends('trigger', 'netdev');
+ o.value('link', _('Link On'));
+ o.value('tx', _('Transmit'));
+ o.value('rx', _('Receive'));
+ }
+});
diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/none.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/none.js
new file mode 100644
index 0000000000..ef21adc7dc
--- /dev/null
+++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/none.js
@@ -0,0 +1,10 @@
+'use strict';
+'require baseclass';
+
+return baseclass.extend({
+ trigger: _('Always off (kernel: none)'),
+ kernel: true,
+ addFormOptions(s){
+ var o;
+ }
+});
diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/timer.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/timer.js
new file mode 100644
index 0000000000..1ec362b601
--- /dev/null
+++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/timer.js
@@ -0,0 +1,19 @@
+'use strict';
+'require baseclass';
+'require form';
+
+return baseclass.extend({
+ trigger: _('Custom flash interval (kernel: timer)'),
+ kernel: true,
+ addFormOptions(s){
+ var o;
+
+ o = s.option(form.Value, 'delayon', _('On-State Delay'));
+ o.modalonly = true;
+ o.depends('trigger', 'timer');
+
+ o = s.option(form.Value, 'delayoff', _('Off-State Delay'));
+ o.modalonly = true;
+ o.depends('trigger', 'timer');
+ }
+});
diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/leds.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/leds.js
index edd4a24e06..9fe1bff55e 100644
--- a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/leds.js
+++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/leds.js
@@ -1,40 +1,58 @@
'use strict';
+'require view';
'require uci';
'require rpc';
'require form';
-'require tools.widgets as widgets';
+'require fs';
-var callLeds, callUSB;
-
-callLeds = rpc.declare({
+var callLeds = rpc.declare({
object: 'luci',
method: 'getLEDs',
expect: { '': {} }
});
-callUSB = rpc.declare({
- object: 'luci',
- method: 'getUSBDevices',
- expect: { '': {} }
-});
-
-return L.view.extend({
+return view.extend({
load: function() {
return Promise.all([
callLeds(),
- callUSB()
- ]);
+ L.resolveDefault(fs.list('/www' + L.resource('view/system/led-trigger')), [])
+ ]).then(function(data) {
+ var plugins = data[1];
+ var tasks = [];
+
+ for (var i = 0; i < plugins.length; i++) {
+ var m = plugins[i].name.match(/^(.+)\.js$/);
+
+ if (plugins[i].type != 'file' || m == null)
+ continue;
+
+ tasks.push(L.require('view.system.led-trigger.' + m[1]).then(L.bind(function(name){
+ return L.resolveDefault(L.require('view.system.led-trigger.' + name)).then(function(form) {
+ return {
+ name: name,
+ form: form,
+ };
+ });
+ }, this, m[1])));
+ }
+
+ return Promise.all(tasks).then(function(plugins) {
+ var value = {};
+ value[0] = data[0];
+ value[1] = plugins;
+ return value;
+ });
+ });
},
- render: function(results) {
- var leds = results[0],
- usb = results[1],
- triggers = {},
- m, s, o;
+ render: function(data) {
+ var m, s, o, triggers = [];
+ var leds = data[0];
+ var plugins = data[1];
for (var k in leds)
for (var i = 0; i < leds[k].triggers.length; i++)
- triggers[leds[k].triggers[i]] = true;
+ triggers[i] = leds[k].triggers[i];
m = new form.Map('system',
_('<abbr title="Light Emitting Diode">LED</abbr> Configuration'),
@@ -49,117 +67,28 @@ return L.view.extend({
s.option(form.Value, 'name', _('Name'));
o = s.option(form.ListValue, 'sysfs', _('<abbr title="Light Emitting Diode">LED</abbr> Name'));
- Object.keys(leds).sort().forEach(function(name) { o.value(name) });
-
- o = s.option(form.Flag, 'default', _('Default state'));
- o.rmempty = false;
- o.textvalue = function(section_id) {
- var cval = this.cfgvalue(section_id);
-
- if (cval == null)
- cval = this.default;
-
- return (cval == this.enabled) ? _('On') : _('Off');
- };
+ Object.keys(leds).sort().forEach(function(name) {
+ o.value(name)
+ });
o = s.option(form.ListValue, 'trigger', _('Trigger'));
- if (usb.devices && usb.devices.length)
- triggers['usbdev'] = true;
- if (usb.ports && usb.ports.length)
- triggers['usbport'] = true;
- Object.keys(triggers).sort().forEach(function(t) { o.value(t, t.replace(/-/g, '')) });
-
- o = s.option(form.Value, 'delayon', _('On-State Delay'));
- o.modalonly = true;
- o.depends('trigger', 'timer');
-
- o = s.option(form.Value, 'delayoff', _('Off-State Delay'));
- o.modalonly = true;
- o.depends('trigger', 'timer');
-
- o = s.option(widgets.DeviceSelect, '_net_dev', _('Device'));
- o.rmempty = true;
- o.ucioption = 'dev';
- o.modalonly = true;
- o.noaliases = true;
- o.depends('trigger', 'netdev');
- o.remove = function(section_id) {
- var topt = this.map.lookupOption('trigger', section_id),
- tval = topt ? topt[0].formvalue(section_id) : null;
-
- if (tval != 'netdev' && tval != 'usbdev')
- uci.unset('system', section_id, 'dev');
- };
-
- o = s.option(form.MultiValue, 'mode', _('Trigger Mode'));
- o.rmempty = true;
- o.modalonly = true;
- o.depends('trigger', 'netdev');
- o.value('link', _('Link On'));
- o.value('tx', _('Transmit'));
- o.value('rx', _('Receive'));
-
- if (usb.devices && usb.devices.length) {
- o = s.option(form.ListValue, '_usb_dev', _('USB Device'));
- o.depends('trigger', 'usbdev');
- o.rmempty = true;
- o.ucioption = 'dev';
- o.modalonly = true;
- o.remove = function(section_id) {
- var topt = this.map.lookupOption('trigger', section_id),
- tval = topt ? topt[0].formvalue(section_id) : null;
-
- if (tval != 'netdev' && tval != 'usbdev')
- uci.unset('system', section_id, 'dev');
- }
- o.value('');
- usb.devices.forEach(function(usbdev) {
- o.value(usbdev.id, '%s (%s - %s)'.format(usbdev.id, usbdev.vendor || '?', usbdev.product || '?'));
- });
- }
-
- if (usb.ports && usb.ports.length) {
- o = s.option(form.MultiValue, 'port', _('USB Ports'));
- o.depends('trigger', 'usbport');
- o.rmempty = true;
- o.modalonly = true;
- o.cfgvalue = function(section_id) {
- var ports = [],
- value = uci.get('system', section_id, 'port');
-
- if (!Array.isArray(value))
- value = String(value || '').split(/\s+/);
-
- for (var i = 0; i < value.length; i++)
- if (value[i].match(/^(\d+)-(\d+)$/))
- ports.push('usb%d-port%d'.format(Regexp.$1, Regexp.$2));
- else
- ports.push(value[i]);
-
- return ports;
- };
- usb.ports.forEach(function(usbport) {
- var dev = (usbport.device && Array.isArray(usb.devices))
- ? usb.devices.filter(function(d) { return d.id == usbport.device })[0] : null;
-
- var label = _('Port %s').format(usbport.port);
-
- if (dev)
- label += ' (%s - %s)'.format(dev.vendor || '?', dev.product || '?');
-
- o.value(usbport.port, label);
- });
+ for (var i = 0; i < plugins.length; i++) {
+ var plugin = plugins[i];
+
+ if ( plugin.form.kernel == false )
+ o.value(plugin.name, plugin.form.trigger);
+ else
+ for (var k = 0; k < triggers.length; k++)
+ if ( plugin.name == triggers[k] )
+ o.value(plugin.name, plugin.form.trigger);
}
- o = s.option(form.Value, 'port_mask', _('Switch Port Mask'));
- o.modalonly = true;
- o.depends('trigger', 'switch0');
- o.depends('trigger', 'switch1');
-
- o = s.option(form.Value, 'speed_mask', _('Switch Speed Mask'));
- o.modalonly = true;
- o.depends('trigger', 'switch0');
- o.depends('trigger', 'switch1');
+ s.addModalOptions = function(s) {
+ for (var i = 0; i < plugins.length; i++) {
+ var plugin = plugins[i];
+ plugin.form.addFormOptions(s);
+ }
+ };
return m.render();
}
diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/mounts.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/mounts.js
index ec5d44cad0..e0a9226562 100644
--- a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/mounts.js
+++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/mounts.js
@@ -1,4 +1,5 @@
'use strict';
+'require view';
'require fs';
'require ui';
'require uci';
@@ -69,7 +70,7 @@ function device_textvalue(devices, section_id) {
}
}
-return L.view.extend({
+return view.extend({
handleDetect: function(m, ev) {
return callBlockDetect()
.then(L.bind(uci.unload, uci, 'fstab'))
@@ -211,7 +212,8 @@ return L.view.extend({
'%.2f%% (%1024.2mB)'.format(100 / this.mounts[i].size * used, used),
umount ? E('button', {
'class': 'btn cbi-button-remove',
- 'click': ui.createHandlerFn(view, 'handleUmount', m, this.mounts[i].mount)
+ 'click': ui.createHandlerFn(view, 'handleUmount', m, this.mounts[i].mount),
+ 'disabled': this.map.readonly || null
}, [ _('Unmount') ]) : '-'
]);
}
diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/password.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/password.js
index c3d317062f..eb9d4a6d76 100644
--- a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/password.js
+++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/password.js
@@ -1,4 +1,6 @@
'use strict';
+'require view';
+'require dom';
'require ui';
'require form';
'require rpc';
@@ -17,7 +19,7 @@ var callSetPassword = rpc.declare({
expect: { result: false }
});
-return L.view.extend({
+return view.extend({
checkPassword: function(section_id, value) {
var strength = document.querySelector('.cbi-value-description'),
strongRegex = new RegExp("^(?=.{8,})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*\\W).*$", "g"),
@@ -42,6 +44,8 @@ return L.view.extend({
var m, s, o;
m = new form.JSONMap(formData, _('Router Password'), _('Changes the administrator password for accessing the device'));
+ m.readonly = !L.hasViewPermission();
+
s = m.section(form.NamedSection, 'password', 'password');
o = s.option(form.Value, 'pw1', _('Password'));
@@ -53,7 +57,7 @@ return L.view.extend({
o.renderWidget = function(/* ... */) {
var node = form.Value.prototype.renderWidget.apply(this, arguments);
- node.childNodes[1].addEventListener('keydown', function(ev) {
+ node.querySelector('input').addEventListener('keydown', function(ev) {
if (ev.keyCode == 13 && !ev.currentTarget.classList.contains('cbi-input-invalid'))
document.querySelector('.cbi-button-save').click();
});
@@ -67,7 +71,7 @@ return L.view.extend({
handleSave: function() {
var map = document.querySelector('.cbi-map');
- return L.dom.callClassMethod(map, 'save').then(function() {
+ return dom.callClassMethod(map, 'save').then(function() {
if (formData.password.pw1 == null || formData.password.pw1.length == 0)
return;
@@ -85,7 +89,7 @@ return L.view.extend({
formData.password.pw1 = null;
formData.password.pw2 = null;
- L.dom.callClassMethod(map, 'render');
+ dom.callClassMethod(map, 'render');
});
});
},
diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/reboot.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/reboot.js
index 3ed87f413f..3b9f450fcd 100644
--- a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/reboot.js
+++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/reboot.js
@@ -1,9 +1,10 @@
'use strict';
+'require view';
'require fs';
'require ui';
'require uci';
-return L.view.extend({
+return view.extend({
load: function() {
return uci.changes();
},
diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/sshkeys.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/sshkeys.js
index ce3bac9612..4740b8c9dd 100644
--- a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/sshkeys.js
+++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/sshkeys.js
@@ -1,8 +1,12 @@
'use strict';
+'require baseclass';
+'require view';
'require fs';
'require ui';
-var SSHPubkeyDecoder = L.Class.singleton({
+var isReadonlyView = !L.hasViewPermission() || null;
+
+var SSHPubkeyDecoder = baseclass.singleton({
lengthDecode: function(s, off)
{
var l = (s.charCodeAt(off++) << 24) |
@@ -213,7 +217,7 @@ function handleWindowDragDropIgnore(ev) {
ev.preventDefault()
}
-return L.view.extend({
+return view.extend({
load: function() {
return fs.lines('/etc/dropbear/authorized_keys').then(function(lines) {
return lines.filter(function(line) {
@@ -223,17 +227,23 @@ return L.view.extend({
},
render: function(keys) {
- var list = E('div', { 'class': 'cbi-dynlist', 'dragover': dragKey, 'drop': dropKey }, [
+ var list = E('div', {
+ 'class': 'cbi-dynlist',
+ 'dragover': isReadonlyView ? null : dragKey,
+ 'drop': isReadonlyView ? null : dropKey
+ }, [
E('div', { 'class': 'add-item' }, [
E('input', {
'class': 'cbi-input-text',
'type': 'text',
'placeholder': _('Paste or drag SSH key fileā€¦') ,
- 'keydown': function(ev) { if (ev.keyCode === 13) addKey(ev) }
+ 'keydown': function(ev) { if (ev.keyCode === 13) addKey(ev) },
+ 'disabled': isReadonlyView
}),
E('button', {
'class': 'cbi-button',
- 'click': ui.createHandlerFn(this, addKey)
+ 'click': ui.createHandlerFn(this, addKey),
+ 'disabled': isReadonlyView
}, _('Add key'))
])
]);
@@ -243,7 +253,7 @@ return L.view.extend({
if (pubkey)
list.insertBefore(E('div', {
class: 'item',
- click: ui.createHandlerFn(this, removeKey),
+ click: isReadonlyView ? null : ui.createHandlerFn(this, removeKey),
'data-key': key
}, [
E('strong', pubkey.comment || _('Unnamed key')), E('br'),
diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/startup.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/startup.js
index 560282de77..ae92ce88b6 100644
--- a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/startup.js
+++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/startup.js
@@ -1,9 +1,12 @@
'use strict';
+'require view';
'require rpc';
'require fs';
'require ui';
-return L.view.extend({
+var isReadonlyView = !L.hasViewPermission() || null;
+
+return view.extend({
callInitList: rpc.declare({
object: 'luci',
method: 'getInitList',
@@ -36,12 +39,12 @@ return L.view.extend({
},
handleEnableDisable: function(name, isEnabled, ev) {
- return this.handleAction(name, isEnabled ? 'disable' : 'enable', ev).then(L.bind(function(name, isEnabled, cell) {
- L.dom.content(cell, this.renderEnableDisable({
+ return this.handleAction(name, isEnabled ? 'disable' : 'enable', ev).then(L.bind(function(name, isEnabled, btn) {
+ btn.parentNode.replaceChild(this.renderEnableDisable({
name: name,
enabled: isEnabled
- }));
- }, this, name, !isEnabled, ev.currentTarget.parentNode));
+ }), btn);
+ }, this, name, !isEnabled, ev.currentTarget));
},
handleRcLocalSave: function(ev) {
@@ -58,7 +61,8 @@ return L.view.extend({
renderEnableDisable: function(init) {
return E('button', {
class: 'btn cbi-button-%s'.format(init.enabled ? 'positive' : 'negative'),
- click: ui.createHandlerFn(this, 'handleEnableDisable', init.name, init.enabled)
+ click: ui.createHandlerFn(this, 'handleEnableDisable', init.name, init.enabled),
+ disabled: isReadonlyView
}, init.enabled ? _('Enabled') : _('Disabled'));
},
@@ -71,10 +75,7 @@ return L.view.extend({
E('div', { 'class': 'tr table-titles' }, [
E('div', { 'class': 'th' }, _('Start priority')),
E('div', { 'class': 'th' }, _('Initscript')),
- E('div', { 'class': 'th' }, _('Enable/Disable')),
- E('div', { 'class': 'th' }, _('Start')),
- E('div', { 'class': 'th' }, _('Restart')),
- E('div', { 'class': 'th' }, _('Stop'))
+ E('div', { 'class': 'th nowrap cbi-section-actions' })
])
]);
@@ -93,10 +94,12 @@ return L.view.extend({
rows.push([
'%02d'.format(list[i].index),
list[i].name,
- this.renderEnableDisable(list[i]),
- E('button', { 'class': 'btn cbi-button-action', 'click': ui.createHandlerFn(this, 'handleAction', list[i].name, 'start') }, _('Start')),
- E('button', { 'class': 'btn cbi-button-action', 'click': ui.createHandlerFn(this, 'handleAction', list[i].name, 'restart') }, _('Restart')),
- E('button', { 'class': 'btn cbi-button-action', 'click': ui.createHandlerFn(this, 'handleAction', list[i].name, 'stop') }, _('Stop'))
+ E('div', [
+ this.renderEnableDisable(list[i]),
+ E('button', { 'class': 'btn cbi-button-action', 'click': ui.createHandlerFn(this, 'handleAction', list[i].name, 'start'), 'disabled': isReadonlyView }, _('Start')),
+ E('button', { 'class': 'btn cbi-button-action', 'click': ui.createHandlerFn(this, 'handleAction', list[i].name, 'restart'), 'disabled': isReadonlyView }, _('Restart')),
+ E('button', { 'class': 'btn cbi-button-action', 'click': ui.createHandlerFn(this, 'handleAction', list[i].name, 'stop'), 'disabled': isReadonlyView }, _('Stop'))
+ ])
]);
}
@@ -111,11 +114,12 @@ return L.view.extend({
]),
E('div', { 'data-tab': 'rc', 'data-tab-title': _('Local Startup') }, [
E('p', {}, _('This is the content of /etc/rc.local. Insert your own commands here (in front of \'exit 0\') to execute them at the end of the boot process.')),
- E('p', {}, E('textarea', { 'style': 'width:100%', 'rows': 20 }, [ (rcLocal != null ? rcLocal : '') ])),
+ E('p', {}, E('textarea', { 'style': 'width:100%', 'rows': 20, 'disabled': isReadonlyView }, [ (rcLocal != null ? rcLocal : '') ])),
E('div', { 'class': 'cbi-page-actions' }, [
E('button', {
'class': 'btn cbi-button-save',
- 'click': ui.createHandlerFn(this, 'handleRcLocalSave')
+ 'click': ui.createHandlerFn(this, 'handleRcLocalSave'),
+ 'disabled': isReadonlyView
}, _('Save'))
])
])
diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/system.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/system.js
index 8ac119b624..2e087f5f4c 100644
--- a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/system.js
+++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/system.js
@@ -1,4 +1,6 @@
'use strict';
+'require view';
+'require poll';
'require ui';
'require uci';
'require rpc';
@@ -48,33 +50,35 @@ callTimezone = rpc.declare({
CBILocalTime = form.DummyValue.extend({
renderWidget: function(section_id, option_id, cfgvalue) {
return E([], [
- E('span', {}, [
- E('input', {
- 'id': 'localtime',
- 'type': 'text',
- 'readonly': true,
- 'value': new Date(cfgvalue * 1000).toLocaleString()
- })
- ]),
- ' ',
- E('button', {
- 'class': 'cbi-button cbi-button-apply',
- 'click': ui.createHandlerFn(this, function() {
- return callSetLocaltime(Math.floor(Date.now() / 1000));
- })
- }, _('Sync with browser')),
- ' ',
- this.ntpd_support ? E('button', {
- 'class': 'cbi-button cbi-button-apply',
- 'click': ui.createHandlerFn(this, function() {
- return callInitAction('sysntpd', 'restart');
- })
- }, _('Sync with NTP-Server')) : ''
+ E('input', {
+ 'id': 'localtime',
+ 'type': 'text',
+ 'readonly': true,
+ 'value': new Date(cfgvalue * 1000).toLocaleString()
+ }),
+ E('br'),
+ E('span', { 'class': 'control-group' }, [
+ E('button', {
+ 'class': 'cbi-button cbi-button-apply',
+ 'click': ui.createHandlerFn(this, function() {
+ return callSetLocaltime(Math.floor(Date.now() / 1000));
+ }),
+ 'disabled': (this.readonly != null) ? this.readonly : this.map.readonly
+ }, _('Sync with browser')),
+ ' ',
+ this.ntpd_support ? E('button', {
+ 'class': 'cbi-button cbi-button-apply',
+ 'click': ui.createHandlerFn(this, function() {
+ return callInitAction('sysntpd', 'restart');
+ }),
+ 'disabled': (this.readonly != null) ? this.readonly : this.map.readonly
+ }, _('Sync with NTP-Server')) : ''
+ ])
]);
},
});
-return L.view.extend({
+return view.extend({
load: function() {
return Promise.all([
callInitList('sysntpd'),
@@ -187,7 +191,7 @@ return L.view.extend({
o = s.taboption('zram', form.ListValue, 'zram_comp_algo', _('ZRam Compression Algorithm'));
o.optional = true;
- o.placeholder = 'lzo';
+ o.default = 'lzo';
o.value('lzo', 'lzo');
o.value('lz4', 'lz4');
o.value('deflate', 'deflate');
@@ -277,7 +281,7 @@ return L.view.extend({
}
return m.render().then(function(mapEl) {
- L.Poll.add(function() {
+ poll.add(function() {
return callGetLocaltime().then(function(t) {
mapEl.querySelector('#localtime').value = new Date(t * 1000).toLocaleString();
});
diff --git a/modules/luci-mod-system/luasrc/controller/admin/system.lua b/modules/luci-mod-system/luasrc/controller/admin/system.lua
deleted file mode 100644
index 1e0bebb4f9..0000000000
--- a/modules/luci-mod-system/luasrc/controller/admin/system.lua
+++ /dev/null
@@ -1,31 +0,0 @@
--- Copyright 2008 Steven Barth <steven@midlink.org>
--- Copyright 2008-2011 Jo-Philipp Wich <jow@openwrt.org>
--- Licensed to the public under the Apache License 2.0.
-
-module("luci.controller.admin.system", package.seeall)
-
-function index()
- entry({"admin", "system", "system"}, view("system/system"), _("System"), 1)
-
- entry({"admin", "system", "admin"}, firstchild(), _("Administration"), 2)
- entry({"admin", "system", "admin", "password"}, view("system/password"), _("Router Password"), 1)
-
- entry({"admin", "system", "admin", "dropbear"}, view("system/dropbear"), _("SSH Access"), 2)
- .uci_depends = { dropbear = true }
-
- entry({"admin", "system", "admin", "sshkeys"}, view("system/sshkeys"), _("SSH-Keys"), 3)
- .uci_depends = { dropbear = true }
-
- entry({"admin", "system", "startup"}, view("system/startup"), _("Startup"), 45)
- entry({"admin", "system", "crontab"}, view("system/crontab"), _("Scheduled Tasks"), 46)
-
- entry({"admin", "system", "mounts"}, view("system/mounts"), _("Mount Points"), 50)
- .file_depends = { "/sbin/block" }
-
- entry({"admin", "system", "leds"}, view("system/leds"), _("<abbr title=\"Light Emitting Diode\">LED</abbr> Configuration"), 60)
- .file_depends = { "/sys/class/leds" }
-
- entry({"admin", "system", "flash"}, view("system/flash"), _("Backup / Flash Firmware"), 70)
-
- entry({"admin", "system", "reboot"}, view("system/reboot"), _("Reboot"), 90)
-end
diff --git a/modules/luci-mod-system/root/usr/share/luci/menu.d/luci-mod-system.json b/modules/luci-mod-system/root/usr/share/luci/menu.d/luci-mod-system.json
new file mode 100644
index 0000000000..4022e0cebf
--- /dev/null
+++ b/modules/luci-mod-system/root/usr/share/luci/menu.d/luci-mod-system.json
@@ -0,0 +1,136 @@
+{
+ "admin/system/system": {
+ "title": "System",
+ "order": 1,
+ "action": {
+ "type": "view",
+ "path": "system/system"
+ },
+ "depends": {
+ "acl": [ "luci-mod-system-config" ]
+ }
+ },
+
+ "admin/system/admin": {
+ "title": "Administration",
+ "order": 2,
+ "action": {
+ "type": "firstchild"
+ },
+ "depends": {
+ "acl": [ "luci-mod-system-config", "luci-mod-system-ssh" ]
+ }
+ },
+
+ "admin/system/admin/password": {
+ "title": "Router Password",
+ "order": 1,
+ "action": {
+ "type": "view",
+ "path": "system/password"
+ },
+ "depends": {
+ "acl": [ "luci-mod-system-config" ]
+ }
+ },
+
+ "admin/system/admin/dropbear": {
+ "title": "SSH Access",
+ "order": 2,
+ "action": {
+ "type": "view",
+ "path": "system/dropbear"
+ },
+ "depends": {
+ "acl": [ "luci-mod-system-ssh" ],
+ "fs": { "/usr/sbin/dropbear": "executable" }
+ }
+ },
+
+ "admin/system/admin/sshkeys": {
+ "title": "SSH-Keys",
+ "order": 3,
+ "action": {
+ "type": "view",
+ "path": "system/sshkeys"
+ },
+ "depends": {
+ "acl": [ "luci-mod-system-ssh" ],
+ "fs": { "/usr/sbin/dropbear": "executable" }
+ }
+ },
+
+ "admin/system/startup": {
+ "title": "Startup",
+ "order": 45,
+ "action": {
+ "type": "view",
+ "path": "system/startup"
+ },
+ "depends": {
+ "acl": [ "luci-mod-system-init" ]
+ }
+ },
+
+ "admin/system/crontab": {
+ "title": "Scheduled Tasks",
+ "order": 46,
+ "action": {
+ "type": "view",
+ "path": "system/crontab"
+ },
+ "depends": {
+ "acl": [ "luci-mod-system-cron" ]
+ }
+ },
+
+ "admin/system/mounts": {
+ "title": "Mount Points",
+ "order": 50,
+ "action": {
+ "type": "view",
+ "path": "system/mounts"
+ },
+ "depends": {
+ "acl": [ "luci-mod-system-mounts" ],
+ "fs": { "/sbin/block": "executable" }
+ }
+ },
+
+ "admin/system/leds": {
+ "title": "LED Configuration",
+ "order": 60,
+ "action": {
+ "type": "view",
+ "path": "system/leds"
+ },
+ "depends": {
+ "acl": [ "luci-mod-system-config" ],
+ "fs": { "/sys/class/leds": "directory" }
+ }
+ },
+
+ "admin/system/flash": {
+ "title": "Backup / Flash Firmware",
+ "order": 70,
+ "action": {
+ "type": "view",
+ "path": "system/flash"
+ },
+ "depends": {
+ "acl": [ "luci-mod-system-flash" ]
+ }
+ },
+
+ "admin/system/reboot": {
+ "title": "Reboot",
+ "order": 90,
+ "action": {
+ "type": "view",
+ "path": "system/reboot"
+ },
+ "depends": {
+ "acl": [ "luci-mod-system-reboot" ]
+ }
+ }
+}
diff --git a/modules/luci-mod-system/root/usr/share/rpcd/acl.d/luci-mod-system.json b/modules/luci-mod-system/root/usr/share/rpcd/acl.d/luci-mod-system.json
new file mode 100644
index 0000000000..b29ddb8f4e
--- /dev/null
+++ b/modules/luci-mod-system/root/usr/share/rpcd/acl.d/luci-mod-system.json
@@ -0,0 +1,167 @@
+{
+ "luci-mod-system-config": {
+ "description": "Grant access to system configuration",
+ "read": {
+ "ubus": {
+ "luci": [ "getInitList", "getLEDs", "getLocaltime", "getTimezones", "getUSBDevices" ]
+ },
+ "uci": [ "luci", "system" ]
+ },
+ "write": {
+ "ubus": {
+ "luci": [ "setInitAction", "setLocaltime", "setPassword" ]
+ },
+ "uci": [ "luci", "system" ]
+ }
+ },
+
+ "luci-mod-system-ssh": {
+ "description": "Grant access to SSH configuration",
+ "read": {
+ "file": {
+ "/etc/dropbear/authorized_keys": [ "read" ]
+ },
+ "ubus": {
+ "file": [ "read" ]
+ },
+ "uci": [ "dropbear" ]
+ },
+ "write": {
+ "file": {
+ "/etc/dropbear/authorized_keys": [ "write" ]
+ },
+ "ubus": {
+ "file": [ "write" ],
+ "luci": [ "setInitAction", "setLocaltime" ]
+ },
+ "uci": [ "dropbear" ]
+ }
+ },
+
+ "luci-mod-system-init": {
+ "description": "Grant access to startup configuration",
+ "read": {
+ "file": {
+ "/etc/rc.local": [ "read" ]
+ },
+ "ubus": {
+ "file": [ "read" ],
+ "luci": [ "getInitList" ]
+ }
+ },
+ "write": {
+ "file": {
+ "/etc/rc.local": [ "write" ]
+ },
+ "ubus": {
+ "file": [ "write" ],
+ "luci": [ "setInitAction" ]
+ }
+ }
+ },
+
+ "luci-mod-system-cron": {
+ "description": "Grant access to crontab configuration",
+ "read": {
+ "file": {
+ "/etc/crontabs/root": [ "read" ]
+ },
+ "ubus": {
+ "file": [ "read" ]
+ }
+ },
+ "write": {
+ "file": {
+ "/etc/crontabs/root": [ "write" ]
+ },
+ "ubus": {
+ "file": [ "write" ]
+ }
+ }
+ },
+
+ "luci-mod-system-mounts": {
+ "description": "Grant access to mount configuration",
+ "read": {
+ "file": {
+ "/etc/filesystems": [ "read" ],
+ "/proc/filesystems": [ "read" ],
+ "/usr/bin/btrfsck": [ "list" ],
+ "/usr/bin/ntfsfix": [ "list" ],
+ "/usr/sbin/e2fsck": [ "list" ],
+ "/usr/sbin/fsck.f2fs": [ "list" ],
+ "/usr/sbin/fsck.fat": [ "list" ]
+ },
+ "ubus": {
+ "file": [ "read", "stat" ],
+ "luci": [ "getBlockDevices", "getMountPoints" ]
+ },
+ "uci": [ "fstab" ]
+ },
+ "write": {
+ "file": {
+ "/etc/crontabs/root": [ "write" ],
+ "/bin/umount": [ "exec" ],
+ "/sbin/block": [ "exec" ]
+ },
+ "ubus": {
+ "file": [ "exec", "write" ],
+ "luci": [ "setBlockDetect" ]
+ },
+ "uci": [ "fstab" ]
+ }
+ },
+
+ "luci-mod-system-flash": {
+ "description": "Grant access to flash operations",
+ "read": {
+ "cgi-io": [ "backup", "download" ],
+ "file": {
+ "/dev/mtdblock[0-9]*": [ "read" ],
+ "/etc/sysupgrade.conf": [ "read" ],
+ "/lib/upgrade/platform.sh": [ "list" ],
+ "/proc/mounts": [ "read" ],
+ "/proc/mtd": [ "read" ],
+ "/proc/partitions": [ "read" ],
+ "/proc/sys/kernel/hostname": [ "read" ],
+ "/sbin/sysupgrade --list-backup": [ "exec" ]
+ },
+ "ubus": {
+ "file": [ "exec", "read", "stat" ]
+ }
+ },
+ "write": {
+ "cgi-io": [ "upload" ],
+ "file": {
+ "/bin/tar -tzf /tmp/backup.tar.gz": [ "exec" ],
+ "/etc/sysupgrade.conf": [ "write" ],
+ "/sbin/firstboot -r -y": [ "exec" ],
+ "/sbin/reboot": [ "exec" ],
+ "/sbin/sysupgrade --force /tmp/firmware.bin": [ "exec" ],
+ "/sbin/sysupgrade -n --force /tmp/firmware.bin": [ "exec" ],
+ "/sbin/sysupgrade -n /tmp/firmware.bin": [ "exec" ],
+ "/sbin/sysupgrade --restore-backup /tmp/backup.tar.gz": [ "exec" ],
+ "/sbin/sysupgrade --test /tmp/firmware.bin": [ "exec" ],
+ "/sbin/sysupgrade /tmp/firmware.bin": [ "exec" ],
+ "/tmp/backup.tar.gz": [ "write" ],
+ "/tmp/firmware.bin": [ "write" ]
+ },
+ "ubus": {
+ "file": [ "exec", "remove", "write" ],
+ "system": [ "validate_firmware_image" ]
+ }
+ }
+ },
+
+ "luci-mod-system-reboot": {
+ "description": "Allow rebooting the device",
+ "write": {
+ "file": {
+ "/sbin/reboot": [ "exec" ]
+ },
+ "ubus": {
+ "file": [ "exec" ]
+ }
+ }
+ }
+}