diff options
Diffstat (limited to 'modules/luci-mod-system')
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" ] + } + } + } +} |