@@ -22,7 +22,7 @@ class WBCollectionListing extends Component { | |||||
prepareRows(items, ownerLookup) { | prepareRows(items, ownerLookup) { | ||||
let { app, renderRenameLink, renderDeleteButton, | let { app, renderRenameLink, renderDeleteButton, | ||||
renderSelectionCell } = this.props; | |||||
renderSelectionCell, renderSharingButton } = this.props; | |||||
return items.map(item => [ | return items.map(item => [ | ||||
renderSelectionCell(item), | renderSelectionCell(item), | ||||
@@ -59,6 +59,8 @@ class WBCollectionListing extends Component { | |||||
</a> | </a> | ||||
{ renderDeleteButton(item, () => this.fetchItems()) } | { renderDeleteButton(item, () => this.fetchItems()) } | ||||
{ renderSharingButton(item) } | |||||
</div>) | </div>) | ||||
]); | ]); | ||||
} | } | ||||
@@ -29,7 +29,7 @@ class WBProcessListing extends Component { | |||||
prepareRows(requests, containerLookup, ownerLookup, outputLookup) { | prepareRows(requests, containerLookup, ownerLookup, outputLookup) { | ||||
const { app, renderRenameLink, renderDeleteButton, | const { app, renderRenameLink, renderDeleteButton, | ||||
renderSelectionCell } = this.props; | |||||
renderSelectionCell, renderSharingButton } = this.props; | |||||
return requests.map(item => { | return requests.map(item => { | ||||
return ( [ | return ( [ | ||||
renderSelectionCell(item), | renderSelectionCell(item), | ||||
@@ -47,6 +47,7 @@ class WBProcessListing extends Component { | |||||
( <WBNameAndUuid app={ app } uuid={ item['output_uuid'] } /> ), | ( <WBNameAndUuid app={ app } uuid={ item['output_uuid'] } /> ), | ||||
(<div> | (<div> | ||||
{ renderDeleteButton(item, () => this.fetchItems()) } | { renderDeleteButton(item, () => this.fetchItems()) } | ||||
{ renderSharingButton(item) } | |||||
</div>) | </div>) | ||||
] ); | ] ); | ||||
}); | }); | ||||
@@ -22,7 +22,7 @@ class WBWorkflowListing extends Component { | |||||
prepareRows(items, ownerLookup) { | prepareRows(items, ownerLookup) { | ||||
const { renderRenameLink, renderDeleteButton, | const { renderRenameLink, renderDeleteButton, | ||||
renderSelectionCell } = this.props; | |||||
renderSelectionCell, renderSharingButton } = this.props; | |||||
return items.map(item => [ | return items.map(item => [ | ||||
renderSelectionCell(item), | renderSelectionCell(item), | ||||
( | ( | ||||
@@ -43,6 +43,7 @@ class WBWorkflowListing extends Component { | |||||
href={ urlForObject(item, 'launch') }><i class="fas fa-running"></i></a> | href={ urlForObject(item, 'launch') }><i class="fas fa-running"></i></a> | ||||
<button class="btn btn-outline-primary mx-1 my-1" title="View"><i class="far fa-eye"></i></button> | <button class="btn btn-outline-primary mx-1 my-1" title="View"><i class="far fa-eye"></i></button> | ||||
{ renderDeleteButton(item, () => this.fetchItems()) } | { renderDeleteButton(item, () => this.fetchItems()) } | ||||
{ renderSharingButton(item) } | |||||
</div>) | </div>) | ||||
]); | ]); | ||||
} | } | ||||
@@ -194,7 +194,8 @@ class WBBrowse extends Component { | |||||
getPageUrl={ i => this.getUrl({ 'collectionPage': i }) } | getPageUrl={ i => this.getUrl({ 'collectionPage': i }) } | ||||
renderRenameLink={ (it, cb) => this.renderRenameLink(it, cb) } | renderRenameLink={ (it, cb) => this.renderRenameLink(it, cb) } | ||||
renderDeleteButton={ (it, cb) => this.renderDeleteButton(it, cb) } | renderDeleteButton={ (it, cb) => this.renderDeleteButton(it, cb) } | ||||
renderSelectionCell={ it => this.renderSelectionCell(it) } /> | |||||
renderSelectionCell={ it => this.renderSelectionCell(it) } | |||||
renderSharingButton={ it => this.renderSharingButton(it) } /> | |||||
) : (objTypeTab === 'process') ? ( | ) : (objTypeTab === 'process') ? ( | ||||
<WBProcessListing app={ app } | <WBProcessListing app={ app } | ||||
@@ -205,7 +206,8 @@ class WBBrowse extends Component { | |||||
onPageChanged={ i => this.route({ 'processPage': i }) } | onPageChanged={ i => this.route({ 'processPage': i }) } | ||||
renderRenameLink={ (it, cb) => this.renderRenameLink(it, cb) } | renderRenameLink={ (it, cb) => this.renderRenameLink(it, cb) } | ||||
renderDeleteButton={ (it, cb) => this.renderDeleteButton(it, cb) } | renderDeleteButton={ (it, cb) => this.renderDeleteButton(it, cb) } | ||||
renderSelectionCell={ it => this.renderSelectionCell(it) } /> | |||||
renderSelectionCell={ it => this.renderSelectionCell(it) } | |||||
renderSharingButton={ it => this.renderSharingButton(it) } /> | |||||
) : (objTypeTab === 'workflow') ? ( | ) : (objTypeTab === 'workflow') ? ( | ||||
<WBWorkflowListing app={ app } | <WBWorkflowListing app={ app } | ||||
@@ -215,7 +217,8 @@ class WBBrowse extends Component { | |||||
getPageUrl={ i => this.getUrl({ 'workflowPage': i }) } | getPageUrl={ i => this.getUrl({ 'workflowPage': i }) } | ||||
renderRenameLink={ (it, cb) => this.renderRenameLink(it, cb) } | renderRenameLink={ (it, cb) => this.renderRenameLink(it, cb) } | ||||
renderDeleteButton={ (it, cb) => this.renderDeleteButton(it, cb) } | renderDeleteButton={ (it, cb) => this.renderDeleteButton(it, cb) } | ||||
renderSelectionCell={ it => this.renderSelectionCell(it) } /> | |||||
renderSelectionCell={ it => this.renderSelectionCell(it) } | |||||
renderSharingButton={ it => this.renderSharingButton(it) } /> | |||||
) : null | ) : null | ||||
} | } | ||||
@@ -22,37 +22,116 @@ class WBSharingPage extends Component { | |||||
const { app, uuid } = this.props; | const { app, uuid } = this.props; | ||||
const { arvHost, arvToken } = app.state; | const { arvHost, arvToken } = app.state; | ||||
let prom = makeArvadosRequest(arvHost, arvToken, | let prom = makeArvadosRequest(arvHost, arvToken, | ||||
'/arvados/v1/permissions/' + encodeURIComponent(uuid)); | |||||
'/arvados/v1/permissions/' + encodeURIComponent(uuid) + | |||||
'?limit=100000'); | |||||
prom = prom.then(xhr => this.setState({ | prom = prom.then(xhr => this.setState({ | ||||
'entries': xhr.response.items, | |||||
'rows': this.prepareRows(xhr.response.items) | 'rows': this.prepareRows(xhr.response.items) | ||||
})); | })); | ||||
} | } | ||||
deletePermission(uuid) { | |||||
throw Error('Not implemented'); | |||||
deleteEntry(it) { | |||||
it._delete = true; | |||||
this.setState({ rows: this.prepareRows(this.state.entries) }); | |||||
} | } | ||||
prepareRows(items) { | prepareRows(items) { | ||||
const { app } = this.props; | const { app } = this.props; | ||||
return items.map(it => [ | |||||
return items.filter(it => (!it._delete)).map(it => [ | |||||
( <WBNameAndUuid app={ app } uuid={ it.tail_uuid } /> ), | ( <WBNameAndUuid app={ app } uuid={ it.tail_uuid } /> ), | ||||
( <WBSelect value={ it.name } options={ ['can_read', 'can_write', 'can_manage'] } /> ), | |||||
( <WBSelect value={ it.name } | |||||
options={ ['can_read', 'can_write', 'can_manage'] } | |||||
onChange={ e => this.modifyEntry(it, e.target.value) } /> ), | |||||
( <button class="btn btn-outline-danger m-1" title="Delete" | ( <button class="btn btn-outline-danger m-1" title="Delete" | ||||
onclick={ () => this.deletePermission(it.uuid) }> | |||||
onclick={ () => this.deleteEntry(it) }> | |||||
<i class="fas fa-trash"></i> | <i class="fas fa-trash"></i> | ||||
</button> ) | </button> ) | ||||
]); | ]); | ||||
} | } | ||||
addEntry(it) { | |||||
throw Error('Not implemented'); | |||||
modifyEntry(it, newPermissionName) { | |||||
it.name = newPermissionName; | |||||
it._dirty = true; | |||||
// this.setState({ rows: this.prepareRows(this.state.entries) }); | |||||
} | |||||
addEntry(it, permissionName='can_read') { | |||||
// throw Error('Not implemented'); | |||||
const { uuid } = this.props; | |||||
let { entries } = this.state; | |||||
if (entries.filter(e => (e.tail_uuid === it.uuid)).length > 0) | |||||
return; // already in the list | |||||
const e = { | |||||
//_dirty: true, | |||||
link_class: 'permission', | |||||
head_uuid: uuid, | |||||
tail_uuid: it.uuid, | |||||
name: permissionName | |||||
}; | |||||
entries = entries.concat([e]); | |||||
this.setState({ | |||||
entries, | |||||
rows: this.prepareRows(entries) | |||||
}); | |||||
} | |||||
disableControls() { | |||||
$('input, select, button').attr('disabled', 'disabled'); | |||||
$('a').each(function() { $(this).data('old_href', $(this).attr('href')); }); | |||||
$('a').attr('href', null); | |||||
} | |||||
enableControls() { | |||||
$('input, select, button').attr('disabled', null); | |||||
$('a').each(function() { $(this).attr('href', $(this).data('old_href')); }); | |||||
} | } | ||||
save() { | save() { | ||||
throw Error('Not implemented'); | |||||
const { entries } = this.state; | |||||
const { arvHost, arvToken } = this.props.app.state; | |||||
let prom = new Promise(accept => accept()); | |||||
this.disableControls(); | |||||
this.setState({ working: true }); | |||||
for (let i = 0; i < entries.length; i++) { | |||||
const e = entries[i]; | |||||
//if (!e._dirty && !e._delete) | |||||
//continue; | |||||
if (!e.uuid) { | |||||
prom = prom.then(() => makeArvadosRequest(arvHost, arvToken, | |||||
'/arvados/v1/links', | |||||
{ 'method': 'POST', | |||||
'data': JSON.stringify({ | |||||
'link_class': 'permission', | |||||
'head_uuid': e.head_uuid, | |||||
'tail_uuid': e.tail_uuid, | |||||
'name': e.name | |||||
}) })); | |||||
} else if (e._delete) { | |||||
prom = prom.then(() => makeArvadosRequest(arvHost, arvToken, | |||||
'/arvados/v1/links/' + e.uuid, | |||||
{ 'method': 'DELETE' })); | |||||
} else if (e._dirty) { | |||||
prom = prom.then(() => makeArvadosRequest(arvHost, arvToken, | |||||
'/arvados/v1/links/' + e.uuid, | |||||
{ 'method': 'PUT', | |||||
'data': JSON.stringify({ | |||||
'name': e.name | |||||
}) })); | |||||
} | |||||
prom = prom.catch(() => {}); | |||||
} | |||||
prom = prom.then(() => { | |||||
this.enableControls(); | |||||
this.fetchData(); | |||||
this.setState({ working: false }); | |||||
}); | |||||
} | } | ||||
render({ app, uuid }, { rows }) { | |||||
render({ app, uuid }, { rows, working }) { | |||||
return ( | return ( | ||||
<div> | <div> | ||||
<WBNavbarCommon app={ app } /> | <WBNavbarCommon app={ app } /> | ||||
@@ -70,6 +149,12 @@ class WBSharingPage extends Component { | |||||
<WBPickObjectDialog app={ app } ref={ this.dialogRef } /> | <WBPickObjectDialog app={ app } ref={ this.dialogRef } /> | ||||
{ working ? (<div class="progress my-2"> | |||||
<div class={ 'progress-bar progress-bar-striped progress-bar-animated' } | |||||
role="progressbar" aria-valuenow="100" aria-valuemin="0" | |||||
aria-valuemax="100" style="width: 100%"></div> | |||||
</div>) : null } | |||||
<button class="btn btn-outline-secondary mr-2" | <button class="btn btn-outline-secondary mr-2" | ||||
onclick={ () => this.dialogRef.current.show('Select User', 'user', it => this.addEntry(it)) }>Add User...</button> | onclick={ () => this.dialogRef.current.show('Select User', 'user', it => this.addEntry(it)) }>Add User...</button> | ||||
<button class="btn btn-outline-secondary mr-2" | <button class="btn btn-outline-secondary mr-2" | ||||