IF YOU WOULD LIKE TO GET AN ACCOUNT, please write an email to s dot adaszewski at gmail dot com. User accounts are meant only to report issues and/or generate pull requests. This is a purpose-specific Git hosting for ADARED projects. Thank you for your understanding!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

178 lines
5.6KB

  1. //
  2. // Copyright (C) Stanislaw Adaszewski, 2020
  3. // Contact: s.adaszewski@gmail.com
  4. // Website: https://adared.ch/wba
  5. // License: GNU Affero General Public License, Version 3
  6. //
  7. import { h, Component, createRef } from 'preact';
  8. import WBNavbarCommon from 'wb-navbar-common';
  9. import WBArvadosCrumbs from 'wb-arvados-crumbs';
  10. import WBNameAndUuid from 'wb-name-and-uuid';
  11. import WBSelect from 'wb-select';
  12. import WBTable from 'wb-table';
  13. import WBPickObjectDialog from 'wb-pick-object-dialog';
  14. import makeArvadosRequest from 'make-arvados-request';
  15. class WBSharingPage extends Component {
  16. constructor(...args) {
  17. super(...args);
  18. this.state.rows = [];
  19. this.dialogRef = createRef();
  20. }
  21. componentDidMount() {
  22. this.fetchData();
  23. }
  24. fetchData() {
  25. const { app, uuid } = this.props;
  26. const { arvHost, arvToken } = app.state;
  27. let prom = makeArvadosRequest(arvHost, arvToken,
  28. '/arvados/v1/permissions/' + encodeURIComponent(uuid) +
  29. '?limit=100000');
  30. prom = prom.then(xhr => this.setState({
  31. 'entries': xhr.response.items,
  32. 'rows': this.prepareRows(xhr.response.items)
  33. }));
  34. }
  35. deleteEntry(it) {
  36. it._delete = true;
  37. this.setState({ rows: this.prepareRows(this.state.entries) });
  38. }
  39. prepareRows(items) {
  40. const { app } = this.props;
  41. return items.filter(it => (!it._delete)).map(it => [
  42. ( <WBNameAndUuid app={ app } uuid={ it.tail_uuid } /> ),
  43. ( <WBSelect value={ it.name }
  44. options={ ['can_read', 'can_write', 'can_manage'] }
  45. onChange={ e => this.modifyEntry(it, e.target.value) } /> ),
  46. ( <button class="btn btn-outline-danger m-1" title="Delete"
  47. onclick={ () => this.deleteEntry(it) }>
  48. <i class="fas fa-trash"></i>
  49. </button> )
  50. ]);
  51. }
  52. modifyEntry(it, newPermissionName) {
  53. it.name = newPermissionName;
  54. it._dirty = true;
  55. // this.setState({ rows: this.prepareRows(this.state.entries) });
  56. }
  57. addEntry(it, permissionName='can_read') {
  58. // throw Error('Not implemented');
  59. const { uuid } = this.props;
  60. let { entries } = this.state;
  61. if (entries.filter(e => (e.tail_uuid === it.uuid)).length > 0)
  62. return; // already in the list
  63. const e = {
  64. //_dirty: true,
  65. link_class: 'permission',
  66. head_uuid: uuid,
  67. tail_uuid: it.uuid,
  68. name: permissionName
  69. };
  70. entries = entries.concat([e]);
  71. this.setState({
  72. entries,
  73. rows: this.prepareRows(entries)
  74. });
  75. }
  76. disableControls() {
  77. $('input, select, button').attr('disabled', 'disabled');
  78. $('a').each(function() { $(this).data('old_href', $(this).attr('href')); });
  79. $('a').attr('href', null);
  80. }
  81. enableControls() {
  82. $('input, select, button').attr('disabled', null);
  83. $('a').each(function() { $(this).attr('href', $(this).data('old_href')); });
  84. }
  85. save() {
  86. const { entries } = this.state;
  87. const { arvHost, arvToken } = this.props.app.state;
  88. let prom = new Promise(accept => accept());
  89. this.disableControls();
  90. this.setState({ working: true });
  91. for (let i = 0; i < entries.length; i++) {
  92. const e = entries[i];
  93. //if (!e._dirty && !e._delete)
  94. //continue;
  95. if (!e.uuid) {
  96. prom = prom.then(() => makeArvadosRequest(arvHost, arvToken,
  97. '/arvados/v1/links',
  98. { 'method': 'POST',
  99. 'data': JSON.stringify({
  100. 'link_class': 'permission',
  101. 'head_uuid': e.head_uuid,
  102. 'tail_uuid': e.tail_uuid,
  103. 'name': e.name
  104. }) }));
  105. } else if (e._delete) {
  106. prom = prom.then(() => makeArvadosRequest(arvHost, arvToken,
  107. '/arvados/v1/links/' + e.uuid,
  108. { 'method': 'DELETE' }));
  109. } else if (e._dirty) {
  110. prom = prom.then(() => makeArvadosRequest(arvHost, arvToken,
  111. '/arvados/v1/links/' + e.uuid,
  112. { 'method': 'PUT',
  113. 'data': JSON.stringify({
  114. 'name': e.name
  115. }) }));
  116. }
  117. prom = prom.catch(() => {});
  118. }
  119. prom = prom.then(() => {
  120. this.enableControls();
  121. this.fetchData();
  122. this.setState({ working: false });
  123. });
  124. }
  125. render({ app, uuid }, { rows, working }) {
  126. return (
  127. <div>
  128. <WBNavbarCommon app={ app } />
  129. <WBArvadosCrumbs app={ app } uuid={ uuid } />
  130. <div class="container-fluid">
  131. <div class="my-2">
  132. This is the sharing management page for { uuid }
  133. </div>
  134. <WBTable columns={ [ 'Name', 'Permission', '' ] }
  135. headerClasses={ [ null, null, 'w-1' ] }
  136. rows={ rows } />
  137. <WBPickObjectDialog app={ app } ref={ this.dialogRef } />
  138. { working ? (<div class="progress my-2">
  139. <div class={ 'progress-bar progress-bar-striped progress-bar-animated' }
  140. role="progressbar" aria-valuenow="100" aria-valuemin="0"
  141. aria-valuemax="100" style="width: 100%"></div>
  142. </div>) : null }
  143. <button class="btn btn-outline-secondary mr-2"
  144. onclick={ () => this.dialogRef.current.show('Select User', 'user', it => this.addEntry(it)) }>Add User...</button>
  145. <button class="btn btn-outline-secondary mr-2"
  146. onclick={ () => this.dialogRef.current.show('Select Group', 'group', it => this.addEntry(it), [['group_class', '=', 'role']]) }>Add Group...</button>
  147. <button class="btn btn-primary mr-2"
  148. onclick={ () => this.save() }>Save</button>
  149. </div>
  150. </div>
  151. );
  152. }
  153. }
  154. export default WBSharingPage;