Skip to content

Commit 2e63674

Browse files
Tim Berners-Leeunknown
authored andcommitted
ACL control plus basically working
1 parent 88f92b8 commit 2e63674

File tree

3 files changed

+129
-25
lines changed

3 files changed

+129
-25
lines changed

src/acl-control.js

Lines changed: 102 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,14 @@ UI.aclControl.ACLControlBox5 = function (subject, dom, noun, kb, callback) {
8181

8282
// A world button can be dragged to gve public access.
8383
// later, allow it to be pressed to make pubicly viewable?
84-
var publicAccessCell = bottomRow.appendChild(dom.createElement('td'))
85-
var publicAccessButton = publicAccessCell.appendChild(UI.widgets.button(dom, UI.icons.iconBase + 'noun_98053.svg', 'Public'))
86-
UI.widgets.makeDraggable(publicAccessButton, UI.ns.foaf('Agent')) // Represent everyone
84+
var bottomLeftCell = bottomRow.appendChild(dom.createElement('td'))
85+
// var bottomMiddleCell = bottomRow.appendChild(dom.createElement('td'))
86+
var bottomRightCell = bottomRow.appendChild(dom.createElement('td'))
87+
88+
// var publicAccessButton = bottomLeftCell.appendChild(UI.widgets.button(dom, UI.icons.iconBase + 'noun_98053.svg', 'Public'))
89+
90+
const bigButtonStyle = 'border-radius: 0.3em; background-color: white; border: 0.1em solid #888;'
8791

88-
var bigButtonStyle = 'border-radius: 0.3em; background-color: white; border: 0.1em solid #888;'
8992

9093
// This is the main function which produces an editable access control.
9194
// There are two of these in all iff the defaults are separate
@@ -162,7 +165,7 @@ UI.aclControl.ACLControlBox5 = function (subject, dom, noun, kb, callback) {
162165
if (ns.vcard('Group').uri in types) {
163166
return {pred: 'agentGroup', obj: obj} // @@ note vcard membership not RDFs
164167
}
165-
if (obj.sameTerm(ns.foaf('Agent')) ||
168+
if (obj.sameTerm(ns.foaf('Agent')) || obj.sameTerm(ns.acl('AuthenticatedAgent')) || // AuthenticatedAgent
166169
obj.sameTerm(ns.rdf('Resource')) || obj.sameTerm(ns.owl('Thing'))) {
167170
return {pred: 'agentClass', obj: obj}
168171
}
@@ -328,9 +331,14 @@ UI.aclControl.ACLControlBox5 = function (subject, dom, noun, kb, callback) {
328331
}
329332

330333
var res = agentTriage(u) // eg 'agent', 'origin', agentClass'
334+
const thing = $rdf.sym(u)
331335
if (!res) {
332-
console.log(' looking up dropped thing ' + u)
333-
await kb.fetcher.load(u)
336+
console.log(' Not obvious: looking up dropped thing ' + thing)
337+
try {
338+
await kb.fetcher.load(thing.doc())
339+
} catch (err) {
340+
console.log('Ignore error looking up dropped thing: ' + err)
341+
}
334342
res = agentTriage(u)
335343
if (!res) {
336344
console.log(' Error: Drop fails to drop appropriate thing! ' + u)
@@ -342,10 +350,18 @@ UI.aclControl.ACLControlBox5 = function (subject, dom, noun, kb, callback) {
342350
}
343351
}// handleOneDroppedURI
344352

353+
async function addNewUIRI (uri) {
354+
await handleOneDroppedURI(uri)
355+
saveAndRestoreUI()
356+
}
357+
345358
if (options.modify) {
359+
row.addNewURI = addNewUIRI
346360
UI.widgets.makeDropTarget(row, handleManyDroppedURIs)
347361
}
348-
}
362+
return row
363+
} // renderCombo
364+
349365
var syncPanel = function () {
350366
var kids = box.children
351367
for (var i = 0; i < kids.length; i++) {
@@ -355,13 +371,84 @@ UI.aclControl.ACLControlBox5 = function (subject, dom, noun, kb, callback) {
355371
} // @@ later -- need to addd combos not in the box?
356372
}
357373

358-
var k, combo
374+
375+
function renderAdditionTool (ele, lastRow) {
376+
const ns = UI.ns
377+
function removeBar () {
378+
ele.removeChild(ele.bar)
379+
ele.bar = null
380+
}
381+
if (ele.bar) { // toggle
382+
return removeBar()
383+
}
384+
const bar = ele.appendChild(dom.createElement('div'))
385+
ele.bar = bar
386+
387+
var personButton = UI.widgets.button(dom, UI.icons.iconBase + UI.widgets.iconForClass['vcard:Individual'], 'Add Person')
388+
bar.appendChild(personButton)
389+
390+
var groupButton = UI.widgets.button(dom, UI.icons.iconBase + UI.widgets.iconForClass['vcard:Group'], 'Add Group')
391+
bar.appendChild(groupButton)
392+
393+
bar.appendChild(UI.widgets.button(dom, UI.icons.iconBase + UI.widgets.iconForClass['foaf:Agent'], 'Add Everyone', async event => {
394+
statusBlock.textContent = 'Adding the general public to those who can read. Drag the globe to a different level to give them more access.'
395+
await lastRow.addNewURI(ns.foaf('Agent').uri)
396+
}))
397+
398+
// AuthenticatedAgent
399+
bar.appendChild(UI.widgets.button(dom, UI.icons.iconBase + 'noun_99101.svg', 'Anyone logged In', async event => {
400+
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.'
401+
await lastRow.addNewURI(ns.acl('AuthenticatedAgent').uri)
402+
}))
403+
404+
// Bots
405+
bar.appendChild(UI.widgets.button(dom, UI.icons.iconBase + 'noun_Robot_849764.svg', 'A Software Agent (bot)', async event => {
406+
let name = await UI.widgets.askName(dom, kb, bar, null , ns.schema('WebApplication'), 'webapp')
407+
if (!name) return removeBar() // user cancelled
408+
const domainNameRegexp = /^https?:/i
409+
if (!name.match(domainNameRegexp)) { // @@ enforce in user input live like a form element
410+
return alert('Not a http URI')
411+
}
412+
// @@ check it actually is a bot and has an owner who agrees they own it
413+
console.log('Adding to ACL bot: ' + name)
414+
await lastRow.addNewURI(name)
415+
}))
416+
417+
// Web Apps
418+
bar.appendChild(UI.widgets.button(dom, UI.icons.iconBase + 'noun_15177.svg', 'A Web App (origin)', async event => {
419+
console.log('@@ AppButton')
420+
let name = await UI.widgets.askName(dom, kb, bar, null , ns.schema('WebApplication'), 'webapp')
421+
if (!name) return removeBar() // user cancelled
422+
const domainNameRegexp = /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i
423+
// https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch08s15.html
424+
if (!name.match(domainNameRegexp)) { // @@ enforce in user input live like a form element
425+
return alert('Not a domain name')
426+
}
427+
const origin = 'https://' + name
428+
console.log('Adding to ACL origin: ' + origin)
429+
await lastRow.addNewURI(origin)
430+
}))
431+
}
432+
433+
function renderAddToolBar (box, lastRow) {
434+
const toolRow = box.appendChild(dom.createElement('tr'))
435+
var addButton = bottomLeftCell.appendChild(UI.widgets.button(dom, UI.icons.iconBase + 'noun_34653_green.svg', 'Add ...', event => {
436+
renderAdditionTool(bottomLeftCell, lastRow)
437+
}))
438+
}
439+
440+
var k, combo, lastRow
359441
for (k = 15; k > 0; k--) {
360442
combo = kToCombo(k)
361443
if ((options.modify && recommended[k]) || byCombo[combo]) {
362-
renderCombo(byCombo, combo)
444+
lastRow = renderCombo(byCombo, combo)
363445
} // if
364446
} // for
447+
448+
if (options.modify) {
449+
renderAddToolBar(box, lastRow)
450+
}
451+
365452
return byCombo
366453
} // ACLControlEditable
367454

@@ -389,7 +476,7 @@ UI.aclControl.ACLControlBox5 = function (subject, dom, noun, kb, callback) {
389476
ACLControlEditable(box, doc, targetACLDoc, kb2, {modify: false}) // Add btton to save them as actual
390477
box.style.cssText = 'color: #777;'
391478

392-
var editPlease = bottomRow.appendChild(dom.createElement('button'))
479+
var editPlease = bottomRightCell.appendChild(dom.createElement('button'))
393480
editPlease.textContent = 'Set specific sharing\nfor this ' + noun
394481
editPlease.style.cssText = bigButtonStyle
395482
editPlease.addEventListener('click', function (event) {
@@ -399,11 +486,11 @@ UI.aclControl.ACLControlBox5 = function (subject, dom, noun, kb, callback) {
399486
statusBlock.textContent += ' (Error writing back access control file: ' + message + ')'
400487
} else {
401488
kb.add(kb2.statements)
402-
fetcher.requested[targetACLDoc.uri] === 'done' // cheat
489+
kb.fetcher.requested[targetACLDoc.uri] = 'done' // cheat
403490
// kb.fetcher.load(targetACLDoc, {force: true})
404491
statusBlock.textContent = ' (Now editing specific access for this ' + noun + ')'
405492
// box.style.cssText = 'color: black;'
406-
bottomRow.removeChild(editPlease)
493+
bottomRightCell.removeChild(editPlease)
407494
renderBox()
408495
}
409496
})
@@ -412,7 +499,7 @@ UI.aclControl.ACLControlBox5 = function (subject, dom, noun, kb, callback) {
412499
} else { // Not using defaults
413500
var useDefault
414501
var addDefaultButton = function (prospectiveDefaultHolder) {
415-
useDefault = bottomRow.appendChild(dom.createElement('button'))
502+
useDefault = bottomRightCell.appendChild(dom.createElement('button'))
416503
useDefault.textContent = 'Stop specific sharing for this ' + noun +
417504
' -- just use default' // + UI.utils.label(thisDefaultHolder)
418505
if (prospectiveDefaultHolder) {
@@ -475,7 +562,7 @@ UI.aclControl.ACLControlBox5 = function (subject, dom, noun, kb, callback) {
475562
box.notice.textContent = 'Sharing for things within the folder currently tracks sharing for the folder.'
476563
box.notice.style.cssText = 'font-size: 80%; color: #888;'
477564
var splitButton = UI.widgets.clearElement(box.offer).appendChild(dom.createElement('button'))
478-
splitButton.innerHTML = '<p> the sharing of folder contets <br />separately from the sharing for the folder</p>'
565+
splitButton.innerHTML = '<p>Set the sharing of folder contets <br />separately from the sharing for the folder</p>'
479566
splitButton.style.cssText = bigButtonStyle
480567
splitButton.addEventListener('click', function (e) {
481568
box.addControlForDefaults()

src/icons/padlock-timbl.svg

Lines changed: 5 additions & 5 deletions
Loading

src/widgets/index.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,29 @@ UI.widgets.iconForClass = { // Potentially extendable by other apps, panes, etc
191191
'vcard:Individual': 'noun_15059.svg',
192192
'schema:Person': 'noun_15059.svg',
193193
'foaf:Person': 'noun_15059.svg',
194+
'foaf:Agent': 'noun_98053.svg',
195+
'acl:AuthenticatedAgent': 'noun_99101.svg',
196+
'prov:SoftwareAgent': 'noun_Robot_849764.svg', // Bot
194197
'vcard:AddressBook': 'noun_15695.svg',
195198
'trip:Trip': 'noun_581629.svg',
196199
'meeting:Meeting': 'noun_66617.svg',
197200
'meeting:LongChat': 'noun_1689339.svg',
198201
'ui:Form': 'noun_122196.svg'
199202
}
200203

204+
var tempSite = function (x) { // use only while one in rdflib fails with origins 2019
205+
var str = x.uri.split('#')[0]
206+
var p = str.indexOf('//')
207+
if (p < 0) throw new Error('This URI does not have a web site part (origin)')
208+
var q = str.indexOf('/', p+2)
209+
if (q < 0) { // no third slash?
210+
return str.slice(0) + '/' // Add slash to a bare origin
211+
} else {
212+
return str.slice(0, q + 1)
213+
}
214+
}
215+
216+
201217
UI.widgets.findImageByClass = function findImageByClass (x) {
202218
const kb = UI.store
203219
const ns = UI.ns
@@ -219,22 +235,23 @@ UI.widgets.findImageByClass = function findImageByClass (x) {
219235
}
220236
// For HTTP(s) documents, we could look at the MIME type if we know it.
221237
if (x.uri.startsWith('https:') && (x.uri.indexOf('#') < 0)) {
222-
return x.site().uri + 'favicon.ico'
238+
return tempSite(x) + 'favicon.ico' // was x.site().uri + ...
223239
// Todo: make the docuent icon a fallback for if the favicon does not exist
224240
// todo: pick up a possible favicon for the web page istelf from a link
225241
// was: return iconDir + 'noun_681601.svg' // document - under solid assumptions
226242
}
227243
}
228244

245+
ns['prov'] = $rdf.Namespace('http://www.w3.org/ns/prov#') // In case not yet there
229246
for (var k in UI.widgets.iconForClass) {
230247
let pref = k.split(':')[0]
231248
let id = k.split(':')[1]
232249
let klass = ns[pref](id)
233-
if (klass.uri in types) { // Allow full URI in new additions
250+
if (klass.uri in types || klass.uri === x.uri) { // Allow full URI in new additions
234251
return $rdf.uri.join(UI.widgets.iconForClass[k], UI.icons.iconBase)
235252
}
236253
}
237-
return iconDir + 'noun_10636_grey.svg' // Gret Circle - some thing
254+
return iconDir + 'noun_10636_grey.svg' // Grey Circle - some thing
238255
}
239256

240257
// @@ Also add icons for *properties* like home, work, email, range, domain, comment,
@@ -278,7 +295,7 @@ var faviconOrDefault = function (dom, x) {
278295
(isOrigin(x) ? 'noun_15177.svg' : 'noun_681601.svg'))
279296
if (x.uri && x.uri.startsWith('https:') && (x.uri.indexOf('#') < 0)) {
280297
var res = dom.createElement('object') // favico with a fallback of a default image if no favicon
281-
res.setAttribute('data', x.site().uri + 'favicon.ico')
298+
res.setAttribute('data', tempSite(x) + 'favicon.ico')
282299
res.setAttribute('type', 'image/x-icon')
283300
res.appendChild(image) // fallback
284301
return res
@@ -390,7 +407,7 @@ UI.widgets.askName = function (dom, kb, container, predicate, klass, noun) {
390407
// namefield.setAttribute('class', 'pendingedit')
391408
// namefield.disabled = true
392409
form.parentNode.removeChild(form)
393-
resolve(namefield.value)
410+
resolve(namefield.value.trim())
394411
}
395412

396413
namefield.addEventListener('keyup', function (e) {

0 commit comments

Comments
 (0)