mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-05 01:44:16 +08:00
⛄ Sync 2026-04-23 20:32:35
This commit is contained in:
1 parent
17738c6f79
commit
fe51d2b304
206 files changed
+725671
No files matched your search
@@ -0,0 +1,183 @@
|
||||
'use strict';
|
||||
|
||||
var s = [0x0000, 0x0000, 0x0000, 0x0000];
|
||||
|
||||
/**
|
||||
* Multiply two 64-bit values represented as arrays of four 16-bit words.
|
||||
*
|
||||
* Arrays use little-endian word order (least-significant 16-bit word first).
|
||||
* The result is truncated to the lower 64 bits and returned as a 4-element
|
||||
* array of 16-bit words.
|
||||
*
|
||||
* @param {Array<number>} a - Multiplicand (4 × 16-bit words, little-endian)
|
||||
* @param {Array<number>} b - Multiplier (4 × 16-bit words, little-endian)
|
||||
* @returns {Array<number>} Product as 4 × 16-bit words (little-endian)
|
||||
*/
|
||||
function mul(a, b) {
|
||||
var r = [0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000];
|
||||
|
||||
for (var j = 0; j < 4; j++) {
|
||||
var k = 0;
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var t = a[i] * b[j] + r[i+j] + k;
|
||||
r[i+j] = t & 0xffff;
|
||||
k = t >>> 16;
|
||||
}
|
||||
r[j+4] = k;
|
||||
}
|
||||
|
||||
r.length = 4;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a small integer to a 64-bit value represented as four 16-bit words.
|
||||
*
|
||||
* Treats `a` as a little-endian 64-bit value (4 × 16-bit words). Adds the
|
||||
* integer `n` to the least-significant word and propagates carry across
|
||||
* subsequent 16-bit words. The result is truncated to 64 bits and returned
|
||||
* as a 4-element array of 16-bit words (little-endian).
|
||||
*
|
||||
* @param {Array<number>} a - Addend as 4 × 16-bit words (little-endian)
|
||||
* @param {number} n - Value to add (integer carry)
|
||||
* @returns {Array<number>} Sum as 4 × 16-bit words (little-endian)
|
||||
*/
|
||||
function add(a, n) {
|
||||
var r = [0x0000, 0x0000, 0x0000, 0x0000],
|
||||
k = n;
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var t = a[i] + k;
|
||||
r[i] = t & 0xffff;
|
||||
k = t >>> 16;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift a 64-bit value (4 × 16-bit words, little-endian) right by `n` bits.
|
||||
*
|
||||
* The input array is treated as little-endian 16-bit words. Bits shifted out
|
||||
* on the right are discarded; the returned array contains the lower 64-bit
|
||||
* result after the logical right shift.
|
||||
*
|
||||
* @param {Array<number>} a - Source value as 4 × 16-bit words (little-endian)
|
||||
* @param {number} n - Number of bits to shift right (non-negative integer)
|
||||
* @returns {Array<number>} Shifted value as 4 × 16-bit words (little-endian)
|
||||
*/
|
||||
function shr(a, n) {
|
||||
var r = [a[0], a[1], a[2], a[3], 0x0000],
|
||||
i = 4,
|
||||
k = 0;
|
||||
|
||||
for (; n > 16; n -= 16, i--)
|
||||
for (var j = 0; j < 4; j++)
|
||||
r[j] = r[j+1];
|
||||
|
||||
for (; i > 0; i--) {
|
||||
var s = r[i-1];
|
||||
r[i-1] = (s >>> n) | k;
|
||||
k = ((s & ((1 << n) - 1)) << (16 - n));
|
||||
}
|
||||
|
||||
r.length = 4;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
return L.Class.extend({
|
||||
/**
|
||||
* Seed the PRNG state.
|
||||
*
|
||||
* The seed is treated as a 32-bit integer; the lower 16 bits are stored
|
||||
* in `s[0]`, the upper 16 bits in `s[1]`. `s[2]` and `s[3]` are zeroed.
|
||||
*
|
||||
* @param {number} n - Seed value (32-bit integer)
|
||||
* @returns {void}
|
||||
*/
|
||||
seed: function(n) {
|
||||
n = (n - 1)|0;
|
||||
s[0] = n & 0xffff;
|
||||
s[1] = n >>> 16;
|
||||
s[2] = 0;
|
||||
s[3] = 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Produce the next PRNG 32-bit integer.
|
||||
*
|
||||
* Advances the internal state and returns a 32-bit pseudo-random integer
|
||||
* derived from the current state.
|
||||
*
|
||||
* @returns {number} 32-bit pseudo-random integer (JS number)
|
||||
*/
|
||||
int: function() {
|
||||
s = mul(s, [0x7f2d, 0x4c95, 0xf42d, 0x5851]);
|
||||
s = add(s, 1);
|
||||
|
||||
var r = shr(s, 33);
|
||||
return (r[1] << 16) | r[0];
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a pseudo-random value.
|
||||
*
|
||||
* Overloads:
|
||||
* - get() -> number in [0, 1]
|
||||
* - get(upper) -> integer in [1, upper]
|
||||
* - get(lower, upper) -> integer in [lower, upper]
|
||||
*
|
||||
* @param {number} [lower=0] - Lower bound (when two args supplied)
|
||||
* @param {number} [upper=0] - Upper bound (when one or two args supplied)
|
||||
* @returns {number} Random value (float in [0,1] or integer in requested range)
|
||||
*/
|
||||
get: function() {
|
||||
var r = (this.int() % 0x7fffffff) / 0x7fffffff, l, u;
|
||||
|
||||
switch (arguments.length) {
|
||||
case 0:
|
||||
return r;
|
||||
|
||||
case 1:
|
||||
l = 1;
|
||||
u = arguments[0]|0;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
l = arguments[0]|0;
|
||||
u = arguments[1]|0;
|
||||
break;
|
||||
}
|
||||
|
||||
return Math.floor(r * (u - l + 1)) + l;
|
||||
},
|
||||
|
||||
/**
|
||||
* Derive a deterministic hex color from an input string.
|
||||
*
|
||||
* The color is produced by seeding the PRNG from a string-derived
|
||||
* hash and producing RGB components. Returns a `#rrggbb` hex string.
|
||||
*
|
||||
* @param {string} string - Input string used to derive the color
|
||||
* @returns {string} Hex color string in `#rrggbb` format
|
||||
*/
|
||||
derive_color: function(string) {
|
||||
this.seed(parseInt(sfh(string), 16));
|
||||
|
||||
var r = this.get(128),
|
||||
g = this.get(128),
|
||||
min = 0,
|
||||
max = 128;
|
||||
|
||||
if ((r + g) < 128)
|
||||
min = 128 - r - g;
|
||||
else
|
||||
max = 255 - r - g;
|
||||
|
||||
var b = min + Math.floor(this.get() * (max - min));
|
||||
|
||||
return '#%02x%02x%02x'.format(0xff - r, 0xff - g, 0xff - b);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,295 @@
|
||||
'use strict';
|
||||
'require poll';
|
||||
'require rpc';
|
||||
'require uci';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require view';
|
||||
|
||||
/* Note that any view implementing this log reader requires the log read
|
||||
acl permission */
|
||||
|
||||
const callLogRead = rpc.declare({
|
||||
object: 'log',
|
||||
method: 'read',
|
||||
params: [ 'lines', 'stream', 'oneshot' ],
|
||||
expect: { log: [] }
|
||||
});
|
||||
|
||||
var CBILogreadBox = function(logtag, name) {
|
||||
return L.view.extend({
|
||||
|
||||
logFacilityFilter: 'any',
|
||||
invertLogFacilitySearch: false,
|
||||
logSeverityFilter: 'any',
|
||||
invertLogSeveritySearch: false,
|
||||
logTextFilter: '',
|
||||
invertLogTextSearch: false,
|
||||
logTagFilter: logtag ? logtag : '',
|
||||
logName: name ? name : _('System Log'),
|
||||
fetchMaxRows: 1000,
|
||||
|
||||
facilities: [
|
||||
['any', 'any', _('Any')],
|
||||
['0', 'kern', _('Kernel')],
|
||||
['1', 'user', _('User')],
|
||||
['2', 'mail', _('Mail')],
|
||||
['3', 'daemon', _('Daemon')],
|
||||
['4', 'auth', _('Auth')],
|
||||
['5', 'syslog', _('Syslog')],
|
||||
['6', 'lpr', _('LPR')],
|
||||
['7', 'news', _('News')],
|
||||
['8', 'uucp', _('UUCP')],
|
||||
['9', 'cron', _('Cron')],
|
||||
['10', 'authpriv', _('Auth Priv')],
|
||||
['11', 'ftp', _('FTP')],
|
||||
['12', 'ntp', _('NTP')],
|
||||
['13', 'security', _('Log audit')],
|
||||
['14', 'console', _('Log alert')],
|
||||
['15', 'cron', _('Scheduling daemon')],
|
||||
['16', 'local0', _('Local 0')],
|
||||
['17', 'local1', _('Local 1')],
|
||||
['18', 'local2', _('Local 2')],
|
||||
['19', 'local3', _('Local 3')],
|
||||
['20', 'local4', _('Local 4')],
|
||||
['21', 'local5', _('Local 5')],
|
||||
['22', 'local6', _('Local 6')],
|
||||
['23', 'local7', _('Local 7')]
|
||||
],
|
||||
|
||||
severity: [
|
||||
['any','any', _('Any')],
|
||||
['0', 'emerg', _('Emergency')],
|
||||
['1', 'alert', _('Alert')],
|
||||
['2', 'crit', _('Critical')],
|
||||
['3', 'err', _('Error')],
|
||||
['4', 'warn', _('Warning')],
|
||||
['5', 'notice', _('Notice')],
|
||||
['6', 'info', _('Info')],
|
||||
['7', 'debug', _('Debug')]
|
||||
],
|
||||
|
||||
|
||||
async retrieveLog() {
|
||||
try {
|
||||
const tz = uci.get('system', '@system[0]', 'zonename')?.replaceAll(' ', '_');
|
||||
const ts = uci.get('system', '@system[0]', 'clock_timestyle') || 0;
|
||||
const hc = uci.get('system', '@system[0]', 'clock_hourcycle') || 0;
|
||||
let loglines = await callLogRead(this.fetchMaxRows, false, true)
|
||||
.then((logEntries) => {
|
||||
const dateObj = new Intl.DateTimeFormat(undefined, {
|
||||
dateStyle: 'medium',
|
||||
timeStyle: (ts == 0) ? 'long' : 'full',
|
||||
hourCycle: (hc == 0) ? undefined : hc,
|
||||
timeZone: tz
|
||||
});
|
||||
|
||||
return logEntries.map(entry => {
|
||||
const time = new Date(entry?.time);
|
||||
const datestr = dateObj.format(time);
|
||||
/* remember to add one since the 'any' entry occupies 1st position i.e. [0] */
|
||||
const facility = this.facilities[Math.floor(entry?.priority / 8) + 1][1] ?? 'unknown';
|
||||
const severity = this.severity[(entry?.priority % 8) + 1][1] ?? 'unknown';
|
||||
return `[${datestr}] ${facility}.${severity}: ${entry?.msg}`;
|
||||
});
|
||||
})
|
||||
.catch(function (){
|
||||
return Promise.all([
|
||||
L.resolveDefault(fs.stat('/usr/libexec/syslog-wrapper'), null),
|
||||
]).then((stat) => {
|
||||
const logger = stat[0]?.path;
|
||||
return fs.exec_direct(logger)
|
||||
.then(logdata => {
|
||||
return logdata.trim().split(/\n/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
loglines = loglines.filter(line => {
|
||||
const sevMatch = this.logSeverityFilter === 'any' || line.includes(`.${this.logSeverityFilter}`);
|
||||
const facMatch = this.logFacilityFilter === 'any' || line.includes(`${this.logFacilityFilter}.`);
|
||||
return (this.invertLogSeveritySearch != sevMatch)
|
||||
&& (this.invertLogFacilitySearch != facMatch);
|
||||
});
|
||||
|
||||
loglines = loglines.filter(line => {
|
||||
return line.toLowerCase().includes(this.logTagFilter?.toLowerCase());
|
||||
});
|
||||
|
||||
loglines = loglines.filter(line => {
|
||||
const match = line.includes(this.logTextFilter);
|
||||
return this.invertLogTextSearch ? !match : match;
|
||||
});
|
||||
|
||||
return {
|
||||
value: loglines?.join('\n'),
|
||||
rows: loglines?.length + 1
|
||||
};
|
||||
}
|
||||
catch (err) {
|
||||
ui.addNotification(null, E('p', {}, _('Unable to load log data: ' + err.message)));
|
||||
return {
|
||||
value: '',
|
||||
rows: 0
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
async pollLog() {
|
||||
const element = document.getElementById('syslog');
|
||||
if (element) {
|
||||
const log = await this.retrieveLog();
|
||||
element.value = log?.value;
|
||||
element.rows = log?.rows;
|
||||
}
|
||||
},
|
||||
|
||||
async load() {
|
||||
poll.add(this.pollLog.bind(this));
|
||||
return Promise.all([
|
||||
uci.load('system'),
|
||||
]).then(() => this.retrieveLog());
|
||||
},
|
||||
|
||||
render(loglines) {
|
||||
const scrollDownButton = E('button', {
|
||||
'id': 'scrollDownButton',
|
||||
'class': 'cbi-button cbi-button-neutral'
|
||||
}, _('Scroll to tail', 'scroll to bottom (the tail) of the log file')
|
||||
);
|
||||
scrollDownButton.addEventListener('click', () => {
|
||||
scrollUpButton.scrollIntoView();
|
||||
scrollDownButton.blur();
|
||||
});
|
||||
|
||||
const scrollUpButton = E('button', {
|
||||
'id' : 'scrollUpButton',
|
||||
'class': 'cbi-button cbi-button-neutral'
|
||||
}, _('Scroll to head', 'scroll to top (the head) of the log file')
|
||||
);
|
||||
scrollUpButton.addEventListener('click', () => {
|
||||
scrollDownButton.scrollIntoView();
|
||||
scrollUpButton.blur();
|
||||
});
|
||||
|
||||
const self = this;
|
||||
|
||||
// Create facility invert checkbox
|
||||
const facilityInvert = E('input', {
|
||||
'id': 'invertLogFacilitySearch',
|
||||
'type': 'checkbox',
|
||||
'class': 'cbi-input-checkbox',
|
||||
});
|
||||
|
||||
// Create facility select-dropdown from facilities map
|
||||
const facilitySelect = E('select', {
|
||||
'id': 'logFacilitySelect',
|
||||
'class': 'cbi-input-select',
|
||||
'style': 'margin-bottom:10px',
|
||||
},
|
||||
this.facilities.map(([ , val, label]) =>
|
||||
(val == 'any') ? E('option', { value: val, selected: '' }, label) : E('option', { value: val }, label)
|
||||
));
|
||||
|
||||
// Create severity invert checkbox
|
||||
const severityInvert = E('input', {
|
||||
'id': 'invertLogSeveritySearch',
|
||||
'type': 'checkbox',
|
||||
'class': 'cbi-input-checkbox',
|
||||
});
|
||||
|
||||
// Create severity select-dropdown from facilities map
|
||||
const severitySelect = E('select', {
|
||||
'id': 'logSeveritySelect',
|
||||
'class': 'cbi-input-select',
|
||||
},
|
||||
this.severity.map(([ , val, label]) =>
|
||||
(val == 'any') ? E('option', { value: val, selected: '' }, label) : E('option', { value: val }, label)
|
||||
));
|
||||
|
||||
// Create raw text search invert checkbox
|
||||
const filterTextInvert = E('input', {
|
||||
'id': 'invertLogTextSearch',
|
||||
'type': 'checkbox',
|
||||
'class': 'cbi-input-checkbox',
|
||||
});
|
||||
|
||||
// Create raw text search text input
|
||||
const filterTextInput = E('input', {
|
||||
'id': 'logTextFilter',
|
||||
'class': 'cbi-input-text',
|
||||
});
|
||||
|
||||
// Create max rows input
|
||||
const filterMaxRows = E('input', {
|
||||
'id': 'logMaxRows',
|
||||
'type': 'number',
|
||||
'class': 'cbi-input',
|
||||
});
|
||||
|
||||
/**
|
||||
* Update the form when log filter parameters change
|
||||
*/
|
||||
function handleLogFilterChange() {
|
||||
self.logFacilityFilter = facilitySelect.value;
|
||||
self.invertLogFacilitySearch = facilityInvert.checked;
|
||||
self.logSeverityFilter = severitySelect.value;
|
||||
self.invertLogSeveritySearch = severityInvert.checked;
|
||||
self.logTextFilter = filterTextInput.value;
|
||||
self.invertLogTextSearch = filterTextInvert.checked;
|
||||
self.fetchMaxRows = Number.parseInt(filterMaxRows.value);
|
||||
self.pollLog();
|
||||
}
|
||||
|
||||
facilitySelect.addEventListener('change', handleLogFilterChange);
|
||||
facilityInvert.addEventListener('change', handleLogFilterChange);
|
||||
severitySelect.addEventListener('change', handleLogFilterChange);
|
||||
severityInvert.addEventListener('change', handleLogFilterChange);
|
||||
filterTextInput.addEventListener('input', handleLogFilterChange);
|
||||
filterTextInvert.addEventListener('change', handleLogFilterChange);
|
||||
filterMaxRows.addEventListener('change', handleLogFilterChange);
|
||||
|
||||
return E([], [
|
||||
E('h2', {}, [ this.logName ]),
|
||||
E('div', { 'id': 'content_syslog' }, [
|
||||
E('div', { class: 'cbi-section-descr' }, this.logTagFilter ? _('The syslog output, pre-filtered for messages related to: ' + this.logTagFilter) : '') ,
|
||||
E('div', { 'style': 'margin-bottom:10px' }, [
|
||||
E('label', { 'for': 'invertLogFacilitySearch', 'style': 'margin-right:5px' }, _('Not')),
|
||||
facilityInvert,
|
||||
E('label', { 'for': 'logFacilitySelect', 'style': 'margin: 0 5px' }, _('facility:')),
|
||||
facilitySelect,
|
||||
E('label', { 'for': 'invertLogSeveritySearch', 'style': 'margin: 0 5px' }, _('Not')),
|
||||
severityInvert,
|
||||
E('label', { 'for': 'logSeveritySelect', 'style': 'margin: 0 5px' }, _('severity:')),
|
||||
severitySelect,
|
||||
]),
|
||||
E('div', { 'style': 'margin-bottom:10px' }, [
|
||||
E('label', { 'for': 'invertLogTextSearch', 'style': 'margin-right:5px' }, _('Not')),
|
||||
filterTextInvert,
|
||||
E('label', { 'for': 'logTextFilter', 'style': 'margin: 0 5px' }, _('including:')),
|
||||
filterTextInput,
|
||||
E('label', { 'for': 'logMaxRows', 'style': 'margin: 0 5px' }, _('Max rows:')),
|
||||
filterMaxRows,
|
||||
]),
|
||||
E('div', {'style': 'padding-bottom: 20px'}, [scrollDownButton]),
|
||||
E('textarea', {
|
||||
'id': 'syslog',
|
||||
'style': 'font-size:12px',
|
||||
'readonly': 'readonly',
|
||||
'wrap': 'off',
|
||||
'rows': loglines?.rows,
|
||||
}, [ loglines?.value ]),
|
||||
E('div', {'style': 'padding-bottom: 20px'}, [scrollUpButton])
|
||||
])
|
||||
]);
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
||||
};
|
||||
|
||||
return L.Class.extend({
|
||||
LogreadBox: CBILogreadBox,
|
||||
});
|
||||
@@ -0,0 +1,855 @@
|
||||
'use strict';
|
||||
'require ui';
|
||||
'require form';
|
||||
'require network';
|
||||
'require firewall';
|
||||
'require fs';
|
||||
'require uci';
|
||||
|
||||
|
||||
/**
|
||||
* Get users found in `/etc/passwd`.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function getUsers() {
|
||||
return fs.lines('/etc/passwd').then(function(lines) {
|
||||
return lines.map(function(line) { return line.split(/:/)[0] });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get users found in `/etc/group`.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function getGroups() {
|
||||
return fs.lines('/etc/group').then(function(lines) {
|
||||
return lines.map(function(line) { return line.split(/:/)[0] });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bridge devices or Layer 3 devices of a network object.
|
||||
* @param {object} network
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function getDevices(network) {
|
||||
if (network.isBridge()) {
|
||||
var devices = network.getDevices();
|
||||
return devices ? devices : [];
|
||||
} else {
|
||||
return L.toArray(network.getL3Device());
|
||||
}
|
||||
}
|
||||
|
||||
var CBIZoneSelect = form.ListValue.extend({
|
||||
__name__: 'CBI.ZoneSelect',
|
||||
|
||||
load(section_id) {
|
||||
return Promise.all([ firewall.getZones(), network.getNetworks() ]).then(L.bind(function(zn) {
|
||||
this.zones = zn[0];
|
||||
this.networks = zn[1];
|
||||
|
||||
return this.super('load', section_id);
|
||||
}, this));
|
||||
},
|
||||
|
||||
filter(section_id, value) {
|
||||
return true;
|
||||
},
|
||||
|
||||
lookupZone(name) {
|
||||
return this.zones.filter(function(zone) { return zone.getName() == name })[0];
|
||||
},
|
||||
|
||||
lookupNetwork(name) {
|
||||
return this.networks.filter(function(network) { return network.getName() == name })[0];
|
||||
},
|
||||
|
||||
renderWidget(section_id, option_index, cfgvalue) {
|
||||
const values = L.toArray((cfgvalue != null) ? cfgvalue : this.default);
|
||||
let isOutputOnly = false;
|
||||
const choices = {};
|
||||
let datatype_str = 'ucifw4zonename';
|
||||
if (!L.hasSystemFeature('firewall4'))
|
||||
datatype_str = `and(${datatype_str},maxlength(11))`;
|
||||
if (this.allowany && this.nocreate)
|
||||
datatype_str = `or(${datatype_str},"*")`;
|
||||
if (this.multiple)
|
||||
datatype_str = `list(${datatype_str})`;
|
||||
|
||||
if (this.option == 'dest') {
|
||||
for (let c of this.section.children) {
|
||||
const opt = c;
|
||||
if (opt.option == 'src') {
|
||||
const val = opt.cfgvalue(section_id) || opt.default;
|
||||
isOutputOnly = (val == null || val == '');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.title = isOutputOnly ? _('Output zone') : _('Destination zone');
|
||||
}
|
||||
|
||||
if (this.allowlocal) {
|
||||
choices[''] = E('span', {
|
||||
'class': 'zonebadge',
|
||||
'style': firewall.getZoneColorStyle(null)
|
||||
}, [
|
||||
E('strong', _('Device')),
|
||||
(this.allowany || this.allowlocal)
|
||||
? E('span', ' (%s)'.format(this.option != 'dest' ? _('output') : _('input'))) : ''
|
||||
]);
|
||||
}
|
||||
else if (!this.multiple && (this.rmempty || this.optional)) {
|
||||
choices[''] = E('span', {
|
||||
'class': 'zonebadge',
|
||||
'style': firewall.getZoneColorStyle(null)
|
||||
}, E('em', _('unspecified')));
|
||||
}
|
||||
|
||||
if (this.allowany) {
|
||||
choices['*'] = E('span', {
|
||||
'class': 'zonebadge',
|
||||
'style': firewall.getZoneColorStyle(null)
|
||||
}, [
|
||||
E('strong', _('Any zone')),
|
||||
(this.allowany && this.allowlocal && !isOutputOnly) ? E('span', ' (%s)'.format(_('forward'))) : ''
|
||||
]);
|
||||
}
|
||||
|
||||
for (let zone of this.zones) {
|
||||
const name = zone.getName();
|
||||
const networks = zone.getNetworks();
|
||||
const ifaces = [];
|
||||
|
||||
if (!this.filter(section_id, name))
|
||||
continue;
|
||||
|
||||
for (let n of networks) {
|
||||
const network = this.lookupNetwork(n);
|
||||
|
||||
if (!network)
|
||||
continue;
|
||||
|
||||
const span = E('span', {
|
||||
'class': 'ifacebadge' + (network.isUp() ? ' ifacebadge-active' : '')
|
||||
}, network.getName() + ': ');
|
||||
|
||||
const devices = getDevices(network);
|
||||
|
||||
for (let d of devices) {
|
||||
span.appendChild(E('img', {
|
||||
'title': d.getI18n(),
|
||||
'src': L.resource('icons/%s%s.svg'.format(d.getType(), d.isUp() ? '' : '_disabled'))
|
||||
}));
|
||||
}
|
||||
|
||||
if (!devices.length)
|
||||
span.appendChild(E('em', _('(empty)')));
|
||||
|
||||
ifaces.push(span);
|
||||
}
|
||||
|
||||
if (!ifaces.length)
|
||||
ifaces.push(E('em', _('(empty)')));
|
||||
|
||||
choices[name] = E('span', {
|
||||
'class': 'zonebadge',
|
||||
'style': firewall.getZoneColorStyle(zone)
|
||||
}, [ E('strong', name) ].concat(ifaces));
|
||||
}
|
||||
|
||||
const widget = new ui.Dropdown(values, choices, {
|
||||
id: this.cbid(section_id),
|
||||
sort: true,
|
||||
multiple: this.multiple,
|
||||
optional: this.optional || this.rmempty,
|
||||
disabled: (this.readonly != null) ? this.readonly : this.map.readonly,
|
||||
select_placeholder: E('em', _('unspecified')),
|
||||
display_items: this.display_size || this.size || 3,
|
||||
dropdown_items: this.dropdown_size || this.size || 5,
|
||||
validate: L.bind(this.validate, this, section_id),
|
||||
datatype: datatype_str,
|
||||
create: !this.nocreate,
|
||||
create_markup: '' +
|
||||
'<li data-value="{{value}}">' +
|
||||
'<span class="zonebadge" style="background:repeating-linear-gradient(45deg,rgba(204,204,204,0.5),rgba(204,204,204,0.5) 5px,rgba(255,255,255,0.5) 5px,rgba(255,255,255,0.5) 10px)">' +
|
||||
'<strong>{{value}}:</strong> <em>('+_('create')+')</em>' +
|
||||
'</span>' +
|
||||
'</li>'
|
||||
});
|
||||
|
||||
const elem = widget.render();
|
||||
|
||||
if (this.option == 'src') {
|
||||
elem.addEventListener('cbi-dropdown-change', L.bind(function(ev) {
|
||||
const opt = this.map.lookupOption('dest', section_id);
|
||||
const val = ev.detail.instance.getValue();
|
||||
|
||||
if (opt == null)
|
||||
return;
|
||||
|
||||
const cbid = opt[0].cbid(section_id);
|
||||
const label = document.querySelector('label[for="widget.%s"]'.format(cbid));
|
||||
const node = document.getElementById(cbid);
|
||||
|
||||
L.dom.content(label, val == '' ? _('Output zone') : _('Destination zone'));
|
||||
|
||||
if (val == '') {
|
||||
if (L.dom.callClassMethod(node, 'getValue') == '')
|
||||
L.dom.callClassMethod(node, 'setValue', '*');
|
||||
|
||||
const emptyval = node.querySelector('[data-value=""]');
|
||||
const anyval = node.querySelector('[data-value="*"]');
|
||||
|
||||
L.dom.content(anyval.querySelector('span'), E('strong', _('Any zone')));
|
||||
|
||||
if (emptyval != null)
|
||||
emptyval.parentNode.removeChild(emptyval);
|
||||
}
|
||||
else {
|
||||
const anyval = node.querySelector('[data-value="*"]') || '';
|
||||
let emptyval = node.querySelector('[data-value=""]') || '';
|
||||
|
||||
if (emptyval == null && anyval) {
|
||||
emptyval = anyval.cloneNode(true);
|
||||
emptyval.removeAttribute('display');
|
||||
emptyval.removeAttribute('selected');
|
||||
emptyval.setAttribute('data-value', '');
|
||||
}
|
||||
|
||||
if (opt[0]?.allowlocal && emptyval)
|
||||
L.dom.content(emptyval.querySelector('span'), [
|
||||
E('strong', _('Device')), E('span', ' (%s)'.format(_('input')))
|
||||
]);
|
||||
if (opt[0]?.allowany && anyval && emptyval) {
|
||||
L.dom.content(anyval.querySelector('span'), [
|
||||
E('strong', _('Any zone')), E('span', ' (%s)'.format(_('forward')))
|
||||
]);
|
||||
|
||||
anyval.parentNode.insertBefore(emptyval, anyval);
|
||||
}
|
||||
}
|
||||
|
||||
}, this));
|
||||
}
|
||||
else if (isOutputOnly) {
|
||||
const emptyval = elem.querySelector('[data-value=""]');
|
||||
emptyval.parentNode.removeChild(emptyval);
|
||||
}
|
||||
|
||||
return elem;
|
||||
},
|
||||
});
|
||||
|
||||
var CBIZoneForwards = form.DummyValue.extend({
|
||||
__name__: 'CBI.ZoneForwards',
|
||||
|
||||
load(section_id) {
|
||||
return Promise.all([
|
||||
firewall.getDefaults(),
|
||||
firewall.getZones(),
|
||||
network.getNetworks(),
|
||||
network.getDevices()
|
||||
]).then(L.bind(function(dznd) {
|
||||
this.defaults = dznd[0];
|
||||
this.zones = dznd[1];
|
||||
this.networks = dznd[2];
|
||||
this.devices = dznd[3];
|
||||
|
||||
return this.super('load', section_id);
|
||||
}, this));
|
||||
},
|
||||
|
||||
renderZone(zone) {
|
||||
const name = zone.getName();
|
||||
const networks = zone.getNetworks();
|
||||
const devices = zone.getDevices();
|
||||
const subnets = zone.getSubnets();
|
||||
const ifaces = [];
|
||||
|
||||
for (let n of networks) {
|
||||
const network = this.networks.filter(function(net) { return net.getName() == n })[0];
|
||||
|
||||
if (!network)
|
||||
continue;
|
||||
|
||||
const span = E('span', {
|
||||
'class': 'ifacebadge' + (network.isUp() ? ' ifacebadge-active' : '')
|
||||
}, network.getName() + ': ');
|
||||
|
||||
const subdevs = getDevices(network);
|
||||
|
||||
for (let s of subdevs) {
|
||||
span.appendChild(E('img', {
|
||||
'title': s.getI18n(),
|
||||
'src': L.resource('icons/%s%s.svg'.format(s.getType(), s.isUp() ? '' : '_disabled'))
|
||||
}));
|
||||
}
|
||||
|
||||
if (!subdevs.length)
|
||||
span.appendChild(E('em', _('(empty)')));
|
||||
|
||||
ifaces.push(span);
|
||||
}
|
||||
|
||||
for (let d of devices) {
|
||||
const device = this.devices.filter(function(dev) { return dev.getName() == d })[0];
|
||||
const title = device ? device.getI18n() : _('Absent Interface');
|
||||
const type = device ? device.getType() : 'ethernet';
|
||||
const up = device ? device.isUp() : false;
|
||||
|
||||
ifaces.push(E('span', { 'class': 'ifacebadge' }, [
|
||||
E('img', {
|
||||
'title': title,
|
||||
'src': L.resource('icons/%s%s.svg'.format(type, up ? '' : '_disabled'))
|
||||
}),
|
||||
device ? device.getName() : d
|
||||
]));
|
||||
}
|
||||
|
||||
if (subnets.length > 0)
|
||||
ifaces.push(E('span', { 'class': 'ifacebadge' }, [ '{ %s }'.format(subnets.join('; ')) ]));
|
||||
|
||||
if (!ifaces.length)
|
||||
ifaces.push(E('span', { 'class': 'ifacebadge' }, E('em', _('(empty)'))));
|
||||
|
||||
return E('label', {
|
||||
'class': 'zonebadge cbi-tooltip-container',
|
||||
'style': firewall.getZoneColorStyle(zone)
|
||||
}, [
|
||||
E('strong', name),
|
||||
E('div', { 'class': 'cbi-tooltip' }, ifaces)
|
||||
]);
|
||||
},
|
||||
|
||||
renderWidget(section_id, option_index, cfgvalue) {
|
||||
const value = (cfgvalue != null) ? cfgvalue : this.default;
|
||||
const zone = this.zones.filter(function(z) { return z.getName() == value })[0];
|
||||
|
||||
if (!zone)
|
||||
return E([]);
|
||||
|
||||
const forwards = zone.getForwardingsBy('src');
|
||||
const dzones = [];
|
||||
|
||||
for (var i = 0; i < forwards.length; i++) {
|
||||
const dzone = forwards[i].getDestinationZone();
|
||||
|
||||
if (!dzone)
|
||||
continue;
|
||||
|
||||
dzones.push(this.renderZone(dzone));
|
||||
}
|
||||
|
||||
if (!dzones.length)
|
||||
dzones.push(E('label', { 'class': 'zonebadge zonebadge-empty' },
|
||||
E('strong', this.defaults.getForward())));
|
||||
else
|
||||
dzones.push(E('label', { 'class': 'zonebadge zonebadge-empty' },
|
||||
E('strong', '%s %s'.format(this.defaults.getForward(), ('all others')))));
|
||||
|
||||
return E('div', { 'class': 'zone-forwards' }, [
|
||||
E('div', { 'class': 'zone-src' }, this.renderZone(zone)),
|
||||
E('span', '⇒'),
|
||||
E('div', { 'class': 'zone-dest' }, dzones)
|
||||
]);
|
||||
},
|
||||
});
|
||||
|
||||
const CBIIPSelect = form.ListValue.extend({
|
||||
__name__: 'CBI.IPSelect',
|
||||
|
||||
load(section_id) {
|
||||
return network.getDevices().then(L.bind(function(devices) {
|
||||
this.devices = devices;
|
||||
return this.super('load', section_id);
|
||||
}, this));
|
||||
},
|
||||
|
||||
filter(section_id, value) {
|
||||
return true;
|
||||
},
|
||||
|
||||
renderIfaceBadge(device, ip) {
|
||||
return E('div', {}, [
|
||||
ip,
|
||||
' ',
|
||||
E('span', { 'class': 'ifacebadge', }, [ device.getName(),
|
||||
E('img', {
|
||||
'title': device.getI18n(),
|
||||
'src': L.resource('icons/%s%s.svg'.format(device.getType(), device.isUp() ? '' : '_disabled'))
|
||||
})
|
||||
]),
|
||||
]);
|
||||
},
|
||||
|
||||
renderWidget(section_id, option_index, cfgvalue) {
|
||||
let values = L.toArray((cfgvalue != null) ? cfgvalue : this.default);
|
||||
const choices = {};
|
||||
const checked = {};
|
||||
|
||||
for (const val of values)
|
||||
checked[val] = true;
|
||||
|
||||
values = [];
|
||||
|
||||
if (!this.multiple && (this.rmempty || this.optional))
|
||||
choices[''] = E('em', _('unspecified'));
|
||||
|
||||
|
||||
for (const device of (this.devices || [])) {
|
||||
const name = device.getName();
|
||||
|
||||
if (name == this.exclude || !this.filter(section_id, name))
|
||||
continue;
|
||||
|
||||
if (name == 'loopback' && !this.loopback)
|
||||
continue;
|
||||
|
||||
if (this.novirtual && device.isVirtual())
|
||||
continue;
|
||||
|
||||
for (const ip of [...device.getIPAddrs(), ...device.getIP6Addrs()]) {
|
||||
const iponly = ip.split('/')?.[0]
|
||||
if (checked[iponly])
|
||||
values.push(iponly);
|
||||
choices[iponly] = this.renderIfaceBadge(device, iponly);
|
||||
}
|
||||
}
|
||||
|
||||
const widget = new ui.Dropdown(this.multiple ? values : values[0], choices, {
|
||||
id: this.cbid(section_id),
|
||||
sort: true,
|
||||
multiple: this.multiple,
|
||||
optional: this.optional || this.rmempty,
|
||||
disabled: (this.readonly != null) ? this.readonly : this.map.readonly,
|
||||
select_placeholder: E('em', _('unspecified')),
|
||||
display_items: this.display_size || this.size || 2,
|
||||
dropdown_items: this.dropdown_size || this.size || 5,
|
||||
datatype: this.multiple ? 'list(ipaddr)' : 'ipaddr',
|
||||
validate: L.bind(this.validate, this, section_id),
|
||||
create: false,
|
||||
});
|
||||
|
||||
return widget.render();
|
||||
},
|
||||
|
||||
textvalue(section_id) {
|
||||
const cfgvalue = this.cfgvalue(section_id);
|
||||
const values = L.toArray((cfgvalue != null) ? cfgvalue : this.default);
|
||||
const rv = E([]);
|
||||
|
||||
for (const device of (this.devices || [])) {
|
||||
for (const ip of [...device.getIPAddrs(), ...device.getIP6Addrs()]) {
|
||||
const iponly = ip.split('/')[0];
|
||||
if (values.indexOf(iponly) === -1)
|
||||
continue;
|
||||
|
||||
if (rv.childNodes.length)
|
||||
rv.appendChild(document.createTextNode(' '));
|
||||
|
||||
rv.appendChild(this.renderIfaceBadge(device, iponly));
|
||||
}
|
||||
}
|
||||
|
||||
if (!rv.firstChild)
|
||||
rv.appendChild(E('em', _('unspecified')));
|
||||
|
||||
return rv;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
var CBINetworkSelect = form.ListValue.extend({
|
||||
__name__: 'CBI.NetworkSelect',
|
||||
|
||||
load(section_id) {
|
||||
return network.getNetworks().then(L.bind(function(networks) {
|
||||
this.networks = networks;
|
||||
|
||||
return this.super('load', section_id);
|
||||
}, this));
|
||||
},
|
||||
|
||||
filter(section_id, value) {
|
||||
return true;
|
||||
},
|
||||
|
||||
renderIfaceBadge(network) {
|
||||
const span = E('span', { 'class': 'ifacebadge' }, network.getName() + ': ');
|
||||
const devices = getDevices(network);
|
||||
|
||||
for (let d of devices) {
|
||||
span.appendChild(E('img', {
|
||||
'title': d.getI18n(),
|
||||
'src': L.resource('icons/%s%s.svg'.format(d.getType(), d.isUp() ? '' : '_disabled'))
|
||||
}));
|
||||
}
|
||||
|
||||
if (!devices.length) {
|
||||
span.appendChild(E('em', { 'class': 'hide-close' }, _('(no interfaces attached)')));
|
||||
span.appendChild(E('em', { 'class': 'hide-open' }, '-'));
|
||||
}
|
||||
|
||||
return span;
|
||||
},
|
||||
|
||||
renderWidget(section_id, option_index, cfgvalue) {
|
||||
let values = L.toArray((cfgvalue != null) ? cfgvalue : this.default);
|
||||
const choices = {};
|
||||
const checked = {};
|
||||
|
||||
for (var i = 0; i < values.length; i++)
|
||||
checked[values[i]] = true;
|
||||
|
||||
values = [];
|
||||
|
||||
if (!this.multiple && (this.rmempty || this.optional))
|
||||
choices[''] = E('em', _('unspecified'));
|
||||
|
||||
for (let network of this.networks) {
|
||||
const name = network.getName();
|
||||
|
||||
if (name == this.exclude || !this.filter(section_id, name))
|
||||
continue;
|
||||
|
||||
if (name == 'loopback' && !this.loopback)
|
||||
continue;
|
||||
|
||||
if (this.novirtual && network.isVirtual())
|
||||
continue;
|
||||
|
||||
if (checked[name])
|
||||
values.push(name);
|
||||
|
||||
choices[name] = this.renderIfaceBadge(network);
|
||||
}
|
||||
|
||||
const widget = new ui.Dropdown(this.multiple ? values : values[0], choices, {
|
||||
id: this.cbid(section_id),
|
||||
sort: true,
|
||||
multiple: this.multiple,
|
||||
optional: this.optional || this.rmempty,
|
||||
disabled: (this.readonly != null) ? this.readonly : this.map.readonly,
|
||||
select_placeholder: E('em', _('unspecified')),
|
||||
display_items: this.display_size || this.size || 3,
|
||||
dropdown_items: this.dropdown_size || this.size || 5,
|
||||
datatype: this.multiple ? 'list(uciname)' : 'uciname',
|
||||
validate: L.bind(this.validate, this, section_id),
|
||||
create: !this.nocreate,
|
||||
create_markup: '' +
|
||||
'<li data-value="{{value}}">' +
|
||||
'<span class="ifacebadge" style="background:repeating-linear-gradient(45deg,rgba(204,204,204,0.5),rgba(204,204,204,0.5) 5px,rgba(255,255,255,0.5) 5px,rgba(255,255,255,0.5) 10px)">' +
|
||||
'{{value}}: <em>('+_('create')+')</em>' +
|
||||
'</span>' +
|
||||
'</li>'
|
||||
});
|
||||
|
||||
return widget.render();
|
||||
},
|
||||
|
||||
textvalue(section_id) {
|
||||
const cfgvalue = this.cfgvalue(section_id);
|
||||
const values = L.toArray((cfgvalue != null) ? cfgvalue : this.default);
|
||||
const rv = E([]);
|
||||
|
||||
for (let network of this.networks) {
|
||||
const name = network.getName();
|
||||
|
||||
if (values.indexOf(name) == -1)
|
||||
continue;
|
||||
|
||||
if (rv.length)
|
||||
L.dom.append(rv, ' ');
|
||||
|
||||
L.dom.append(rv, this.renderIfaceBadge(network));
|
||||
}
|
||||
|
||||
if (!rv.firstChild)
|
||||
rv.appendChild(E('em', _('unspecified')));
|
||||
|
||||
return rv;
|
||||
},
|
||||
});
|
||||
|
||||
var CBIDeviceSelect = form.ListValue.extend({
|
||||
__name__: 'CBI.DeviceSelect',
|
||||
|
||||
load(section_id) {
|
||||
return Promise.all([
|
||||
network.getDevices(),
|
||||
this.noaliases ? null : network.getNetworks()
|
||||
]).then(L.bind(function(data) {
|
||||
this.devices = data[0];
|
||||
this.networks = data[1];
|
||||
|
||||
return this.super('load', section_id);
|
||||
}, this));
|
||||
},
|
||||
|
||||
filter(section_id, value) {
|
||||
return true;
|
||||
},
|
||||
|
||||
renderWidget(section_id, option_index, cfgvalue) {
|
||||
var values = L.toArray((cfgvalue != null) ? cfgvalue : this.default),
|
||||
choices = {},
|
||||
checked = {},
|
||||
order = [];
|
||||
|
||||
for (var i = 0; i < values.length; i++)
|
||||
checked[values[i]] = true;
|
||||
|
||||
values = [];
|
||||
|
||||
if (!this.multiple && (this.rmempty || this.optional))
|
||||
choices[''] = E('em', _('unspecified'));
|
||||
|
||||
for (let device of this.devices) {
|
||||
const name = device.getName();
|
||||
const type = device.getType();
|
||||
|
||||
if (name == 'lo' || name == this.exclude || !this.filter(section_id, name))
|
||||
continue;
|
||||
|
||||
if (this.noaliases && type == 'alias')
|
||||
continue;
|
||||
|
||||
if (this.nobridges && type == 'bridge')
|
||||
continue;
|
||||
|
||||
if (this.noinactive && device.isUp() == false)
|
||||
continue;
|
||||
|
||||
const item = E([
|
||||
E('img', {
|
||||
'title': device.getI18n(),
|
||||
'src': L.resource('icons/%s%s.svg'.format(type, device.isUp() ? '' : '_disabled'))
|
||||
}),
|
||||
E('span', { 'class': 'hide-open' }, [ name ]),
|
||||
E('span', { 'class': 'hide-close'}, [ device.getI18n() ])
|
||||
]);
|
||||
|
||||
const networks = device.getNetworks();
|
||||
|
||||
if (networks.length > 0)
|
||||
L.dom.append(item.lastChild, [ ' (', networks.map(function(n) { return n.getName() }).join(', '), ')' ]);
|
||||
|
||||
if (checked[name])
|
||||
values.push(name);
|
||||
|
||||
choices[name] = item;
|
||||
order.push(name);
|
||||
}
|
||||
|
||||
if (this.networks != null) {
|
||||
for (let net of this.networks) {
|
||||
const device = network.instantiateDevice('@%s'.format(net.getName()), net);
|
||||
const name = device.getName();
|
||||
|
||||
if (name == '@loopback' || name == this.exclude || !this.filter(section_id, name))
|
||||
continue;
|
||||
|
||||
if (this.noinactive && net.isUp() == false)
|
||||
continue;
|
||||
|
||||
const item = E([
|
||||
E('img', {
|
||||
'title': device.getI18n(),
|
||||
'src': L.resource('icons/alias%s.svg'.format(device.isUp() ? '' : '_disabled'))
|
||||
}),
|
||||
E('span', { 'class': 'hide-open' }, [ name ]),
|
||||
E('span', { 'class': 'hide-close'}, [ device.getI18n() ])
|
||||
]);
|
||||
|
||||
if (checked[name])
|
||||
values.push(name);
|
||||
|
||||
choices[name] = item;
|
||||
order.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.includeips) {
|
||||
this.devices.forEach(net_dev => {
|
||||
['getIPAddrs', 'getIP6Addrs'].forEach(fn => {
|
||||
net_dev[fn]().forEach(addr => {
|
||||
const name = addr.split('/')[0];
|
||||
if (checked[name]) values.push(name);
|
||||
|
||||
choices[name] = E([], [name, ' (', E('strong', net_dev.getName()), ')']);
|
||||
order.push(name);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.nocreate) {
|
||||
const keys = Object.keys(checked).sort(L.naturalCompare);
|
||||
|
||||
for (let k of keys) {
|
||||
if (choices.hasOwnProperty(k))
|
||||
continue;
|
||||
|
||||
choices[k] = E([
|
||||
E('img', {
|
||||
'title': _('Absent Interface'),
|
||||
'src': L.resource('icons/ethernet_disabled.svg')
|
||||
}),
|
||||
E('span', { 'class': 'hide-open' }, [ k ]),
|
||||
E('span', { 'class': 'hide-close'}, [ '%s: "%h"'.format(_('Absent Interface'), k) ])
|
||||
]);
|
||||
|
||||
values.push(k);
|
||||
order.push(k);
|
||||
}
|
||||
}
|
||||
|
||||
const widget = new ui.Dropdown(this.multiple ? values : values[0], choices, {
|
||||
id: this.cbid(section_id),
|
||||
sort: order,
|
||||
multiple: this.multiple,
|
||||
optional: this.optional || this.rmempty,
|
||||
disabled: (this.readonly != null) ? this.readonly : this.map.readonly,
|
||||
select_placeholder: E('em', _('unspecified')),
|
||||
display_items: this.display_size || this.size || 3,
|
||||
dropdown_items: this.dropdown_size || this.size || 5,
|
||||
validate: L.bind(this.validate, this, section_id),
|
||||
create: !this.nocreate,
|
||||
create_markup: '' +
|
||||
'<li data-value="{{value}}">' +
|
||||
'<img title="'+_('Custom Interface')+': "{{value}}"" src="'+L.resource('icons/ethernet_disabled.svg')+'" />' +
|
||||
'<span class="hide-open">{{value}}</span>' +
|
||||
'<span class="hide-close">'+_('Custom Interface')+': "{{value}}"</span>' +
|
||||
'</li>'
|
||||
});
|
||||
|
||||
return widget.render();
|
||||
},
|
||||
});
|
||||
|
||||
var CBIUserSelect = form.ListValue.extend({
|
||||
__name__: 'CBI.UserSelect',
|
||||
|
||||
load(section_id) {
|
||||
return getUsers().then(L.bind(function(users) {
|
||||
delete this.keylist;
|
||||
delete this.vallist;
|
||||
for (var i = 0; i < users.length; i++) {
|
||||
this.value(users[i]);
|
||||
}
|
||||
|
||||
return this.super('load', section_id);
|
||||
}, this));
|
||||
},
|
||||
|
||||
filter(section_id, value) {
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
var CBIGroupSelect = form.ListValue.extend({
|
||||
__name__: 'CBI.GroupSelect',
|
||||
|
||||
load(section_id) {
|
||||
return getGroups().then(L.bind(function(groups) {
|
||||
for (var i = 0; i < groups.length; i++) {
|
||||
this.value(groups[i]);
|
||||
}
|
||||
|
||||
return this.super('load', section_id);
|
||||
}, this));
|
||||
},
|
||||
|
||||
filter(section_id, value) {
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
var CBIWifidogxGroupSelect = form.ListValue.extend({
|
||||
__name__: 'CBI.WifidogxGroupSelect',
|
||||
|
||||
// group_type: 1: Domain Group, 2: MAC Group
|
||||
// add a new property to store the group type
|
||||
group_type: '1',
|
||||
|
||||
load: function(section_id) {
|
||||
this.sappList = [];
|
||||
var sections = uci.sections('wifidogx', 'group');
|
||||
|
||||
for (var i = 0; i < sections.length; i++) {
|
||||
if (sections[i]['g_type'] == this.group_type)
|
||||
this.sappList.push(sections[i]['.name']);
|
||||
}
|
||||
|
||||
return this.super('load', section_id);
|
||||
},
|
||||
|
||||
setGroupType: function(group_type) {
|
||||
if (group_type == 'mac') {
|
||||
this.group_type = '2';
|
||||
} else if (group_type == 'wildcard') {
|
||||
this.group_type = '3';
|
||||
}
|
||||
},
|
||||
|
||||
filter: function(section_id, value) {
|
||||
return true;
|
||||
},
|
||||
|
||||
renderWidget: function(section_id, option_index, cfgvalue) {
|
||||
var values = L.toArray((cfgvalue != null) ? cfgvalue : this.default),
|
||||
choices = {},
|
||||
checked = {};
|
||||
|
||||
for (var i = 0; i < values.length; i++)
|
||||
checked[values[i]] = true;
|
||||
|
||||
values = [];
|
||||
|
||||
if (!this.multiple && (this.rmempty || this.optional))
|
||||
choices[''] = E('em', _('unspecified'));
|
||||
|
||||
for (var i = 0; i < this.sappList.length; i++) {
|
||||
var name = this.sappList[i];
|
||||
|
||||
if (checked[name])
|
||||
values.push(name);
|
||||
|
||||
choices[name] = E('span', { 'class': 'ifacebadge' }, name);
|
||||
}
|
||||
|
||||
var widget = new ui.Dropdown(this.multiple ? values : values[0], choices, {
|
||||
id: this.cbid(section_id),
|
||||
sort: true,
|
||||
multiple: this.multiple,
|
||||
optional: this.optional || this.rmempty,
|
||||
disabled: (this.readonly != null) ? this.readonly : this.map.readonly,
|
||||
select_placeholder: E('em', _('unspecified')),
|
||||
display_items: this.display_size || this.size || 3,
|
||||
dropdown_items: this.dropdown_size || this.size || 5,
|
||||
validate: L.bind(this.validate, this, section_id),
|
||||
create: !this.nocreate,
|
||||
create_markup: '' +
|
||||
'<li data-value="{{value}}">' +
|
||||
'<span class="ifacebadge" style="background:repeating-linear-gradient(45deg,rgba(204,204,204,0.5),rgba(204,204,204,0.5) 5px,rgba(255,255,255,0.5) 5px,rgba(255,255,255,0.5) 10px)">' +
|
||||
'{{value}}: <em>('+_('create')+')</em>' +
|
||||
'</span>' +
|
||||
'</li>'
|
||||
});
|
||||
|
||||
return widget.render();
|
||||
}
|
||||
});
|
||||
|
||||
return L.Class.extend({
|
||||
ZoneSelect: CBIZoneSelect,
|
||||
ZoneForwards: CBIZoneForwards,
|
||||
IPSelect: CBIIPSelect,
|
||||
NetworkSelect: CBINetworkSelect,
|
||||
DeviceSelect: CBIDeviceSelect,
|
||||
UserSelect: CBIUserSelect,
|
||||
GroupSelect: CBIGroupSelect,
|
||||
WifidogxGroupSelect: CBIWifidogxGroupSelect,
|
||||
});
|
||||
Reference in new issue
Block a user