| @@ -18,7 +18,7 @@ class WBProjectListing extends Component { | |||||
| prepareRows(items) { | prepareRows(items) { | ||||
| const { app, renderRenameLink, renderDeleteButton, | const { app, renderRenameLink, renderDeleteButton, | ||||
| renderSelectionCell } = this.props; | |||||
| renderSelectionCell, renderSharingButton } = this.props; | |||||
| return items.map(item => [ | return items.map(item => [ | ||||
| renderSelectionCell(item), | renderSelectionCell(item), | ||||
| @@ -39,6 +39,7 @@ class WBProjectListing extends Component { | |||||
| <i class="fas fa-toolbox"></i> | <i class="fas fa-toolbox"></i> | ||||
| </button> | </button> | ||||
| { renderDeleteButton(item, () => this.fetchItems()) } | { renderDeleteButton(item, () => this.fetchItems()) } | ||||
| { renderSharingButton(item) } | |||||
| </div>) | </div>) | ||||
| ]); | ]); | ||||
| } | } | ||||
| @@ -42,6 +42,7 @@ class WBBrowseDialog extends Component { | |||||
| this.state.bottomPage = 0; | this.state.bottomPage = 0; | ||||
| this.state.collectionPath = ''; | this.state.collectionPath = ''; | ||||
| this.state.textSearch = ''; | this.state.textSearch = ''; | ||||
| this.state.id = ('id' in this.props) ? this.props.id : uuid.v4(); | |||||
| } | } | ||||
| navigateBack() { | navigateBack() { | ||||
| @@ -134,10 +135,14 @@ class WBBrowseDialog extends Component { | |||||
| ); | ); | ||||
| } | } | ||||
| render({ app, id, selectMany, selectWhat }, | |||||
| show(callback) { | |||||
| $('#' + this.state.id).modal(); | |||||
| } | |||||
| render({ app, selectMany, selectWhat }, | |||||
| { history, currentUrl, mode, uuid, | { history, currentUrl, mode, uuid, | ||||
| topPage, bottomPage, textSearch, | topPage, bottomPage, textSearch, | ||||
| collectionPath }) { | |||||
| collectionPath, id }) { | |||||
| return ( | return ( | ||||
| <div class="modal" id={ id } tabindex="-1" role="dialog"> | <div class="modal" id={ id } tabindex="-1" role="dialog"> | ||||
| @@ -13,6 +13,7 @@ import WBWorkflowView from 'wb-workflow-view'; | |||||
| import WBLaunchWorkflowPage from 'wb-launch-workflow-page'; | import WBLaunchWorkflowPage from 'wb-launch-workflow-page'; | ||||
| import WBDownloadPage from 'wb-download-page'; | import WBDownloadPage from 'wb-download-page'; | ||||
| import WBImageViewerPage from 'wb-image-viewer-page'; | import WBImageViewerPage from 'wb-image-viewer-page'; | ||||
| import WBSharingPage from 'wb-sharing-page'; | |||||
| import arvadosTypeName from 'arvados-type-name'; | import arvadosTypeName from 'arvados-type-name'; | ||||
| class WBApp extends Component { | class WBApp extends Component { | ||||
| @@ -97,6 +98,8 @@ class WBApp extends Component { | |||||
| <WBDownloadPage path="/download/:blocksBlobUrl/:inline?" app={ this } /> | <WBDownloadPage path="/download/:blocksBlobUrl/:inline?" app={ this } /> | ||||
| <WBImageViewerPage path="/image-viewer/:blobUrl" app={ this } /> | <WBImageViewerPage path="/image-viewer/:blobUrl" app={ this } /> | ||||
| <WBSharingPage path="/sharing/:uuid" app={ this } /> | |||||
| </Router> | </Router> | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -109,6 +109,15 @@ class WBBrowse extends Component { | |||||
| ); | ); | ||||
| } | } | ||||
| renderSharingButton(item) { | |||||
| return ( | |||||
| <a class="btn btn-outline-success m-1" title="Share" | |||||
| href={ '/sharing/' + item.uuid }> | |||||
| <i class="fas fa-share-alt"></i> | |||||
| </a> | |||||
| ); | |||||
| } | |||||
| moveOrCopyOp(op) { | moveOrCopyOp(op) { | ||||
| const { ownerUuid, app } = this.props; | const { ownerUuid, app } = this.props; | ||||
| const { selected } = this.state; | const { selected } = this.state; | ||||
| @@ -164,7 +173,8 @@ class WBBrowse extends Component { | |||||
| getPageUrl={ i => this.getUrl({ 'activePage': i }) } | getPageUrl={ i => this.getUrl({ 'activePage': 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) } /> | |||||
| { (mode !== 'browse') ? null : ( | { (mode !== 'browse') ? null : ( | ||||
| <WBTabs tabs={ [ | <WBTabs tabs={ [ | ||||
| @@ -0,0 +1,71 @@ | |||||
| import { h, Component, createRef } from 'preact'; | |||||
| import WBNavbarCommon from 'wb-navbar-common'; | |||||
| import WBArvadosCrumbs from 'wb-arvados-crumbs'; | |||||
| import WBNameAndUuid from 'wb-name-and-uuid'; | |||||
| import WBSelect from 'wb-select'; | |||||
| import WBTable from 'wb-table'; | |||||
| import WBBrowseDialog from 'wb-browse-dialog'; | |||||
| import makeArvadosRequest from 'make-arvados-request'; | |||||
| class WBSharingPage extends Component { | |||||
| constructor(...args) { | |||||
| super(...args); | |||||
| this.state.rows = []; | |||||
| this.browseDialogRef = createRef(); | |||||
| } | |||||
| componentDidMount() { | |||||
| this.fetchData(); | |||||
| } | |||||
| fetchData() { | |||||
| const { app, uuid } = this.props; | |||||
| const { arvHost, arvToken } = app.state; | |||||
| let prom = makeArvadosRequest(arvHost, arvToken, | |||||
| '/arvados/v1/permissions/' + encodeURIComponent(uuid)); | |||||
| prom = prom.then(xhr => this.setState({ | |||||
| 'rows': this.prepareRows(xhr.response.items) | |||||
| })); | |||||
| } | |||||
| deletePermission(uuid) { | |||||
| throw Error('Not implemented'); | |||||
| } | |||||
| prepareRows(items) { | |||||
| const { app } = this.props; | |||||
| return items.map(it => [ | |||||
| ( <WBNameAndUuid app={ app } uuid={ it.tail_uuid } /> ), | |||||
| ( <WBSelect value={ it.name } options={ ['can_read', 'can_write', 'can_manage'] } /> ), | |||||
| ( <button class="btn btn-outline-danger m-1" title="Delete" | |||||
| onclick={ () => this.deletePermission(it.uuid) }> | |||||
| <i class="fas fa-trash"></i> | |||||
| </button> ) | |||||
| ]); | |||||
| } | |||||
| render({ app, uuid }, { rows }) { | |||||
| return ( | |||||
| <div> | |||||
| <WBNavbarCommon app={ app } /> | |||||
| <WBArvadosCrumbs app={ app } uuid={ uuid } /> | |||||
| <div class="my-2"> | |||||
| This is the sharing management page for { uuid } | |||||
| </div> | |||||
| <WBTable columns={ [ 'Name', 'Permission', '' ] } | |||||
| headerClasses={ [ null, null, 'w-1' ] } | |||||
| rows={ rows } /> | |||||
| <WBBrowseDialog app={ app } ref={ this.browseDialogRef } /> | |||||
| <button class="btn btn-primary" | |||||
| onclick={ () => this.browseDialogRef.current.show() }>Add</button> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| } | |||||
| export default WBSharingPage; | |||||
| @@ -0,0 +1,19 @@ | |||||
| import { h, Component } from 'preact'; | |||||
| class WBSelect extends Component { | |||||
| render({ value, options, onChange }) { | |||||
| return ( | |||||
| <select class="form-control" onchange={ onChange }> | |||||
| { options.map(o => { | |||||
| const name = (typeof(o) === 'string') ? o : o.name; | |||||
| const id = (typeof(o) === 'object') ? o.id : name; | |||||
| return ( | |||||
| <option selected={ (value === id) } value={ id }>{ name }</option> | |||||
| ); | |||||
| }) } | |||||
| </select> | |||||
| ); | |||||
| } | |||||
| } | |||||
| export default WBSelect; | |||||