diff --git a/docs/jsapi/LuCI.baseclass.html b/docs/jsapi/LuCI.baseclass.html index 88d5230950ca..f5da82c7ece1 100644 --- a/docs/jsapi/LuCI.baseclass.html +++ b/docs/jsapi/LuCI.baseclass.html @@ -74,12 +74,16 @@
Make option element readonly.
+This property defaults to the readonly state of the parent form element.
+When set to true
, the underlying widget is rendered in disabled state,
+means its contents cannot be changed and the widget cannot be interacted
+with.
Make option element readonly.
+This property defaults to the readonly state of the parent form element.
+When set to true
, the underlying widget is rendered in disabled state,
+means its contents cannot be changed and the widget cannot be interacted
+with.
Make option element readonly.
+This property defaults to the readonly state of the parent form element.
+When set to true
, the underlying widget is rendered in disabled state,
+means its contents cannot be changed and the widget cannot be interacted
+with.
Make option element readonly.
+This property defaults to the readonly state of the parent form element.
+When set to true
, the underlying widget is rendered in disabled state,
+means its contents cannot be changed and the widget cannot be interacted
+with.
Make option element readonly.
+This property defaults to the readonly state of the parent form element.
+When set to true
, the underlying widget is rendered in disabled state,
+means its contents cannot be changed and the widget cannot be interacted
+with.
Make option element readonly.
+This property defaults to the readonly state of the parent form element.
+When set to true
, the underlying widget is rendered in disabled state,
+means its contents cannot be changed and the widget cannot be interacted
+with.
Make option element readonly.
+This property defaults to the readonly state of the parent form element.
+When set to true
, the underlying widget is rendered in disabled state,
+means its contents cannot be changed and the widget cannot be interacted
+with.
Toggle readonly state of the form.
+If set to true
, the Map instance is marked readonly and any form
+option elements added to it will inherit the readonly state.
If left unset, the Map will test the access permission of the primary +uci configuration upon loading and mark the form readonly if no write +permissions are granted.
+Make option element readonly.
+This property defaults to the readonly state of the parent form element.
+When set to true
, the underlying widget is rendered in disabled state,
+means its contents cannot be changed and the widget cannot be interacted
+with.
Toggle readonly state of the form.
+If set to true
, the Map instance is marked readonly and any form
+option elements added to it will inherit the readonly state.
If left unset, the Map will test the access permission of the primary +uci configuration upon loading and mark the form readonly if no write +permissions are granted.
+Make option element readonly.
+This property defaults to the readonly state of the parent form element.
+When set to true
, the underlying widget is rendered in disabled state,
+means its contents cannot be changed and the widget cannot be interacted
+with.
Make option element readonly.
+This property defaults to the readonly state of the parent form element.
+When set to true
, the underlying widget is rendered in disabled state,
+means its contents cannot be changed and the widget cannot be interacted
+with.
Make option element readonly.
+This property defaults to the readonly state of the parent form element.
+When set to true
, the underlying widget is rendered in disabled state,
+means its contents cannot be changed and the widget cannot be interacted
+with.
Make option element readonly.
+This property defaults to the readonly state of the parent form element.
+When set to true
, the underlying widget is rendered in disabled state,
+means its contents cannot be changed and the widget cannot be interacted
+with.
Construct an absolute filesystem path relative to the server +document root.
+Name | + + +Type | + + + +Description | +
---|---|---|
parts |
+
+
+ + + +string + + + + | + + + +
+
+
+ optional
+
+
+
+
+ repeatable
+
+
+ An array of parts to join into a path. |
+
Type | +Description | +
---|---|
+ + string + + + | +Return the joined path. | +
Check whether a view has sufficient permissions.
+Type | +Description | +
---|---|
+ + boolean + | + + null + + + | +Returns null if the current session has no permission at all to
+load resources required by the view. Returns false if readonly
+permissions are granted or true if at least one required ACL
+group is granted with write permissions. |
+
The name of the remote ubus
object to invoke.
The name of the remote ubus
method to invoke.
Lists the named parameters expected by the remote ubus
RPC method.
The arguments passed to the resulting generated method call function
@@ -4683,6 +4795,10 @@
Describes the expected return data structure. The given object is supposed to contain a single key selecting the value to use from @@ -4743,12 +4859,55 @@
Specfies an optional filter function which is invoked to transform the received reply data before it is returned to the caller.
reject
If set to true
, non-zero ubus call status codes are treated as fatal
+error and lead to the rejection of the call promise. The default
+behaviour is to resolve with the call return code value instead.
disabled
Specifies whether the widget should be rendered in disabled state
+(true
) or not (false
). Disabled widgets cannot be interacted with
+and are displayed in a slightly faded style.
'use strict';
'require ui';
'require uci';
+'require rpc';
'require dom';
'require baseclass';
var scope = this;
+var callSessionAccess = rpc.declare({
+ object: 'session',
+ method: 'access',
+ params: [ 'scope', 'object', 'function' ],
+ expect: { 'access': false }
+});
+
var CBIJSONConfig = baseclass.extend({
__init__: function(data) {
data = Object.assign({}, data);
@@ -3581,6 +3687,20 @@ Source: form.js
this.data = uci;
},
+ /**
+ * Toggle readonly state of the form.
+ *
+ * If set to `true`, the Map instance is marked readonly and any form
+ * option elements added to it will inherit the readonly state.
+ *
+ * If left unset, the Map will test the access permission of the primary
+ * uci configuration upon loading and mark the form readonly if no write
+ * permissions are granted.
+ *
+ * @name LuCI.form.Map.prototype#readonly
+ * @type boolean
+ */
+
/**
* Find all DOM nodes within this Map which match the given search
* parameters. This function is essentially a convenience wrapper around
@@ -3727,8 +3847,17 @@ Source: form.js
* an error.
*/
load: function() {
- return this.data.load(this.parsechain || [ this.config ])
- .then(this.loadChildren.bind(this));
+ var doCheckACL = (!(this instanceof CBIJSONMap) && this.readonly == null);
+
+ return Promise.all([
+ doCheckACL ? callSessionAccess('uci', this.config, 'write') : true,
+ this.data.load(this.parsechain || [ this.config ])
+ ]).then(L.bind(function(res) {
+ if (res[0] === false)
+ this.readonly = true;
+
+ return this.loadChildren();
+ }, this));
},
/**
@@ -3782,11 +3911,18 @@ Source: form.js
.then(this.data.save.bind(this.data))
.then(this.load.bind(this))
.catch(function(e) {
- if (!silent)
- alert('Cannot save due to invalid values');
+ if (!silent) {
+ ui.showModal(_('Save error'), [
+ E('p', {}, [ _('An error occurred while saving the form:') ]),
+ E('p', {}, [ E('em', { 'style': 'white-space:pre' }, [ e.message ]) ]),
+ E('div', { 'class': 'right' }, [
+ E('button', { 'click': ui.hideModal }, [ _('Dismiss') ])
+ ])
+ ]);
+ }
- return Promise.reject();
- }).finally(this.renderContents.bind(this));
+ return Promise.reject(e);
+ }).then(this.renderContents.bind(this));
},
/**
@@ -4519,6 +4655,19 @@ Source: form.js
* @default null
*/
+ /**
+ * Make option element readonly.
+ *
+ * This property defaults to the readonly state of the parent form element.
+ * When set to `true`, the underlying widget is rendered in disabled state,
+ * means its contents cannot be changed and the widget cannot be interacted
+ * with.
+ *
+ * @name LuCI.form.AbstractValue.prototype#readonly
+ * @type boolean
+ * @default false
+ */
+
/**
* Override the cell width of a table or grid section child option.
*
@@ -4972,8 +5121,10 @@ Source: form.js
cval = this.cfgvalue(section_id),
fval = active ? this.formvalue(section_id) : null;
- if (active && !this.isValid(section_id))
- return Promise.reject();
+ if (active && !this.isValid(section_id)) {
+ var title = this.stripTags(this.title).trim();
+ return Promise.reject(new TypeError(_('Option "%s" contains an invalid input value.').format(title || this.option)));
+ }
if (fval != '' && fval != null) {
if (this.forcewrite || !isEqual(cval, fval))
@@ -4984,8 +5135,8 @@ Source: form.js
return Promise.resolve(this.remove(section_id));
}
else if (!isEqual(cval, fval)) {
- console.log('This should have been catched by isValid()');
- return Promise.reject();
+ var title = this.stripTags(this.title).trim();
+ return Promise.reject(new TypeError(_('Option "%s" must not be empty.').format(title || this.option)));
}
}
@@ -5169,13 +5320,15 @@ Source: form.js
createEl.appendChild(E('button', {
'class': 'cbi-button cbi-button-add',
'title': btn_title || _('Add'),
- 'click': ui.createHandlerFn(this, 'handleAdd')
+ 'click': ui.createHandlerFn(this, 'handleAdd'),
+ 'disabled': this.map.readonly || null
}, [ btn_title || _('Add') ]));
}
else {
var nameEl = E('input', {
'type': 'text',
- 'class': 'cbi-section-create-name'
+ 'class': 'cbi-section-create-name',
+ 'disabled': this.map.readonly || null
});
dom.append(createEl, [
@@ -5190,7 +5343,8 @@ Source: form.js
return;
return this.handleAdd(ev, nameEl.value);
- })
+ }),
+ 'disabled': this.map.readonly || null
})
]);
@@ -5233,7 +5387,8 @@ Source: form.js
'class': 'cbi-button',
'name': 'cbi.rts.%s.%s'.format(config_name, cfgsections[i]),
'data-section-id': cfgsections[i],
- 'click': ui.createHandlerFn(this, 'handleRemove', cfgsections[i])
+ 'click': ui.createHandlerFn(this, 'handleRemove', cfgsections[i]),
+ 'disabled': this.map.readonly || null
}, [ _('Delete') ])));
}
@@ -5609,7 +5764,8 @@ Source: form.js
E('div', {
'title': _('Drag to reorder'),
'class': 'btn cbi-button drag-handle center',
- 'style': 'cursor:move'
+ 'style': 'cursor:move',
+ 'disabled': this.map.readonly || null
}, '☰')
]);
}
@@ -5650,7 +5806,8 @@ Source: form.js
E('button', {
'title': btn_title || _('Delete'),
'class': 'cbi-button cbi-button-remove',
- 'click': ui.createHandlerFn(this, 'handleRemove', section_id)
+ 'click': ui.createHandlerFn(this, 'handleRemove', section_id),
+ 'disabled': this.map.readonly || null
}, [ btn_title || _('Delete') ])
);
}
@@ -5801,6 +5958,7 @@ Source: form.js
s = m.section(CBINamedSection, section_id, this.sectiontype);
m.parent = parent;
+ m.readonly = parent.readonly;
s.tabs = this.tabs;
s.tab_names = this.tab_names;
@@ -5848,7 +6006,8 @@ Source: form.js
}, [ _('Dismiss') ]), ' ',
E('button', {
'class': 'cbi-button cbi-button-positive important',
- 'click': ui.createHandlerFn(this, 'handleModalSave', m)
+ 'click': ui.createHandlerFn(this, 'handleModalSave', m),
+ 'disabled': m.readonly || null
}, [ _('Save') ])
])
], 'cbi-modal');
@@ -6135,7 +6294,8 @@ Source: form.js
E('div', { 'class': 'cbi-section-remove right' },
E('button', {
'class': 'cbi-button',
- 'click': ui.createHandlerFn(this, 'handleRemove')
+ 'click': ui.createHandlerFn(this, 'handleRemove'),
+ 'disabled': this.map.readonly || null
}, [ _('Delete') ])));
}
@@ -6150,7 +6310,8 @@ Source: form.js
sectionEl.appendChild(
E('button', {
'class': 'cbi-button cbi-button-add',
- 'click': ui.createHandlerFn(this, 'handleAdd')
+ 'click': ui.createHandlerFn(this, 'handleAdd'),
+ 'disabled': this.map.readonly || null
}, [ _('Add') ]));
}
@@ -6344,7 +6505,8 @@ Source: form.js
optional: this.optional || this.rmempty,
datatype: this.datatype,
select_placeholder: this.placeholder || placeholder,
- validate: L.bind(this.validate, this, section_id)
+ validate: L.bind(this.validate, this, section_id),
+ disabled: (this.readonly != null) ? this.readonly : this.map.readonly
});
}
else {
@@ -6354,7 +6516,8 @@ Source: form.js
optional: this.optional || this.rmempty,
datatype: this.datatype,
placeholder: this.placeholder,
- validate: L.bind(this.validate, this, section_id)
+ validate: L.bind(this.validate, this, section_id),
+ disabled: (this.readonly != null) ? this.readonly : this.map.readonly
});
}
@@ -6409,7 +6572,8 @@ Source: form.js
optional: this.optional || this.rmempty,
datatype: this.datatype,
placeholder: this.placeholder,
- validate: L.bind(this.validate, this, section_id)
+ validate: L.bind(this.validate, this, section_id),
+ disabled: (this.readonly != null) ? this.readonly : this.map.readonly
});
return widget.render();
@@ -6474,7 +6638,8 @@ Source: form.js
sort: this.keylist,
optional: this.optional,
placeholder: this.placeholder,
- validate: L.bind(this.validate, this, section_id)
+ validate: L.bind(this.validate, this, section_id),
+ disabled: (this.readonly != null) ? this.readonly : this.map.readonly
});
return widget.render();
@@ -6545,7 +6710,8 @@ Source: form.js
id: this.cbid(section_id),
value_enabled: this.enabled,
value_disabled: this.disabled,
- validate: L.bind(this.validate, this, section_id)
+ validate: L.bind(this.validate, this, section_id),
+ disabled: (this.readonly != null) ? this.readonly : this.map.readonly
});
return widget.render();
@@ -6584,8 +6750,10 @@ Source: form.js
if (this.isActive(section_id)) {
var fval = this.formvalue(section_id);
- if (!this.isValid(section_id))
- return Promise.reject();
+ if (!this.isValid(section_id)) {
+ var title = this.stripTags(this.title).trim();
+ return Promise.reject(new TypeError(_('Option "%s" contains an invalid input value.').format(title || this.option)));
+ }
if (fval == this.default && (this.optional || this.rmempty))
return Promise.resolve(this.remove(section_id));
@@ -6671,7 +6839,8 @@ Source: form.js
select_placeholder: this.placeholder,
display_items: this.display_size || this.size || 3,
dropdown_items: this.dropdown_size || this.size || -1,
- validate: L.bind(this.validate, this, section_id)
+ validate: L.bind(this.validate, this, section_id),
+ disabled: (this.readonly != null) ? this.readonly : this.map.readonly
});
return widget.render();
@@ -6763,7 +6932,8 @@ Source: form.js
cols: this.cols,
rows: this.rows,
wrap: this.wrap,
- validate: L.bind(this.validate, this, section_id)
+ validate: L.bind(this.validate, this, section_id),
+ disabled: (this.readonly != null) ? this.readonly : this.map.readonly
});
return widget.render();
@@ -6833,7 +7003,7 @@ Source: form.js
hiddenEl = new ui.Hiddenfield(value, { id: this.cbid(section_id) }),
outputEl = E('div');
- if (this.href)
+ if (this.href && !((this.readonly != null) ? this.readonly : this.map.readonly))
outputEl.appendChild(E('a', { 'href': this.href }));
dom.append(outputEl.lastChild || outputEl,
@@ -6954,7 +7124,8 @@ Source: form.js
ev.currentTarget.parentNode.nextElementSibling.value = value;
return this.map.save();
- }, section_id)
+ }, section_id),
+ 'disabled': ((this.readonly != null) ? this.readonly : this.map.readonly) || null
}, [ btn_title ])
]);
else
@@ -7129,7 +7300,8 @@ Source: form.js
show_hidden: this.show_hidden,
enable_upload: this.enable_upload,
enable_remove: this.enable_remove,
- root_directory: this.root_directory
+ root_directory: this.root_directory,
+ disabled: (this.readonly != null) ? this.readonly : this.map.readonly
});
return browserEl.render();
@@ -7338,7 +7510,7 @@ Source: form.js
diff --git a/docs/jsapi/fs.js.html b/docs/jsapi/fs.js.html
index a356962f0474..160faf2000a9 100644
--- a/docs/jsapi/fs.js.html
+++ b/docs/jsapi/fs.js.html
@@ -74,12 +74,16 @@
error
+ fspath
+
get
halt
hasSystemFeature
+ hasViewPermission
+
isObject
location
@@ -331,6 +335,8 @@
optional
+ readonly
+
rmempty
uciconfig
@@ -423,6 +429,8 @@
placeholder
+ readonly
+
rmempty
uciconfig
@@ -511,6 +519,8 @@
placeholder
+ readonly
+
rmempty
uciconfig
@@ -595,6 +605,8 @@
placeholder
+ readonly
+
rmempty
uciconfig
@@ -687,6 +699,8 @@
placeholder
+ readonly
+
rmempty
uciconfig
@@ -775,6 +789,8 @@
placeholder
+ readonly
+
rmempty
uciconfig
@@ -929,6 +945,8 @@
placeholder
+ readonly
+
rmempty
uciconfig
@@ -997,6 +1015,10 @@