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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. import { ActionsDropdown, ItemButton, ItemLink, PopupZLevel } from 'design-system';
  21. import { difference } from 'lodash';
  22. import * as React from 'react';
  23. import { withRouter } from '~sonar-aligned/components/hoc/withRouter';
  24. import { queryToSearchString } from '~sonar-aligned/helpers/urls';
  25. import { Router } from '~sonar-aligned/types/router';
  26. import {
  27. deletePermissionTemplate,
  28. setDefaultPermissionTemplate,
  29. updatePermissionTemplate,
  30. } from '../../../api/permissions';
  31. import { translate, translateWithParameters } from '../../../helpers/l10n';
  32. import { PermissionTemplate } from '../../../types/types';
  33. import { PERMISSION_TEMPLATES_PATH } from '../utils';
  34. import DeleteForm from './DeleteForm';
  35. import Form from './Form';
  36. interface Props {
  37. fromDetails?: boolean;
  38. permissionTemplate: PermissionTemplate;
  39. refresh: () => void;
  40. router: Router;
  41. topQualifiers: string[];
  42. }
  43. interface State {
  44. deleteForm: boolean;
  45. updateModal: boolean;
  46. }
  47. class ActionsCell extends React.PureComponent<Props, State> {
  48. mounted = false;
  49. state: State = { deleteForm: false, updateModal: false };
  50. componentDidMount() {
  51. this.mounted = true;
  52. }
  53. componentWillUnmount() {
  54. this.mounted = false;
  55. }
  56. handleUpdateClick = () => {
  57. this.setState({ updateModal: true });
  58. };
  59. handleCloseUpdateModal = () => {
  60. if (this.mounted) {
  61. this.setState({ updateModal: false });
  62. }
  63. };
  64. handleSubmitUpdateModal = (data: {
  65. description: string;
  66. name: string;
  67. projectKeyPattern: string;
  68. }) => {
  69. return updatePermissionTemplate({ id: this.props.permissionTemplate.id, ...data }).then(
  70. this.props.refresh,
  71. );
  72. };
  73. handleDeleteClick = () => {
  74. this.setState({ deleteForm: true });
  75. };
  76. handleCloseDeleteForm = () => {
  77. if (this.mounted) {
  78. this.setState({ deleteForm: false });
  79. }
  80. };
  81. handleDeleteSubmit = () => {
  82. return deletePermissionTemplate({ templateId: this.props.permissionTemplate.id }).then(() => {
  83. this.props.router.replace(PERMISSION_TEMPLATES_PATH);
  84. this.props.refresh();
  85. });
  86. };
  87. setDefault = (qualifier: string) => () => {
  88. setDefaultPermissionTemplate(this.props.permissionTemplate.id, qualifier).then(
  89. this.props.refresh,
  90. () => {},
  91. );
  92. };
  93. getAvailableQualifiers() {
  94. return difference(this.props.topQualifiers, this.props.permissionTemplate.defaultFor);
  95. }
  96. renderSetDefaultsControl() {
  97. const availableQualifiers = this.getAvailableQualifiers();
  98. if (availableQualifiers.length === 0) {
  99. return null;
  100. }
  101. return this.props.topQualifiers.length === 1
  102. ? this.renderIfSingleTopQualifier(availableQualifiers)
  103. : this.renderIfMultipleTopQualifiers(availableQualifiers);
  104. }
  105. renderSetDefaultLink(qualifier: string, child: React.ReactNode) {
  106. return (
  107. <ItemButton
  108. className="js-set-default"
  109. data-qualifier={qualifier}
  110. key={qualifier}
  111. onClick={this.setDefault(qualifier)}
  112. >
  113. {child}
  114. </ItemButton>
  115. );
  116. }
  117. renderIfSingleTopQualifier(availableQualifiers: string[]) {
  118. return availableQualifiers.map((qualifier) =>
  119. this.renderSetDefaultLink(
  120. qualifier,
  121. <span>{translate('permission_templates.set_default')}</span>,
  122. ),
  123. );
  124. }
  125. renderIfMultipleTopQualifiers(availableQualifiers: string[]) {
  126. return availableQualifiers.map((qualifier) =>
  127. this.renderSetDefaultLink(
  128. qualifier,
  129. <span>
  130. {translate('permission_templates.set_default_for')} {translate('qualifiers', qualifier)}
  131. </span>,
  132. ),
  133. );
  134. }
  135. render() {
  136. const { permissionTemplate: t } = this.props;
  137. return (
  138. <>
  139. <ActionsDropdown
  140. allowResizing
  141. id={`permission-template-actions-${t.id}`}
  142. zLevel={PopupZLevel.Global}
  143. toggleClassName="it__permission-actions"
  144. ariaLabel={translateWithParameters('permission_templates.show_actions_for_x', t.name)}
  145. >
  146. <>
  147. {this.renderSetDefaultsControl()}
  148. {!this.props.fromDetails && (
  149. <ItemLink
  150. to={{
  151. pathname: PERMISSION_TEMPLATES_PATH,
  152. search: queryToSearchString({ id: t.id }),
  153. }}
  154. >
  155. {translate('edit_permissions')}
  156. </ItemLink>
  157. )}
  158. <ItemButton className="js-update" onClick={this.handleUpdateClick}>
  159. {translate('update_details')}
  160. </ItemButton>
  161. {t.defaultFor.length === 0 && (
  162. <ItemButton className="js-delete" onClick={this.handleDeleteClick}>
  163. {translate('delete')}
  164. </ItemButton>
  165. )}
  166. </>
  167. </ActionsDropdown>
  168. {this.state.updateModal && (
  169. <Form
  170. confirmButtonText={translate('update_verb')}
  171. header={translate('permission_template.edit_template')}
  172. onClose={this.handleCloseUpdateModal}
  173. onSubmit={this.handleSubmitUpdateModal}
  174. permissionTemplate={t}
  175. />
  176. )}
  177. {this.state.deleteForm && (
  178. <DeleteForm
  179. onClose={this.handleCloseDeleteForm}
  180. onSubmit={this.handleDeleteSubmit}
  181. permissionTemplate={t}
  182. />
  183. )}
  184. </>
  185. );
  186. }
  187. }
  188. export default withRouter(ActionsCell);