solid-ui
Version:
UI library for writing Solid read-write-web applications
507 lines (503 loc) • 19.1 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.AccessGroups = void 0;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _rdflib = require("rdflib");
var _acl = require("./acl");
var widgets = _interopRequireWildcard(require("../widgets"));
var ns = _interopRequireWildcard(require("../ns"));
var _addAgentButtons = require("./add-agent-buttons");
var debug = _interopRequireWildcard(require("../debug"));
var style = _interopRequireWildcard(require("../style"));
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t3 in e) "default" !== _t3 && {}.hasOwnProperty.call(e, _t3) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t3)) && (i.get || i.set) ? o(f, _t3, i) : f[_t3] = e[_t3]); return f; })(e, t); }
/**
* Contains the [[AccessGroups]]
* and [[AccessGroupsOptions]] classes
* @packageDocumentation
*/
var ACL = ns.acl;
var COLLOQUIAL = {
13: 'Owners',
9: 'Owners (write locked)',
5: 'Editors',
3: 'Posters',
2: 'Submitters',
1: 'Viewers'
};
var RECOMMENDED = {
13: true,
5: true,
3: true,
2: true,
1: true
};
var EXPLANATION = {
13: 'can read, write, and control sharing.',
9: 'can read and control sharing, currently write-locked.',
5: 'can read and change information',
3: 'can add new information, and read but not change existing information',
2: 'can add new information but not read any',
1: 'can read but not change information'
};
/**
* Type for the options parameter of [[AccessGroups]]
*/
/**
* Renders the table of Owners, Editors, Posters, Submitters, Viewers
* for https://github.com/solidos/userguide/blob/main/views/sharing/userguide.md
*/
var AccessGroups = exports.AccessGroups = /*#__PURE__*/function () {
// @@ was LiveStore but does not need to be connected to web
function AccessGroups(doc, aclDoc, controller, store) {
var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
(0, _classCallCheck2["default"])(this, AccessGroups);
this.doc = doc;
this.aclDoc = aclDoc;
this.controller = controller;
this.options = options;
(0, _defineProperty2["default"])(this, "defaults", void 0);
(0, _defineProperty2["default"])(this, "byCombo", void 0);
(0, _defineProperty2["default"])(this, "aclMap", void 0);
(0, _defineProperty2["default"])(this, "addAgentButton", void 0);
(0, _defineProperty2["default"])(this, "rootElement", void 0);
(0, _defineProperty2["default"])(this, "_store", void 0);
this.defaults = options.defaults || false;
this._store = store;
this.aclMap = (0, _acl.readACL)(doc, aclDoc, store, this.defaults);
this.byCombo = (0, _acl.ACLbyCombination)(this.aclMap);
this.addAgentButton = new _addAgentButtons.AddAgentButtons(this);
this.rootElement = this.controller.dom.createElement('div');
this.rootElement.setAttribute('style', style.accessGroupList);
}
return (0, _createClass2["default"])(AccessGroups, [{
key: "store",
get: function get() {
return this._store;
},
set: function set(store) {
this._store = store;
this.aclMap = (0, _acl.readACL)(this.doc, this.aclDoc, store, this.defaults);
this.byCombo = (0, _acl.ACLbyCombination)(this.aclMap);
}
}, {
key: "render",
value: function render() {
var _this = this;
this.rootElement.innerHTML = '';
this.renderGroups().forEach(function (group) {
return _this.rootElement.appendChild(group);
});
if (this.controller.isEditable) {
this.rootElement.appendChild(this.addAgentButton.render());
}
return this.rootElement;
}
}, {
key: "renderGroups",
value: function renderGroups() {
var groupElements = [];
for (var comboIndex = 15; comboIndex > 0; comboIndex--) {
var combo = kToCombo(comboIndex);
if (this.controller.isEditable && RECOMMENDED[comboIndex] || this.byCombo[combo]) {
groupElements.push(this.renderGroup(comboIndex, combo));
}
}
return groupElements;
}
}, {
key: "renderGroup",
value: function renderGroup(comboIndex, combo) {
var _this2 = this;
var groupRow = this.controller.dom.createElement('div');
groupRow.setAttribute('style', style.accessGroupListItem);
widgets.makeDropTarget(groupRow, function (uris) {
return _this2.handleDroppedUris(uris, combo).then(function () {
return _this2.controller.render();
})["catch"](function (error) {
return _this2.controller.renderStatus(error);
});
});
var groupColumns = this.renderGroupElements(comboIndex, combo);
groupColumns.forEach(function (column) {
return groupRow.appendChild(column);
});
return groupRow;
}
}, {
key: "renderGroupElements",
value: function renderGroupElements(comboIndex, combo) {
var _this3 = this;
var groupNameColumn = this.controller.dom.createElement('div');
groupNameColumn.setAttribute('style', style.group);
if (this.controller.isEditable) {
switch (comboIndex) {
case 1:
groupNameColumn.setAttribute('style', style.group1);
break;
case 2:
groupNameColumn.setAttribute('style', style.group2);
break;
case 3:
groupNameColumn.setAttribute('style', style.group3);
break;
case 5:
groupNameColumn.setAttribute('style', style.group5);
break;
case 9:
groupNameColumn.setAttribute('style', style.group9);
break;
case 13:
groupNameColumn.setAttribute('style', style.group13);
break;
default:
groupNameColumn.setAttribute('style', style.group);
}
}
groupNameColumn.innerText = COLLOQUIAL[comboIndex] || ktToList(comboIndex);
var groupAgentsColumn = this.controller.dom.createElement('div');
groupAgentsColumn.setAttribute('style', style.group);
if (this.controller.isEditable) {
switch (comboIndex) {
case 1:
groupAgentsColumn.setAttribute('style', style.group1);
break;
case 2:
groupAgentsColumn.setAttribute('style', style.group2);
break;
case 3:
groupAgentsColumn.setAttribute('style', style.group3);
break;
case 5:
groupAgentsColumn.setAttribute('style', style.group5);
break;
case 9:
groupAgentsColumn.setAttribute('style', style.group9);
break;
case 13:
groupAgentsColumn.setAttribute('style', style.group13);
break;
default:
groupAgentsColumn.setAttribute('style', style.group);
}
}
var groupAgentsTable = groupAgentsColumn.appendChild(this.controller.dom.createElement('table'));
var combos = this.byCombo[combo] || [];
combos.map(function (_ref) {
var _ref2 = (0, _slicedToArray2["default"])(_ref, 2),
pred = _ref2[0],
obj = _ref2[1];
return _this3.renderAgent(groupAgentsTable, combo, pred, obj);
}).forEach(function (agentElement) {
return groupAgentsTable.appendChild(agentElement);
});
var groupDescriptionElement = this.controller.dom.createElement('div');
groupDescriptionElement.setAttribute('style', style.group);
if (this.controller.isEditable) {
switch (comboIndex) {
case 1:
groupDescriptionElement.setAttribute('style', style.group1);
break;
case 2:
groupDescriptionElement.setAttribute('style', style.group2);
break;
case 3:
groupDescriptionElement.setAttribute('style', style.group3);
break;
case 5:
groupDescriptionElement.setAttribute('style', style.group5);
break;
case 9:
groupDescriptionElement.setAttribute('style', style.group9);
break;
case 13:
groupDescriptionElement.setAttribute('style', style.group13);
break;
default:
groupDescriptionElement.setAttribute('style', style.group);
}
}
groupDescriptionElement.innerText = EXPLANATION[comboIndex] || 'Unusual combination';
return [groupNameColumn, groupAgentsColumn, groupDescriptionElement];
}
}, {
key: "renderAgent",
value: function renderAgent(groupAgentsTable, combo, pred, obj) {
var _this4 = this;
var personRow = widgets.personTR(this.controller.dom, ACL(pred), (0, _rdflib.sym)(obj), this.controller.isEditable ? {
deleteFunction: function deleteFunction() {
return _this4.deleteAgent(combo, pred, obj).then(function () {
return groupAgentsTable.removeChild(personRow);
})["catch"](function (error) {
return _this4.controller.renderStatus(error);
});
}
} : {});
return personRow;
}
}, {
key: "deleteAgent",
value: function () {
var _deleteAgent = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee(combo, pred, obj) {
var combos, comboToRemove;
return _regenerator["default"].wrap(function (_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
combos = this.byCombo[combo] || [];
comboToRemove = combos.find(function (_ref3) {
var _ref4 = (0, _slicedToArray2["default"])(_ref3, 2),
comboPred = _ref4[0],
comboObj = _ref4[1];
return comboPred === pred && comboObj === obj;
});
if (comboToRemove) {
combos.splice(combos.indexOf(comboToRemove), 1);
}
_context.next = 1;
return this.controller.save();
case 1:
case "end":
return _context.stop();
}
}, _callee, this);
}));
function deleteAgent(_x, _x2, _x3) {
return _deleteAgent.apply(this, arguments);
}
return deleteAgent;
}()
}, {
key: "addNewURI",
value: function () {
var _addNewURI = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee2(uri) {
return _regenerator["default"].wrap(function (_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
_context2.next = 1;
return this.handleDroppedUri(uri, kToCombo(1));
case 1:
_context2.next = 2;
return this.controller.save();
case 2:
case "end":
return _context2.stop();
}
}, _callee2, this);
}));
function addNewURI(_x4) {
return _addNewURI.apply(this, arguments);
}
return addNewURI;
}()
}, {
key: "handleDroppedUris",
value: function () {
var _handleDroppedUris = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee3(uris, combo) {
var _this5 = this;
var _t;
return _regenerator["default"].wrap(function (_context3) {
while (1) switch (_context3.prev = _context3.next) {
case 0:
_context3.prev = 0;
_context3.next = 1;
return Promise.all(uris.map(function (uri) {
return _this5.handleDroppedUri(uri, combo);
}));
case 1:
_context3.next = 2;
return this.controller.save();
case 2:
_context3.next = 4;
break;
case 3:
_context3.prev = 3;
_t = _context3["catch"](0);
return _context3.abrupt("return", Promise.reject(_t));
case 4:
case "end":
return _context3.stop();
}
}, _callee3, this, [[0, 3]]);
}));
function handleDroppedUris(_x5, _x6) {
return _handleDroppedUris.apply(this, arguments);
}
return handleDroppedUris;
}()
}, {
key: "handleDroppedUri",
value: function () {
var _handleDroppedUri = (0, _asyncToGenerator2["default"])(function (uri, combo) {
var _this6 = this;
var secondAttempt = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
return /*#__PURE__*/_regenerator["default"].mark(function _callee4() {
var agent, thing, _this6$_store, message, error, _t2;
return _regenerator["default"].wrap(function (_context4) {
while (1) switch (_context4.prev = _context4.next) {
case 0:
agent = findAgent(uri, _this6.store); // eg 'agent', 'origin', agentClass'
thing = (0, _rdflib.sym)(uri);
if (!(!agent && !secondAttempt)) {
_context4.next = 5;
break;
}
debug.log(" Not obvious: looking up dropped thing ".concat(thing));
_context4.prev = 1;
_context4.next = 2;
return (_this6$_store = _this6._store) === null || _this6$_store === void 0 || (_this6$_store = _this6$_store.fetcher) === null || _this6$_store === void 0 ? void 0 : _this6$_store.load(thing.doc());
case 2:
_context4.next = 4;
break;
case 3:
_context4.prev = 3;
_t2 = _context4["catch"](1);
message = "Ignore error looking up dropped thing: ".concat(_t2);
debug.error(message);
return _context4.abrupt("return", Promise.reject(new Error(message)));
case 4:
return _context4.abrupt("return", _this6.handleDroppedUri(uri, combo, true));
case 5:
if (agent) {
_context4.next = 6;
break;
}
error = " Error: Drop fails to drop appropriate thing! ".concat(uri);
debug.error(error);
return _context4.abrupt("return", Promise.reject(new Error(error)));
case 6:
_this6.setACLCombo(combo, uri, agent, _this6.controller.subject);
case 7:
case "end":
return _context4.stop();
}
}, _callee4, null, [[1, 3]]);
})();
});
function handleDroppedUri(_x7, _x8) {
return _handleDroppedUri.apply(this, arguments);
}
return handleDroppedUri;
}()
}, {
key: "setACLCombo",
value: function setACLCombo(combo, uri, res, subject) {
if (!(combo in this.byCombo)) {
this.byCombo[combo] = [];
}
this.removeAgentFromCombos(uri); // Combos are mutually distinct
this.byCombo[combo].push([res.pred, res.obj.uri]);
debug.log("ACL: setting access to ".concat(subject, " by ").concat(res.pred, ": ").concat(res.obj));
}
}, {
key: "removeAgentFromCombos",
value: function removeAgentFromCombos(uri) {
for (var k = 0; k < 16; k++) {
var combos = this.byCombo[kToCombo(k)];
if (combos) {
for (var i = 0; i < combos.length; i++) {
while (i < combos.length && combos[i][1] === uri) {
combos.splice(i, 1);
}
}
}
}
}
}]);
}();
function kToCombo(k) {
var y = ['Read', 'Append', 'Write', 'Control'];
var combo = [];
for (var i = 0; i < 4; i++) {
if (k & 1 << i) {
combo.push('http://www.w3.org/ns/auth/acl#' + y[i]);
}
}
combo.sort();
return combo.join('\n');
}
function ktToList(k) {
var list = '';
var y = ['Read', 'Append', 'Write', 'Control'];
for (var i = 0; i < 4; i++) {
if (k & 1 << i) {
list += y[i];
}
}
return list;
}
function findAgent(uri, kb) {
var obj = (0, _rdflib.sym)(uri);
var types = kb.findTypeURIs(obj);
for (var ty in types) {
debug.log(' drop object type includes: ' + ty);
}
// An Origin URI is one like https://fred.github.io eith no trailing slash
if (uri.startsWith('http') && uri.split('/').length === 3) {
// there is no third slash
return {
pred: 'origin',
obj: obj
}; // The only way to know an origin alas
}
// @@ This is an almighty kludge needed because drag and drop adds extra slashes to origins
if (uri.startsWith('http') && uri.split('/').length === 4 && uri.endsWith('/')) {
// there IS third slash
debug.log('Assuming final slash on dragged origin URI was unintended!');
return {
pred: 'origin',
obj: (0, _rdflib.sym)(uri.slice(0, -1))
}; // Fix a URI where the drag and drop system has added a spurious slash
}
if (ns.vcard('WebID').uri in types) return {
pred: 'agent',
obj: obj
};
if (ns.vcard('Group').uri in types) {
return {
pred: 'agentGroup',
obj: obj
}; // @@ note vcard membership not RDFs
}
if (obj.sameTerm(ns.foaf('Agent')) || obj.sameTerm(ns.acl('AuthenticatedAgent')) ||
// AuthenticatedAgent
obj.sameTerm(ns.rdf('Resource')) || obj.sameTerm(ns.owl('Thing'))) {
return {
pred: 'agentClass',
obj: obj
};
}
if (ns.vcard('Individual').uri in types || ns.foaf('Person').uri in types || ns.foaf('Agent').uri in types) {
var pref = kb.any(obj, ns.foaf('preferredURI'));
if (pref) return {
pred: 'agent',
obj: (0, _rdflib.sym)(pref)
};
return {
pred: 'agent',
obj: obj
};
}
if (ns.solid('AppProvider').uri in types) {
return {
pred: 'origin',
obj: obj
};
}
if (ns.solid('AppProviderClass').uri in types) {
return {
pred: 'originClass',
obj: obj
};
}
debug.log(' Triage fails for ' + uri);
return null;
}
//# sourceMappingURL=access-groups.js.map