UNPKG

solid-ui

Version:

UI library for writing Solid read-write-web applications

1,166 lines (971 loc) • 41.4 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); /* global confirm */ // ///////////////////////////// ACL User Interface // See https://www.coshx.com/blog/2014/04/11/preventing-drag-and-drop-disasters-with-a-chrome-userscript/ // Without this dropping anything onto a browser page will cause chrome etc to jump to diff page // throwing away all the user's work. /* global alert window */ var UI = {}; UI.authn = require('./signin'); UI.acl = require('./acl'); UI.icons = require('./iconBase'); UI.ns = require('./ns'); UI.widgets = require('./widgets'); UI.style = require('./style'); UI.utils = require('./utils'); UI.aclControl = module.exports = {}; // In apps which may use drag and drop, this utility takes care of the fact // by default in a browser, an uncuaght user drop into a browser window // causes the bowser to lose all its work in tat window and navigate to another page UI.aclControl.preventBrowserDropEvents = function (document) { console.log('preventBrowserDropEvents called.'); if (typeof window !== 'undefined') { if (window.preventBrowserDropEventsDone) return; window.preventBrowserDropEventsDone = true; } function preventDrag(e) { e.stopPropagation(); e.preventDefault(); } function handleDrop(e) { if (e.dataTransfer.files.length > 0) { if (!confirm('Are you sure you want to drop this file here? ' + '(Cancel opens it in a new tab)')) { e.stopPropagation(); e.preventDefault(); console.log('@@@@ document-level DROP suppressed: ' + e.dataTransfer.dropEffect); } } } document.addEventListener('drop', handleDrop, false); document.addEventListener('dragenter', preventDrag, false); document.addEventListener('dragover', preventDrag, false); }; UI.aclControl.shortNameForFolder = function (x) { var str = x.uri; if (str.slice(-1) === '/') { str = str.slice(0, -1); } var slash = str.lastIndexOf('/'); if (slash >= 0) { str = str.slice(slash + 1); } return str || '/'; }; UI.aclControl.ACLControlBox5 = function (subject, dom, noun, kb, callback) { var updater = kb.updater || new $rdf.UpdateManager(kb); var ACL = UI.ns.acl; var doc = subject.doc(); // The ACL is actually to the doc describing the thing var table = dom.createElement('table'); table.setAttribute('style', 'margin: 1em; border: 0.1em #ccc ;'); var headerRow = table.appendChild(dom.createElement('tr')); headerRow.textContent = 'Sharing for ' + noun + ' ' + UI.utils.label(subject); headerRow.setAttribute('style', 'min-width: 20em; padding: 1em; font-size: 120%; border-bottom: 0.1em solid red; margin-bottom: 2em;'); var statusRow = table.appendChild(dom.createElement('tr')); var statusCell = statusRow.appendChild(dom.createElement('td')); var statusBlock = statusCell.appendChild(dom.createElement('div')); statusBlock.setAttribute('style', 'padding: 2em;'); var MainRow = table.appendChild(dom.createElement('tr')); var box = MainRow.appendChild(dom.createElement('table')); var bottomRow = table.appendChild(dom.createElement('tr')); // A world button can be dragged to gve public access. // later, allow it to be pressed to make pubicly viewable? var bottomLeftCell = bottomRow.appendChild(dom.createElement('td')); // var bottomMiddleCell = bottomRow.appendChild(dom.createElement('td')) var bottomRightCell = bottomRow.appendChild(dom.createElement('td')); // var publicAccessButton = bottomLeftCell.appendChild(UI.widgets.button(dom, UI.icons.iconBase + 'noun_98053.svg', 'Public')) var bigButtonStyle = 'border-radius: 0.3em; background-color: white; border: 0.1em solid #888;'; // This is the main function which produces an editable access control. // There are two of these in all iff the defaults are separate // function ACLControlEditable(box, doc, aclDoc, kb, options) { options = options || {}; var byCombo; var kToCombo = 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(); combo = combo.join('\n'); return combo; }; 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' }; var kToColor = { 13: 'purple', 9: 'blue', 5: 'red', 3: 'orange', 2: '#cc0', 1: 'green' }; 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; } var removeAgentFromCombos = function removeAgentFromCombos(uri) { for (var k = 0; k < 16; k++) { var a = byCombo[kToCombo(k)]; if (a) { for (var i = 0; i < a.length; i++) { while (i < a.length && a[i][1] === uri) { a.splice(i, 1); } } } } }; function agentTriage(uri) { var ns = UI.ns; var obj = $rdf.sym(uri); var types = kb.findTypeURIs(obj); for (var ty in types) { console.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 console.log('Assuming final slash on dragged origin URI was unintended!'); return { pred: 'origin', obj: $rdf.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: $rdf.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 }; } console.log(' Triage fails for ' + uri); } box.saveBack = function (callback) { var kb2 = $rdf.graph(); if (!box.isContainer) { UI.acl.makeACLGraphbyCombo(kb2, doc, box.mainByCombo, aclDoc, true); } else if (box.defaultsDiffer) { // Pair of controls UI.acl.makeACLGraphbyCombo(kb2, doc, box.mainByCombo, aclDoc, true); UI.acl.makeACLGraphbyCombo(kb2, doc, box.defByCombo, aclDoc, false, true); } else { // Linked controls UI.acl.makeACLGraphbyCombo(kb2, doc, box.mainByCombo, aclDoc, true, true); } var updater = kb2.updater || new $rdf.UpdateManager(kb2); updater.put(aclDoc, kb2.statementsMatching(undefined, undefined, undefined, aclDoc), 'text/turtle', function (uri, ok, message) { var error = null; if (!ok) { error = 'ACL file save failed: ' + message; console.log(error); } else { kb.fetcher.unload(aclDoc); kb.add(kb2.statements); kb.fetcher.requested[aclDoc.uri] = 'done'; // missing: save headers console.log('ACL modification: success!'); } callback(ok, error); }); }; function renderCombo(byCombo, combo) { var row = box.appendChild(dom.createElement('tr')); row.combo = combo; row.setAttribute('style', 'color: ' + (options.modify ? kToColor[k] || 'black' : '#888') + ';'); var left = row.appendChild(dom.createElement('td')); left.textContent = colloquial[k] || ktToList[k]; left.setAttribute('style', 'padding-bottom: 2em;'); var middle = row.appendChild(dom.createElement('td')); var middleTable = middle.appendChild(dom.createElement('table')); middleTable.style.width = '100%'; var right = row.appendChild(dom.createElement('td')); right.textContent = explanation[k] || 'Unusual combination'; right.setAttribute('style', 'max-width: 30%;'); var addAgent = function addAgent(pred, obj) { if (middleTable.NoneTR) { middleTable.removeChild(middleTable.NoneTR); delete middleTable.NoneTR; } var opt = {}; if (options.modify) { opt.deleteFunction = function deletePerson() { var arr = byCombo[combo]; for (var b = 0; b < arr.length; b++) { if (arr[b][0] === pred && arr[b][1] === obj) { arr.splice(b, 1); // remove from ACL break; } } box.saveBack(function (ok, error) { if (ok) { middleTable.removeChild(tr); } else { alert(error); } }); }; } var tr = middleTable.appendChild(UI.widgets.personTR(dom, ACL(pred), $rdf.sym(obj), opt)); tr.predObj = [pred.uri, obj.uri]; }; var syncCombo = function syncCombo(combo) { var i; var arr = byCombo[combo]; if (arr && arr.length) { var already = middleTable.children; arr.sort(); for (var j = 0; j < already.length; j++) { already[j].trashme = true; } for (var a = 0; a < arr.length; a++) { var found = false; for (i = 0; i < already.length; i++) { if (already[i].predObj && // skip NoneTR already[i].predObj[0] === arr[a][0] && already[i].predObj[1] === arr[a][1]) { found = true; delete already[i].trashme; break; } } if (!found) { addAgent(arr[a][0], arr[a][1]); } } for (i = already.length - 1; i >= 0; i--) { if (already[i].trashme) { middleTable.removeChild(already[i]); } } } else { UI.widgets.clearElement(middleTable); var tr = middleTable.appendChild(dom.createElement('tr')); tr.textContent = 'None'; tr.setAttribute('style', 'padding: 1em;'); middleTable.NoneTR = tr; } }; syncCombo(combo); row.refresh = function () { syncCombo(combo); }; function saveAndRestoreUI() { box.saveBack(function (ok, error) { if (ok) { row.style.backgroundColor = 'white'; // restore look to before drag syncPanel(); } else { alert(error); } }); } function handleManyDroppedURIs(uris) { Promise.all(uris.map(function (u) { return handleOneDroppedURI(u); // can add to meetingDoc but must be sync })).then(function (a) { saveAndRestoreUI(); }); } function handleOneDroppedURI(_x) { return _handleOneDroppedURI.apply(this, arguments); } // handleOneDroppedURI function _handleOneDroppedURI() { _handleOneDroppedURI = (0, _asyncToGenerator2["default"])( /*#__PURE__*/ _regenerator["default"].mark(function _callee(u) { var setACLCombo, res, thing; return _regenerator["default"].wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: setACLCombo = function _ref() { if (!(combo in byCombo)) { byCombo[combo] = []; } removeAgentFromCombos(u); // Combos are mutually distinct byCombo[combo].push([res.pred, res.obj.uri]); console.log('ACL: setting access to ' + subject + ' by ' + res.pred + ': ' + res.obj); }; res = agentTriage(u); // eg 'agent', 'origin', agentClass' thing = $rdf.sym(u); if (res) { _context.next = 17; break; } console.log(' Not obvious: looking up dropped thing ' + thing); _context.prev = 5; _context.next = 8; return kb.fetcher.load(thing.doc()); case 8: _context.next = 13; break; case 10: _context.prev = 10; _context.t0 = _context["catch"](5); console.log('Ignore error looking up dropped thing: ' + _context.t0); case 13: res = agentTriage(u); if (!res) { console.log(' Error: Drop fails to drop appropriate thing! ' + u); } else { setACLCombo(); } _context.next = 18; break; case 17: setACLCombo(); case 18: case "end": return _context.stop(); } } }, _callee, null, [[5, 10]]); })); return _handleOneDroppedURI.apply(this, arguments); } function addNewUIRI(_x2) { return _addNewUIRI.apply(this, arguments); } function _addNewUIRI() { _addNewUIRI = (0, _asyncToGenerator2["default"])( /*#__PURE__*/ _regenerator["default"].mark(function _callee2(uri) { return _regenerator["default"].wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: _context2.next = 2; return handleOneDroppedURI(uri); case 2: saveAndRestoreUI(); case 3: case "end": return _context2.stop(); } } }, _callee2); })); return _addNewUIRI.apply(this, arguments); } if (options.modify) { row.addNewURI = addNewUIRI; UI.widgets.makeDropTarget(row, handleManyDroppedURIs); } return row; } // renderCombo var syncPanel = function syncPanel() { var kids = box.children; for (var i = 0; i < kids.length; i++) { if (kids[i].refresh) { kids[i].refresh(); } } // @@ later -- need to addd combos not in the box? }; function renderAdditionTool(ele, lastRow) { var ns = UI.ns; function removeOthers(button) { button.keep = true; button.parentNode.keep = true; var removeThese = []; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = bar.children[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var ele = _step.value; if (!ele.keep) removeThese.push(ele); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator["return"] != null) { _iterator["return"](); } } finally { if (_didIteratorError) { throw _iteratorError; } } } removeThese.forEach(function (e) { return bar.removeChild(e); }); } function removeBar() { ele.removeChild(ele.bar); ele.bar = null; } if (ele.bar) { // toggle return removeBar(); } var bar = ele.appendChild(dom.createElement('div')); ele.bar = bar; /** Buttons to add different types of theings to have access */ // Person bar.appendChild(UI.widgets.button(dom, UI.icons.iconBase + UI.widgets.iconForClass['vcard:Individual'], 'Add Person', /*#__PURE__*/ function () { var _ref2 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/ _regenerator["default"].mark(function _callee3(event) { var name, domainNameRegexp; return _regenerator["default"].wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: removeOthers(event.target); _context3.next = 3; return UI.widgets.askName(dom, kb, bar, ns.vcard('URI'), ns.vcard('Individual'), 'person'); case 3: name = _context3.sent; if (name) { _context3.next = 6; break; } return _context3.abrupt("return", removeBar()); case 6: // user cancelled domainNameRegexp = /^https?:/i; if (name.match(domainNameRegexp)) { _context3.next = 9; break; } return _context3.abrupt("return", alert('Not a http URI')); case 9: // @@ check it actually is a person and has an owner who agrees they own it console.log('Adding to ACL person: ' + name); _context3.next = 12; return lastRow.addNewURI(name); case 12: removeBar(); case 13: case "end": return _context3.stop(); } } }, _callee3); })); return function (_x3) { return _ref2.apply(this, arguments); }; }())); // Group bar.appendChild(UI.widgets.button(dom, UI.icons.iconBase + UI.widgets.iconForClass['vcard:Group'], 'Add Group', /*#__PURE__*/ function () { var _ref3 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/ _regenerator["default"].mark(function _callee4(event) { var name, domainNameRegexp; return _regenerator["default"].wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: removeOthers(event.target); _context4.next = 3; return UI.widgets.askName(dom, kb, bar, ns.vcard('URI'), ns.vcard('Group'), 'group'); case 3: name = _context4.sent; if (name) { _context4.next = 6; break; } return _context4.abrupt("return", removeBar()); case 6: // user cancelled domainNameRegexp = /^https?:/i; if (name.match(domainNameRegexp)) { _context4.next = 9; break; } return _context4.abrupt("return", alert('Not a http URI')); case 9: // @@ check it actually is a group and has an owner who agrees they own it console.log('Adding to ACL group: ' + name); _context4.next = 12; return lastRow.addNewURI(name); case 12: removeBar(); case 13: case "end": return _context4.stop(); } } }, _callee4); })); return function (_x4) { return _ref3.apply(this, arguments); }; }())); // General public bar.appendChild(UI.widgets.button(dom, UI.icons.iconBase + UI.widgets.iconForClass['foaf:Agent'], 'Add Everyone', /*#__PURE__*/ function () { var _ref4 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/ _regenerator["default"].mark(function _callee5(event) { return _regenerator["default"].wrap(function _callee5$(_context5) { while (1) { switch (_context5.prev = _context5.next) { case 0: statusBlock.textContent = 'Adding the general public to those who can read. Drag the globe to a different level to give them more access.'; _context5.next = 3; return lastRow.addNewURI(ns.foaf('Agent').uri); case 3: removeBar(); case 4: case "end": return _context5.stop(); } } }, _callee5); })); return function (_x5) { return _ref4.apply(this, arguments); }; }())); // AuthenticatedAgent bar.appendChild(UI.widgets.button(dom, UI.icons.iconBase + 'noun_99101.svg', 'Anyone logged In', /*#__PURE__*/ function () { var _ref5 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/ _regenerator["default"].mark(function _callee6(event) { return _regenerator["default"].wrap(function _callee6$(_context6) { while (1) { switch (_context6.prev = _context6.next) { case 0: statusBlock.textContent = 'Adding the anyone logged in to those who can read. Drag the ID icon to a different level to give them more access.'; _context6.next = 3; return lastRow.addNewURI(ns.acl('AuthenticatedAgent').uri); case 3: removeBar(); case 4: case "end": return _context6.stop(); } } }, _callee6); })); return function (_x6) { return _ref5.apply(this, arguments); }; }())); // Bots bar.appendChild(UI.widgets.button(dom, UI.icons.iconBase + 'noun_Robot_849764.svg', 'A Software Agent (bot)', /*#__PURE__*/ function () { var _ref6 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/ _regenerator["default"].mark(function _callee7(event) { var name, domainNameRegexp; return _regenerator["default"].wrap(function _callee7$(_context7) { while (1) { switch (_context7.prev = _context7.next) { case 0: removeOthers(event.target); _context7.next = 3; return UI.widgets.askName(dom, kb, bar, ns.vcard('URI'), ns.schema('Application'), 'bot'); case 3: name = _context7.sent; if (name) { _context7.next = 6; break; } return _context7.abrupt("return", removeBar()); case 6: // user cancelled domainNameRegexp = /^https?:/i; if (name.match(domainNameRegexp)) { _context7.next = 9; break; } return _context7.abrupt("return", alert('Not a http URI')); case 9: // @@ check it actually is a bot and has an owner who agrees they own it console.log('Adding to ACL bot: ' + name); _context7.next = 12; return lastRow.addNewURI(name); case 12: removeBar(); case 13: case "end": return _context7.stop(); } } }, _callee7); })); return function (_x7) { return _ref6.apply(this, arguments); }; }())); // Web Apps bar.appendChild(UI.widgets.button(dom, UI.icons.iconBase + 'noun_15177.svg', 'A Web App (origin)', /*#__PURE__*/ function () { var _ref7 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/ _regenerator["default"].mark(function _callee9(event) { var context, trustedApps, trustedOrigins, table, trustedAppControl, cancel, name, domainNameRegexp, origin; return _regenerator["default"].wrap(function _callee9$(_context9) { while (1) { switch (_context9.prev = _context9.next) { case 0: removeOthers(event.target); context = { div: bar, dom: dom }; _context9.next = 4; return UI.authn.logInLoadProfile(context); case 4: trustedApps = kb.each(context.me, ns.acl('trustedApp')); trustedOrigins = trustedApps.flatMap(function (app) { return kb.each(app, ns.acl('origin')); }); bar.appendChild(dom.createElement('p')).textContent = "You have ".concat(trustedOrigins.length, " selected web apps."); table = bar.appendChild(dom.createElement('table')); trustedApps.forEach(function (app) { var origin = kb.any(app, ns.acl('origin')); var thingTR = UI.widgets.personTR(dom, ns.acl('origin'), origin, {}); var innerTable = dom.createElement('table'); var innerRow = innerTable.appendChild(dom.createElement('tr')); var innerLeft = innerRow.appendChild(dom.createElement('td')); var innerMiddle = innerRow.appendChild(dom.createElement('td')); var innerRight = innerRow.appendChild(dom.createElement('td')); innerLeft.appendChild(thingTR); innerMiddle.textContent = 'Give access to ' + noun + ' ' + UI.utils.label(subject) + '?'; innerRight.appendChild(UI.widgets.continueButton(dom, /*#__PURE__*/ function () { var _ref8 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/ _regenerator["default"].mark(function _callee8(event) { return _regenerator["default"].wrap(function _callee8$(_context8) { while (1) { switch (_context8.prev = _context8.next) { case 0: _context8.next = 2; return lastRow.addNewURI(origin.uri); case 2: case "end": return _context8.stop(); } } }, _callee8); })); return function (_x9) { return _ref8.apply(this, arguments); }; }())); table.appendChild(innerTable); }); table.style = 'margin: em; background-color: #eee;'; // Add the Trusted App pane for managing you set of apps trustedAppControl = window.panes.trustedApplications.render(context.me, dom, {}); trustedAppControl.style.borderColor = 'orange'; trustedAppControl.style.borderWidth = '0.1em'; trustedAppControl.style.borderRadius = '1em'; bar.appendChild(trustedAppControl); cancel = UI.widgets.cancelButton(dom, function () { return bar.removeChild(trustedAppControl); }); trustedAppControl.insertBefore(cancel, trustedAppControl.firstChild); cancel.style["float"] = 'right'; _context9.next = 20; return UI.widgets.askName(dom, kb, bar, null, ns.schema('WebApplication'), 'webapp domain'); case 20: name = _context9.sent; if (name) { _context9.next = 23; break; } return _context9.abrupt("return", removeBar()); case 23: // user cancelled domainNameRegexp = /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i; // https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch08s15.html if (name.match(domainNameRegexp)) { _context9.next = 26; break; } return _context9.abrupt("return", alert('Not a domain name')); case 26: origin = 'https://' + name; console.log('Adding to ACL origin: ' + origin); _context9.next = 30; return lastRow.addNewURI(origin); case 30: removeBar(); case 31: case "end": return _context9.stop(); } } }, _callee9); })); return function (_x8) { return _ref7.apply(this, arguments); }; }())); } function renderAddToolBar(box, lastRow) { // const toolRow = box.appendChild(dom.createElement('tr')) bottomLeftCell.appendChild(UI.widgets.button(dom, UI.icons.iconBase + 'noun_34653_green.svg', 'Add ...', function (event) { renderAdditionTool(bottomLeftCell, lastRow); })); } var k, combo, lastRow; for (k = 15; k > 0; k--) { combo = kToCombo(k); if (options.modify && recommended[k] || byCombo[combo]) { lastRow = renderCombo(byCombo, combo); } // if } // for if (options.modify) { renderAddToolBar(box, lastRow); } return byCombo; } // ACLControlEditable var renderBox = function renderBox() { box.innerHTML = ''; UI.acl.getACLorDefault(doc, function (ok, p2, targetDoc, targetACLDoc, defaultHolder, defaultACLDoc) { var defa = !p2; // @@ Could also set from classes ldp:Container etc etc if (!ok) { statusBlock.textContent += 'Error reading ' + (defa ? ' default ' : '') + 'ACL.' + ' status ' + targetDoc + ': ' + targetACLDoc; } else { box.isContainer = targetDoc.uri.slice(-1) === '/'; // Give default for all directories if (defa) { var defaults = kb.each(undefined, ACL('default'), defaultHolder, defaultACLDoc).concat(kb.each(undefined, ACL('defaultForNew'), defaultHolder, defaultACLDoc)); if (!defaults.length) { statusBlock.textContent += ' (No defaults given.)'; } else { statusBlock.innerHTML = ''; statusBlock.textContent = 'The sharing for this ' + noun + ' is the default for folder '; var a = statusBlock.appendChild(dom.createElement('a')); a.setAttribute('href', defaultHolder.uri); a.textContent = UI.aclControl.shortNameForFolder(defaultHolder); var kb2 = UI.acl.adoptACLDefault(doc, targetACLDoc, defaultHolder, defaultACLDoc); ACLControlEditable(box, doc, targetACLDoc, kb2, { modify: false }); // Add btton to save them as actual box.style.cssText = 'color: #777;'; var editPlease = bottomRightCell.appendChild(dom.createElement('button')); editPlease.textContent = 'Set specific sharing\nfor this ' + noun; editPlease.style.cssText = bigButtonStyle; editPlease.addEventListener('click', /*#__PURE__*/ function () { var _ref9 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/ _regenerator["default"].mark(function _callee10(event) { var msg; return _regenerator["default"].wrap(function _callee10$(_context10) { while (1) { switch (_context10.prev = _context10.next) { case 0: kb2.statements.forEach(function (st) { kb.add(st.subject, st.predicate, st.object, targetACLDoc); }); _context10.prev = 1; kb.fetcher.putBack(targetACLDoc).then(function () { statusBlock.textContent = ' (Now editing specific access for this ' + noun + ')'; bottomRightCell.removeChild(editPlease); renderBox(); }); _context10.next = 11; break; case 5: _context10.prev = 5; _context10.t0 = _context10["catch"](1); msg = ' Error writing back access control file! ' + _context10.t0; console.error(msg); statusBlock.textContent += msg; return _context10.abrupt("return"); case 11: case "end": return _context10.stop(); } } }, _callee10, null, [[1, 5]]); })); return function (_x10) { return _ref9.apply(this, arguments); }; }()); } // defaults.length } else { // Not using defaults var useDefault; var addDefaultButton = function addDefaultButton(prospectiveDefaultHolder) { useDefault = bottomRightCell.appendChild(dom.createElement('button')); useDefault.textContent = 'Stop specific sharing for this ' + noun + ' -- just use default'; // + UI.utils.label(thisDefaultHolder) if (prospectiveDefaultHolder) { useDefault.textContent += ' for ' + UI.utils.label(prospectiveDefaultHolder); } useDefault.style.cssText = bigButtonStyle; useDefault.addEventListener('click', function (event) { kb.fetcher["delete"](targetACLDoc.uri).then(function () { statusBlock.textContent = ' The sharing for this ' + noun + ' is now the default.'; bottomRightCell.removeChild(useDefault); box.style.cssText = 'color: #777;'; bottomLeftCell.innerHTML = ''; renderBox(); })["catch"](function (e) { statusBlock.textContent += ' (Error deleting access control file: ' + targetACLDoc + ': ' + e + ')'; }); }); }; var prospectiveDefaultHolder; var str = targetDoc.uri.split('#')[0]; var p = str.slice(0, -1).lastIndexOf('/'); var q = str.indexOf('//'); var targetDocDir = q >= 0 && p < q + 2 || p < 0 ? null : str.slice(0, p + 1); // @@ TODO: The methods used for targetIsStorage are HACKs - it should not be relied upon, and work is // @@ underway to standardize a behavior that does not rely upon this hack // @@ hopefully fixed as part of https://github.com/solid/data-interoperability-panel/issues/10 var targetIsStorage = kb.holds(targetDoc, UI.ns.rdf('type'), UI.ns.space('Storage'), targetACLDoc); var targetAclIsProtected = hasProtectedAcl(targetDoc); var targetIsProtected = targetIsStorage || targetAclIsProtected; if (!targetIsProtected && targetDocDir) { UI.acl.getACLorDefault($rdf.sym(targetDocDir), function (ok2, p22, targetDoc2, targetACLDoc2, defaultHolder2, defaultACLDoc2) { if (ok2) { prospectiveDefaultHolder = p22 ? targetDoc2 : defaultHolder2; } addDefaultButton(prospectiveDefaultHolder); }); } else if (!targetIsProtected) { addDefaultButton(); } box.addControlForDefaults = function () { box.notice.textContent = 'Access to things within this folder:'; box.notice.style.cssText = 'font-size: 120%; color: black;'; var mergeButton = UI.widgets.clearElement(box.offer).appendChild(dom.createElement('button')); mergeButton.innerHTML = '<p>Set default for folder contents to<br />just track the sharing for the folder</p>'; mergeButton.style.cssText = bigButtonStyle; mergeButton.addEventListener('click', function (e) { delete box.defaultsDiffer; delete box.defByCombo; box.saveBack(function (ok, error) { if (ok) { box.removeControlForDefaults(); } else { alert(error); } }); }, false); box.defaultsDiffer = true; box.defByCombo = ACLControlEditable(box, targetDoc, targetACLDoc, kb, { modify: true, doingDefaults: true }); }; box.removeControlForDefaults = function () { statusBlock.textContent = 'This is also the default for things in this folder.'; box.notice.textContent = 'Sharing for things within the folder currently tracks sharing for the folder.'; box.notice.style.cssText = 'font-size: 80%; color: #888;'; var splitButton = UI.widgets.clearElement(box.offer).appendChild(dom.createElement('button')); splitButton.innerHTML = '<p>Set the sharing of folder contents <br />separately from the sharing for the folder</p>'; splitButton.style.cssText = bigButtonStyle; splitButton.addEventListener('click', function (e) { box.addControlForDefaults(); statusBlock.textContent = ''; }); while (box.divider.nextSibling) { box.removeChild(box.divider.nextSibling); } statusBlock.textContent = 'This is now also the default for things in this folder.'; }; box.mainByCombo = ACLControlEditable(box, targetDoc, targetACLDoc, kb, { modify: true }); // yes can edit box.divider = box.appendChild(dom.createElement('tr')); box.notice = box.divider.appendChild(dom.createElement('td')); box.notice.style.cssText = 'font-size: 80%; color: #888;'; box.offer = box.divider.appendChild(dom.createElement('td')); box.notice.setAttribute('colspan', '2'); if (box.isContainer) { var ac = UI.acl.readACL(targetDoc, targetACLDoc, kb); var acd = UI.acl.readACL(targetDoc, targetACLDoc, kb, true); box.defaultsDiffer = !UI.acl.sameACL(ac, acd); console.log('Defaults differ ACL: ' + box.defaultsDiffer); if (box.defaultsDiffer) { box.addControlForDefaults(); } else { box.removeControlForDefaults(); } } } // Not using defaults } }); }; renderBox(); return table; }; // ACLControlBox function hasProtectedAcl(targetDoc) { // @@ TODO: This is hacky way of knowing whether or not a certain ACL file can be removed // Hopefully we'll find a better, standardized solution to this - https://github.com/solid/specification/issues/37 return targetDoc.uri === targetDoc.site().uri; } // ends //# sourceMappingURL=acl-control.js.map