* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { BaseSearchProjectsParameters } from './components';
+import { PermissionTemplate } from '../app/types';
+import throwGlobalError from '../app/utils/throwGlobalError';
import { getJSON, post, postJSON, RequestData } from '../helpers/request';
const PAGE_SIZE = 100;
return post('/api/permissions/remove_group', data);
}
-export interface PermissionTemplate {
- id: string;
- name: string;
- description?: string;
- projectKeyPattern?: string;
- createdAt: string;
- updatedAt?: string;
- permissions: Array<{
- key: string;
- usersCount: number;
- groupsCount: number;
- withProjectCreator?: boolean;
- }>;
-}
-
interface GetPermissionTemplatesResponse {
permissionTemplates: PermissionTemplate[];
defaultTemplates: Array<{ templateId: string; qualifier: string }>;
return post('/api/permissions/update_template', data);
}
-export function deletePermissionTemplate(data: RequestData): Promise<void> {
- return post('/api/permissions/delete_template', data);
+export function deletePermissionTemplate(data: RequestData) {
+ return post('/api/permissions/delete_template', data).catch(throwGlobalError);
}
/**
return post('/api/permissions/set_default_template', { templateId, qualifier });
}
-export function applyTemplateToProject(data: RequestData): Promise<void> {
- return post('/api/permissions/apply_template', data);
+export function applyTemplateToProject(data: RequestData) {
+ return post('/api/permissions/apply_template', data).catch(throwGlobalError);
}
export function bulkApplyTemplate(data: BaseSearchProjectsParameters): Promise<void> {
}
interface ComponentConfiguration {
+ canApplyPermissionTemplate?: boolean;
extensions?: Extension[];
showBackgroundTasks?: boolean;
showLinks?: boolean;
value: string;
updatedAt?: string;
}
+
+export interface PermissionTemplate {
+ defaultFor: string[];
+ id: string;
+ name: string;
+ description?: string;
+ projectKeyPattern?: string;
+ createdAt: string;
+ updatedAt?: string;
+ permissions: Array<{
+ key: string;
+ usersCount: number;
+ groupsCount: number;
+ withProjectCreator?: boolean;
+ }>;
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import React from 'react';
-import PropTypes from 'prop-types';
-import { Link } from 'react-router';
-import { difference } from 'lodash';
-import Backbone from 'backbone';
-import { PermissionTemplateType, CallbackType } from '../propTypes';
-import ActionsDropdown, { ActionsDropdownItem } from '../../../components/controls/ActionsDropdown';
-import QualifierIcon from '../../../components/shared/QualifierIcon';
-import UpdateView from '../views/UpdateView';
-import DeleteView from '../views/DeleteView';
-import { translate } from '../../../helpers/l10n';
-import { setDefaultPermissionTemplate } from '../../../api/permissions';
-
-export default class ActionsCell extends React.PureComponent {
- static propTypes = {
- organization: PropTypes.object,
- permissionTemplate: PermissionTemplateType.isRequired,
- topQualifiers: PropTypes.array.isRequired,
- refresh: CallbackType,
- fromDetails: PropTypes.bool
- };
-
- static defaultProps = {
- fromDetails: false
- };
-
- static contextTypes = {
- router: PropTypes.object
- };
-
- handleUpdateClick = () => {
- new UpdateView({
- model: new Backbone.Model(this.props.permissionTemplate),
- refresh: this.props.refresh
- }).render();
- };
-
- handleDeleteClick = () => {
- new DeleteView({
- model: new Backbone.Model(this.props.permissionTemplate)
- })
- .on('done', () => {
- const pathname = this.props.organization
- ? `/organizations/${this.props.organization.key}/permission_templates`
- : '/permission_templates';
- this.context.router.replace(pathname);
- this.props.refresh();
- })
- .render();
- };
-
- setDefault = qualifier => () => {
- setDefaultPermissionTemplate(this.props.permissionTemplate.id, qualifier).then(
- this.props.refresh,
- () => {}
- );
- };
-
- getAvailableQualifiers() {
- const topQualifiers =
- this.props.organization && !this.props.organization.isDefault
- ? ['TRK']
- : this.props.topQualifiers;
- return difference(topQualifiers, this.props.permissionTemplate.defaultFor);
- }
-
- renderSetDefaultsControl() {
- const availableQualifiers = this.getAvailableQualifiers();
-
- if (availableQualifiers.length === 0) {
- return null;
- }
-
- return this.props.topQualifiers.length === 1
- ? this.renderIfSingleTopQualifier(availableQualifiers)
- : this.renderIfMultipleTopQualifiers(availableQualifiers);
- }
-
- renderSetDefaultLink(qualifier, child) {
- return (
- <ActionsDropdownItem
- key={qualifier}
- className="js-set-default"
- data-qualifier={qualifier}
- onClick={this.setDefault(qualifier)}>
- {child}
- </ActionsDropdownItem>
- );
- }
-
- renderIfSingleTopQualifier(availableQualifiers) {
- return availableQualifiers.map(qualifier =>
- this.renderSetDefaultLink(
- qualifier,
- <span>{translate('permission_templates.set_default')}</span>
- )
- );
- }
-
- renderIfMultipleTopQualifiers(availableQualifiers) {
- return availableQualifiers.map(qualifier =>
- this.renderSetDefaultLink(
- qualifier,
- <span>
- {translate('permission_templates.set_default_for')}{' '}
- <QualifierIcon qualifier={qualifier} /> {translate('qualifiers', qualifier)}
- </span>
- )
- );
- }
-
- render() {
- const { permissionTemplate: t, organization } = this.props;
-
- const pathname = organization
- ? `/organizations/${organization.key}/permission_templates`
- : '/permission_templates';
-
- return (
- <ActionsDropdown>
- {this.renderSetDefaultsControl()}
-
- {!this.props.fromDetails && (
- <ActionsDropdownItem to={{ pathname, query: { id: t.id } }}>
- {translate('edit_permissions')}
- </ActionsDropdownItem>
- )}
-
- <ActionsDropdownItem className="js-update" onClick={this.handleUpdateClick}>
- {translate('update_details')}
- </ActionsDropdownItem>
-
- {t.defaultFor.length === 0 && (
- <ActionsDropdownItem className="js-delete" onClick={this.handleDeleteClick}>
- {translate('delete')}
- </ActionsDropdownItem>
- )}
- </ActionsDropdown>
- );
- }
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import * as PropTypes from 'prop-types';
+import { difference } from 'lodash';
+import Form from './Form';
+import {
+ setDefaultPermissionTemplate,
+ deletePermissionTemplate,
+ updatePermissionTemplate
+} from '../../../api/permissions';
+import { PermissionTemplate } from '../../../app/types';
+import ActionsDropdown, { ActionsDropdownItem } from '../../../components/controls/ActionsDropdown';
+import ConfirmButton from '../../../components/controls/ConfirmButton';
+import QualifierIcon from '../../../components/shared/QualifierIcon';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+
+interface Props {
+ fromDetails?: boolean;
+ organization?: { isDefault?: boolean; key: string };
+ permissionTemplate: PermissionTemplate;
+ refresh: () => void;
+ topQualifiers: string[];
+}
+
+interface State {
+ updateModal: boolean;
+}
+
+export default class ActionsCell extends React.PureComponent<Props, State> {
+ mounted = false;
+
+ static contextTypes = {
+ router: PropTypes.object
+ };
+
+ state: State = { updateModal: false };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleUpdateClick = () => {
+ this.setState({ updateModal: true });
+ };
+
+ handleCloseUpdateModal = () => {
+ if (this.mounted) {
+ this.setState({ updateModal: false });
+ }
+ };
+
+ handleSubmitUpdateModal = (data: {
+ description: string;
+ name: string;
+ projectKeyPattern: string;
+ }) => {
+ return updatePermissionTemplate({ id: this.props.permissionTemplate.id, ...data }).then(
+ this.props.refresh
+ );
+ };
+
+ handleDelete = (templateId: string) => {
+ return deletePermissionTemplate({ templateId }).then(() => {
+ const pathname = this.props.organization
+ ? `/organizations/${this.props.organization.key}/permission_templates`
+ : '/permission_templates';
+ this.context.router.replace(pathname);
+ this.props.refresh();
+ });
+ };
+
+ setDefault = (qualifier: string) => () => {
+ setDefaultPermissionTemplate(this.props.permissionTemplate.id, qualifier).then(
+ this.props.refresh,
+ () => {}
+ );
+ };
+
+ getAvailableQualifiers() {
+ const topQualifiers =
+ this.props.organization && !this.props.organization.isDefault
+ ? ['TRK']
+ : this.props.topQualifiers;
+ return difference(topQualifiers, this.props.permissionTemplate.defaultFor);
+ }
+
+ renderSetDefaultsControl() {
+ const availableQualifiers = this.getAvailableQualifiers();
+
+ if (availableQualifiers.length === 0) {
+ return null;
+ }
+
+ return this.props.topQualifiers.length === 1
+ ? this.renderIfSingleTopQualifier(availableQualifiers)
+ : this.renderIfMultipleTopQualifiers(availableQualifiers);
+ }
+
+ renderSetDefaultLink(qualifier: string, child: React.ReactNode) {
+ return (
+ <ActionsDropdownItem
+ className="js-set-default"
+ data-qualifier={qualifier}
+ key={qualifier}
+ onClick={this.setDefault(qualifier)}>
+ {child}
+ </ActionsDropdownItem>
+ );
+ }
+
+ renderIfSingleTopQualifier(availableQualifiers: string[]) {
+ return availableQualifiers.map(qualifier =>
+ this.renderSetDefaultLink(
+ qualifier,
+ <span>{translate('permission_templates.set_default')}</span>
+ )
+ );
+ }
+
+ renderIfMultipleTopQualifiers(availableQualifiers: string[]) {
+ return availableQualifiers.map(qualifier =>
+ this.renderSetDefaultLink(
+ qualifier,
+ <span>
+ {translate('permission_templates.set_default_for')}{' '}
+ <QualifierIcon qualifier={qualifier} /> {translate('qualifiers', qualifier)}
+ </span>
+ )
+ );
+ }
+
+ render() {
+ const { permissionTemplate: t, organization } = this.props;
+
+ const pathname = organization
+ ? `/organizations/${organization.key}/permission_templates`
+ : '/permission_templates';
+
+ return (
+ <ActionsDropdown>
+ {this.renderSetDefaultsControl()}
+
+ {!this.props.fromDetails && (
+ <ActionsDropdownItem to={{ pathname, query: { id: t.id } }}>
+ {translate('edit_permissions')}
+ </ActionsDropdownItem>
+ )}
+
+ <ActionsDropdownItem className="js-update" onClick={this.handleUpdateClick}>
+ {translate('update_details')}
+ </ActionsDropdownItem>
+ {this.state.updateModal && (
+ <Form
+ confirmButtonText={translate('update_verb')}
+ header={translate('permission_template.edit_template')}
+ onClose={this.handleCloseUpdateModal}
+ onSubmit={this.handleSubmitUpdateModal}
+ permissionTemplate={t}
+ />
+ )}
+
+ {t.defaultFor.length === 0 && (
+ <ConfirmButton
+ confirmButtonText={translate('delete')}
+ confirmData={t.id}
+ isDestructive={true}
+ modalBody={translateWithParameters(
+ 'permission_template.do_you_want_to_delete_template_xxx',
+ t.name
+ )}
+ modalHeader={translate('permission_template.delete_confirm_title')}
+ onConfirm={this.handleDelete}>
+ {({ onClick }) => (
+ <ActionsDropdownItem className="js-delete" destructive={true} onClick={onClick}>
+ {translate('delete')}
+ </ActionsDropdownItem>
+ )}
+ </ConfirmButton>
+ )}
+ </ActionsDropdown>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import DeferredSpinner from '../../../components/common/DeferredSpinner';
+import SimpleModal from '../../../components/controls/SimpleModal';
+import { translate } from '../../../helpers/l10n';
+
+interface Props {
+ confirmButtonText: string;
+ header: string;
+ permissionTemplate?: { description?: string; name: string; projectKeyPattern?: string };
+ onClose: () => void;
+ onSubmit: (
+ data: { description: string; name: string; projectKeyPattern: string }
+ ) => Promise<void>;
+}
+
+interface State {
+ description: string;
+ name: string;
+ projectKeyPattern: string;
+}
+
+export default class Form extends React.PureComponent<Props, State> {
+ mounted = false;
+
+ constructor(props: Props) {
+ super(props);
+ this.state = {
+ description: (props.permissionTemplate && props.permissionTemplate.description) || '',
+ name: (props.permissionTemplate && props.permissionTemplate.name) || '',
+ projectKeyPattern:
+ (props.permissionTemplate && props.permissionTemplate.projectKeyPattern) || ''
+ };
+ }
+
+ handleSubmit = () => {
+ return this.props
+ .onSubmit({
+ description: this.state.description,
+ name: this.state.name,
+ projectKeyPattern: this.state.projectKeyPattern
+ })
+ .then(this.props.onClose);
+ };
+
+ handleNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+ this.setState({ name: event.currentTarget.value });
+ };
+
+ handleDescriptionChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
+ this.setState({ description: event.currentTarget.value });
+ };
+
+ handleProjectKeyPatternChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+ this.setState({ projectKeyPattern: event.currentTarget.value });
+ };
+
+ render() {
+ return (
+ <SimpleModal
+ header={this.props.header}
+ onClose={this.props.onClose}
+ onSubmit={this.handleSubmit}>
+ {({ onCloseClick, onFormSubmit, submitting }) => (
+ <form id="permission-template-form" onSubmit={onFormSubmit}>
+ <header className="modal-head">
+ <h2>{this.props.header}</h2>
+ </header>
+
+ <div className="modal-body">
+ <div className="modal-field">
+ <label htmlFor="permission-template-name">
+ {translate('name')}
+ <em className="mandatory">*</em>
+ </label>
+ <input
+ autoFocus={true}
+ id="permission-template-name"
+ maxLength={256}
+ name="name"
+ onChange={this.handleNameChange}
+ required={true}
+ type="text"
+ value={this.state.name}
+ />
+ <div className="modal-field-description">{translate('should_be_unique')}</div>
+ </div>
+
+ <div className="modal-field">
+ <label htmlFor="permission-template-description">{translate('description')}</label>
+ <textarea
+ id="permission-template-description"
+ name="description"
+ onChange={this.handleDescriptionChange}
+ value={this.state.description}
+ />
+ </div>
+
+ <div className="modal-field">
+ <label htmlFor="permission-template-project-key-pattern">
+ {translate('permission_template.key_pattern')}
+ </label>
+ <input
+ id="permission-template-project-key-pattern"
+ maxLength={500}
+ name="projectKeyPattern"
+ onChange={this.handleProjectKeyPatternChange}
+ type="text"
+ value={this.state.projectKeyPattern}
+ />
+ <div className="modal-field-description">
+ {translate('permission_template.key_pattern.description')}
+ </div>
+ </div>
+ </div>
+
+ <footer className="modal-foot">
+ <DeferredSpinner className="spacer-right" loading={submitting} />
+ <button disabled={submitting} id="permission-template-submit" type="submit">
+ {this.props.confirmButtonText}
+ </button>
+ <button
+ className="button-link"
+ disabled={submitting}
+ id="permission-template-cancel"
+ onClick={onCloseClick}
+ type="reset">
+ {translate('cancel')}
+ </button>
+ </footer>
+ </form>
+ )}
+ </SimpleModal>
+ );
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import React from 'react';
-import PropTypes from 'prop-types';
-import CreateView from '../views/CreateView';
-import { translate } from '../../../helpers/l10n';
-import { CallbackType } from '../propTypes';
-
-export default class Header extends React.PureComponent {
- static propTypes = {
- organization: PropTypes.object,
- ready: PropTypes.bool.isRequired,
- refresh: CallbackType
- };
-
- static contextTypes = {
- router: PropTypes.object
- };
-
- componentWillMount() {
- this.handleCreateClick = this.handleCreateClick.bind(this);
- }
-
- handleCreateClick(e) {
- e.preventDefault();
- const { organization } = this.props;
-
- new CreateView({ organization })
- .on('done', r => {
- this.props.refresh().then(() => {
- const pathname = organization
- ? `/organizations/${organization.key}/permission_templates`
- : '/permission_templates';
- this.context.router.push({
- pathname,
- query: { id: r.permissionTemplate.id }
- });
- });
- })
- .render();
- }
-
- render() {
- return (
- <header id="project-permissions-header" className="page-header">
- <h1 className="page-title">{translate('permission_templates.page')}</h1>
-
- {!this.props.ready && <i className="spinner" />}
-
- <div className="page-actions">
- <button onClick={this.handleCreateClick}>{translate('create')}</button>
- </div>
-
- <p className="page-description">{translate('permission_templates.page.description')}</p>
- </header>
- );
- }
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import * as PropTypes from 'prop-types';
+import Form from './Form';
+import { createPermissionTemplate } from '../../../api/permissions';
+import { translate } from '../../../helpers/l10n';
+
+interface Props {
+ organization?: { key: string };
+ ready?: boolean;
+ refresh: () => Promise<void>;
+}
+
+interface State {
+ createModal: boolean;
+}
+
+export default class Header extends React.PureComponent<Props, State> {
+ mounted = false;
+
+ static contextTypes = {
+ router: PropTypes.object
+ };
+
+ state: State = { createModal: false };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleCreateClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ this.setState({ createModal: true });
+ };
+
+ handleCreateModalClose = () => {
+ if (this.mounted) {
+ this.setState({ createModal: false });
+ }
+ };
+
+ handleCreateModalSubmit = (data: {
+ description: string;
+ name: string;
+ projectKeyPattern: string;
+ }) => {
+ const organization = this.props.organization && this.props.organization.key;
+ return createPermissionTemplate({ ...data, organization }).then(response => {
+ this.props.refresh().then(() => {
+ const pathname = organization
+ ? `/organizations/${organization}/permission_templates`
+ : '/permission_templates';
+ this.context.router.push({ pathname, query: { id: response.permissionTemplate.id } });
+ });
+ });
+ };
+
+ render() {
+ return (
+ <header className="page-header" id="project-permissions-header">
+ <h1 className="page-title">{translate('permission_templates.page')}</h1>
+
+ {!this.props.ready && <i className="spinner" />}
+
+ <div className="page-actions">
+ <button onClick={this.handleCreateClick} type="button">
+ {translate('create')}
+ </button>
+
+ {this.state.createModal && (
+ <Form
+ confirmButtonText={translate('create')}
+ header={translate('permission_template.new_template')}
+ onClose={this.handleCreateModalClose}
+ onSubmit={this.handleCreateModalSubmit}
+ />
+ )}
+ </div>
+
+ <p className="page-description">{translate('permission_templates.page.description')}</p>
+ </header>
+ );
+ }
+}
+++ /dev/null
-<form id="delete-permission-template-form">
- <div class="modal-head">
- <h2>{{t 'permission_template.delete_confirm_title'}}</h2>
- </div>
- <div class="modal-body">
- <div class="js-modal-messages"></div>
- {{tp 'permission_template.do_you_want_to_delete_template_xxx' name}}
- </div>
- <div class="modal-foot">
- <button id="delete-permission-template-submit" class="button-red">{{t 'delete'}}</button>
- <a href="#" class="js-modal-close" id="delete-permission-template-cancel">{{t 'cancel'}}</a>
- </div>
-</form>
+++ /dev/null
-<form id="permission-template-form" autocomplete="off">
- <div class="modal-head">
- <h2>{{#if id}}{{t 'permission_template.edit_template'}}{{else}}{{t 'permission_template.new_template'}}{{/if}}</h2>
- </div>
- <div class="modal-body">
- <div class="js-modal-messages"></div>
-
- <div class="modal-field">
- <label for="permission-template-name">{{t 'name'}}<em class="mandatory">*</em></label>
- <input id="permission-template-name" name="name" type="text" maxlength="256" required value="{{name}}">
- <div class="modal-field-description">
- {{t 'should_be_unique'}}
- </div>
- </div>
-
- <div class="modal-field">
- <label for="permission-template-description">{{t 'description'}}</label>
- <textarea id="permission-template-description" name="description" maxlength="4000" rows="5">{{description}}</textarea>
- </div>
-
- <div class="modal-field">
- <label for="permission-template-project-key-pattern">{{t 'permission_template.key_pattern'}}</label>
- <input id="permission-template-project-key-pattern" name="keyPattern" type="text" maxlength="500"
- value="{{projectKeyPattern}}">
- <div class="modal-field-description">
- {{t 'permission_template.key_pattern.description'}}
- </div>
- </div>
- </div>
- <div class="modal-foot">
- <button id="permission-template-submit">{{#if id}}{{t 'update_verb'}}{{else}}{{t 'create'}}{{/if}}</button>
- <a href="#" class="js-modal-close" id="permission-template-cancel">{{t 'cancel'}}</a>
- </div>
-</form>
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import FormView from './FormView';
-import { createPermissionTemplate } from '../../../api/permissions';
-import { parseError } from '../../../helpers/request';
-
-export default FormView.extend({
- sendRequest() {
- this.disableForm();
- const data = {
- name: this.$('#permission-template-name').val(),
- description: this.$('#permission-template-description').val(),
- projectKeyPattern: this.$('#permission-template-project-key-pattern').val()
- };
- if (this.options.organization) {
- Object.assign(data, { organization: this.options.organization.key });
- }
- createPermissionTemplate(data).then(
- r => {
- this.trigger('done', r);
- this.destroy();
- },
- e => {
- this.enableForm();
- parseError(e).then(message => this.showSingleError(message));
- }
- );
- }
-});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import ModalForm from '../../../components/common/modal-form';
-import { deletePermissionTemplate } from '../../../api/permissions';
-import Template from '../templates/permission-templates-delete.hbs';
-import { parseError } from '../../../helpers/request';
-
-export default ModalForm.extend({
- template: Template,
-
- onFormSubmit() {
- ModalForm.prototype.onFormSubmit.apply(this, arguments);
- this.sendRequest();
- },
-
- sendRequest() {
- deletePermissionTemplate({ templateId: this.model.id }).then(
- () => {
- this.trigger('done');
- this.destroy();
- },
- e => {
- this.enableForm();
- parseError(e).then(message => this.showSingleError(message));
- }
- );
- }
-});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import ModalForm from '../../../components/common/modal-form';
-import Template from '../templates/permission-templates-form.hbs';
-
-export default ModalForm.extend({
- template: Template,
-
- onRender() {
- ModalForm.prototype.onRender.apply(this, arguments);
- this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' });
- this.$('#create-custom-measure-metric').select2({
- width: '250px',
- minimumResultsForSearch: 20
- });
- },
-
- onDestroy() {
- ModalForm.prototype.onDestroy.apply(this, arguments);
- this.$('[data-toggle="tooltip"]').tooltip('destroy');
- },
-
- onFormSubmit() {
- ModalForm.prototype.onFormSubmit.apply(this, arguments);
- this.sendRequest();
- }
-});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import FormView from './FormView';
-import { updatePermissionTemplate } from '../../../api/permissions';
-import { parseError } from '../../../helpers/request';
-
-export default FormView.extend({
- sendRequest() {
- this.disableForm();
- updatePermissionTemplate({
- id: this.model.id,
- name: this.$('#permission-template-name').val(),
- description: this.$('#permission-template-description').val(),
- projectKeyPattern: this.$('#permission-template-project-key-pattern').val()
- }).then(
- () => {
- this.options.refresh();
- this.destroy();
- },
- e => {
- this.enableForm();
- parseError(e).then(message => this.showSingleError(message));
- }
- );
- }
-});
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { getPermissionTemplates, applyTemplateToProject } from '../../../../api/permissions';
+import { PermissionTemplate } from '../../../../app/types';
+import DeferredSpinner from '../../../../components/common/DeferredSpinner';
+import SimpleModal from '../../../../components/controls/SimpleModal';
+import Select from '../../../../components/controls/Select';
+import { translateWithParameters, translate } from '../../../../helpers/l10n';
+
+interface Props {
+ onApply?: () => void;
+ onClose: () => void;
+ organization: string | undefined;
+ project: { key: string; name: string };
+}
+
+interface State {
+ done: boolean;
+ loading: boolean;
+ permissionTemplate?: string;
+ permissionTemplates?: PermissionTemplate[];
+}
+
+export default class ApplyTemplate extends React.PureComponent<Props, State> {
+ mounted = false;
+ state: State = { done: false, loading: true };
+
+ componentDidMount() {
+ this.mounted = true;
+ this.fetchPermissionTemplates();
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ fetchPermissionTemplates = () => {
+ getPermissionTemplates(this.props.organization).then(
+ ({ permissionTemplates }) => {
+ if (this.mounted) {
+ this.setState({ loading: false, permissionTemplates });
+ }
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ }
+ );
+ };
+
+ handleSubmit = () => {
+ if (this.state.permissionTemplate) {
+ return applyTemplateToProject({
+ organization: this.props.organization,
+ projectKey: this.props.project.key,
+ templateId: this.state.permissionTemplate
+ }).then(() => {
+ if (this.mounted) {
+ if (this.props.onApply) {
+ this.props.onApply();
+ }
+ this.setState({ done: true });
+ }
+ });
+ } else {
+ return Promise.reject(undefined);
+ }
+ };
+
+ handlePermissionTemplateChange = ({ value }: { value: string }) => {
+ this.setState({ permissionTemplate: value });
+ };
+
+ render() {
+ const header = translateWithParameters(
+ 'projects_role.apply_template_to_xxx',
+ this.props.project.name
+ );
+
+ return (
+ <SimpleModal header={header} onClose={this.props.onClose} onSubmit={this.handleSubmit}>
+ {({ onCloseClick, onFormSubmit, submitting }) => (
+ <form id="project-permissions-apply-template-form" onSubmit={onFormSubmit}>
+ <header className="modal-head">
+ <h2>{header}</h2>
+ </header>
+
+ <div className="modal-body">
+ {this.state.done ? (
+ <div className="alert alert-success">
+ {translate('projects_role.apply_template.success')}
+ </div>
+ ) : (
+ <>
+ {this.state.loading ? (
+ <i className="spinner" />
+ ) : (
+ <div className="modal-field">
+ <label htmlFor="project-permissions-template">
+ {translate('template')}
+ <em className="mandatory">*</em>
+ </label>
+ {this.state.permissionTemplates && (
+ <Select
+ clearable={false}
+ onChange={this.handlePermissionTemplateChange}
+ options={this.state.permissionTemplates.map(permissionTemplate => ({
+ label: permissionTemplate.name,
+ value: permissionTemplate.id
+ }))}
+ value={this.state.permissionTemplate}
+ />
+ )}
+ </div>
+ )}
+ </>
+ )}
+ </div>
+
+ <footer className="modal-foot">
+ <DeferredSpinner className="spacer-right" loading={submitting} />
+ {!this.state.done && (
+ <button disabled={submitting || !this.state.permissionTemplate} type="submit">
+ {translate('apply')}
+ </button>
+ )}
+ <button className="button-link" onClick={onCloseClick} type="reset">
+ {translate(this.state.done ? 'close' : 'cancel')}
+ </button>
+ </footer>
+ </form>
+ )}
+ </SimpleModal>
+ );
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-// @flow
-import React from 'react';
-import { translate } from '../../../../helpers/l10n';
-import ApplyTemplateView from '../views/ApplyTemplateView';
-
-/*::
-type Props = {|
- component: {
- configuration?: {
- canApplyPermissionTemplate: boolean,
- canUpdateProjectVisibilityToPrivate: boolean
- },
- key: string,
- qualifier: string,
- visibility: string
- },
- loadHolders: () => void,
- loading: boolean
-|};
-*/
-
-export default class PageHeader extends React.PureComponent {
- /*:: props: Props; */
-
- handleApplyTemplate = (e /*: Event & { target: HTMLButtonElement } */) => {
- e.preventDefault();
- e.target.blur();
- const { component, loadHolders } = this.props;
- const organization = component.organization ? { key: component.organization } : null;
- new ApplyTemplateView({ project: component, organization })
- .on('done', () => loadHolders())
- .render();
- };
-
- render() {
- const { component } = this.props;
- const configuration = component.configuration;
- const canApplyPermissionTemplate =
- configuration != null && configuration.canApplyPermissionTemplate;
-
- const description = ['VW', 'SVW', 'APP'].includes(component.qualifier)
- ? translate('roles.page.description_portfolio')
- : translate('roles.page.description2');
-
- const visibilityDescription =
- component.qualifier === 'TRK'
- ? translate('visibility', component.visibility, 'description')
- : null;
-
- return (
- <header className="page-header">
- <h1 className="page-title">{translate('permissions.page')}</h1>
-
- {this.props.loading && <i className="spinner" />}
-
- {canApplyPermissionTemplate && (
- <div className="page-actions">
- <button className="js-apply-template" onClick={this.handleApplyTemplate}>
- {translate('projects_role.apply_template')}
- </button>
- </div>
- )}
-
- <div className="page-description">
- <p>{description}</p>
- {visibilityDescription != null && <p>{visibilityDescription}</p>}
- </div>
- </header>
- );
- }
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import ApplyTemplate from './ApplyTemplate';
+import { Component } from '../../../../app/types';
+import { translate } from '../../../../helpers/l10n';
+
+interface Props {
+ component: Component;
+ loadHolders: () => void;
+ loading: boolean;
+}
+
+interface State {
+ applyTemplateModal: boolean;
+}
+
+export default class PageHeader extends React.PureComponent<Props, State> {
+ mounted = false;
+ state: State = { applyTemplateModal: false };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleApplyTemplate = (event: React.SyntheticEvent<HTMLButtonElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ this.setState({ applyTemplateModal: true });
+ };
+
+ handleApplyTemplateClose = () => {
+ if (this.mounted) {
+ this.setState({ applyTemplateModal: false });
+ }
+ };
+
+ render() {
+ const { component } = this.props;
+ const { configuration } = component;
+ const canApplyPermissionTemplate =
+ configuration != null && configuration.canApplyPermissionTemplate;
+
+ const description = ['VW', 'SVW', 'APP'].includes(component.qualifier)
+ ? translate('roles.page.description_portfolio')
+ : translate('roles.page.description2');
+
+ const visibilityDescription =
+ component.qualifier === 'TRK' && component.visibility
+ ? translate('visibility', component.visibility, 'description')
+ : null;
+
+ return (
+ <header className="page-header">
+ <h1 className="page-title">{translate('permissions.page')}</h1>
+
+ {this.props.loading && <i className="spinner" />}
+
+ {canApplyPermissionTemplate && (
+ <div className="page-actions">
+ <button className="js-apply-template" onClick={this.handleApplyTemplate} type="button">
+ {translate('projects_role.apply_template')}
+ </button>
+
+ {this.state.applyTemplateModal && (
+ <ApplyTemplate
+ onApply={this.props.loadHolders}
+ onClose={this.handleApplyTemplateClose}
+ organization={component.organization}
+ project={component}
+ />
+ )}
+ </div>
+ )}
+
+ <div className="page-description">
+ <p>{description}</p>
+ {visibilityDescription != null && <p>{visibilityDescription}</p>}
+ </div>
+ </header>
+ );
+ }
+}
+++ /dev/null
-<form id="project-permissions-apply-template-form" autocomplete="off">
- <div class="modal-head">
- <h2>{{tp 'projects_role.apply_template_to_xxx' project.name}}</h2>
- </div>
-
- <div class="modal-body">
- <div class="js-modal-messages"></div>
-
- {{#if done}}
- <div class="alert alert-success">
- {{t 'projects_role.apply_template.success'}}
- </div>
- {{/if}}
-
- {{#unless done}}
- {{#notNull permissionTemplates}}
- <div class="modal-field">
- <label for="project-permissions-template">
- {{t 'template'}}<em class="mandatory">*</em>
- </label>
- <select id="project-permissions-template">
- {{#each permissionTemplates}}
- <option value="{{id}}">{{name}}</option>
- {{/each}}
- </select>
- </div>
- {{else}}
- <i class="spinner"></i>
- {{/notNull}}
- {{/unless}}
- </div>
-
- <div class="modal-foot">
- {{#unless done}}
- {{#notNull permissionTemplates}}
- <button id="project-permissions-apply-template">{{t 'apply'}}</button>
- {{/notNull}}
- {{/unless}}
- <a href="#" class="js-modal-close">{{t 'close'}}</a>
- </div>
-</form>
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import ModalForm from '../../../../components/common/modal-form';
-import { applyTemplateToProject, getPermissionTemplates } from '../../../../api/permissions';
-import Template from '../templates/ApplyTemplateTemplate.hbs';
-
-export default ModalForm.extend({
- template: Template,
-
- initialize() {
- this.loadPermissionTemplates();
- this.done = false;
- },
-
- loadPermissionTemplates() {
- return getPermissionTemplates(this.options.organization.key).then(r => {
- this.permissionTemplates = r.permissionTemplates;
- this.render();
- });
- },
-
- onRender() {
- ModalForm.prototype.onRender.apply(this, arguments);
- this.$('#project-permissions-template').select2({
- width: '250px',
- minimumResultsForSearch: 20
- });
- },
-
- onFormSubmit() {
- ModalForm.prototype.onFormSubmit.apply(this, arguments);
- const permissionTemplate = this.$('#project-permissions-template').val();
- this.disableForm();
-
- const data = {
- organization: this.options.organization.key,
- projectKey: this.options.project.key,
- templateId: permissionTemplate
- };
- applyTemplateToProject(data)
- .then(() => {
- this.trigger('done');
- this.done = true;
- this.render();
- })
- .catch(function(e) {
- e.response.json().then(r => {
- this.showErrors(r.errors, r.warnings);
- this.enableForm();
- });
- });
- },
-
- serializeData() {
- return {
- permissionTemplates: this.permissionTemplates,
- project: this.options.project,
- done: this.done
- };
- }
-});
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import {
- getPermissionTemplates,
- bulkApplyTemplate,
- PermissionTemplate
-} from '../../api/permissions';
+import { getPermissionTemplates, bulkApplyTemplate } from '../../api/permissions';
+import { PermissionTemplate } from '../../app/types';
import { translate, translateWithParameters } from '../../helpers/l10n';
import AlertWarnIcon from '../../components/icons-components/AlertWarnIcon';
import Modal from '../../components/controls/Modal';
interface Props {
currentUser: { login: string };
- onApplyTemplate: (project: Project) => void;
onProjectCheck: (project: Project, checked: boolean) => void;
+ organization: string | undefined;
project: Project;
selected: boolean;
}
<td className="nowrap">
<Link
- to={{ pathname: '/dashboard', query: { id: project.key } }}
- className="link-with-icon">
+ className="link-with-icon"
+ to={{ pathname: '/dashboard', query: { id: project.key } }}>
<QualifierIcon qualifier={project.qualifier} /> <span>{project.name}</span>
</Link>
</td>
<td className="thin nowrap">
<ProjectRowActions
currentUser={this.props.currentUser}
- onApplyTemplate={this.props.onApplyTemplate}
+ organization={this.props.organization}
project={project}
/>
</td>
import * as React from 'react';
import RestoreAccessModal from './RestoreAccessModal';
import { Project } from './utils';
+import ApplyTemplate from '../permissions/project/components/ApplyTemplate';
import { getComponentShow } from '../../api/components';
import { getComponentNavigation } from '../../api/nav';
import ActionsDropdown, { ActionsDropdownItem } from '../../components/controls/ActionsDropdown';
export interface Props {
currentUser: { login: string };
- onApplyTemplate: (project: Project) => void;
+ organization: string | undefined;
project: Project;
}
interface State {
+ applyTemplateModal: boolean;
hasAccess?: boolean;
loading: boolean;
restoreAccessModal: boolean;
export default class ProjectRowActions extends React.PureComponent<Props, State> {
mounted = false;
- state: State = { loading: false, restoreAccessModal: false };
+ state: State = { applyTemplateModal: false, loading: false, restoreAccessModal: false };
componentDidMount() {
this.mounted = true;
};
handleApplyTemplateClick = () => {
- this.props.onApplyTemplate(this.props.project);
+ this.setState({ applyTemplateModal: true });
+ };
+
+ handleApplyTemplateClose = () => {
+ if (this.mounted) {
+ this.setState({ applyTemplateModal: false });
+ }
};
handleRestoreAccessClick = () => {
project={this.props.project}
/>
)}
+
+ {this.state.applyTemplateModal && (
+ <ApplyTemplate
+ onClose={this.handleApplyTemplateClose}
+ organization={this.props.organization}
+ project={this.props.project}
+ />
+ )}
</ActionsDropdown>
);
}
import * as classNames from 'classnames';
import ProjectRow from './ProjectRow';
import { Project } from './utils';
-import ApplyTemplateView from '../permissions/project/views/ApplyTemplateView';
import { Organization } from '../../app/types';
import { translate } from '../../helpers/l10n';
}
};
- handleApplyTemplate = (project: Project) => {
- new ApplyTemplateView({ project, organization: this.props.organization }).render();
- };
-
render() {
return (
<div className="boxed-group boxed-group-inner">
<ProjectRow
currentUser={this.props.currentUser}
key={project.key}
- onApplyTemplate={this.handleApplyTemplate}
onProjectCheck={this.onProjectCheck}
+ organization={this.props.organization && this.props.organization.key}
project={project}
selected={this.props.selection.includes(project.key)}
/>
});
it('applies permission template', () => {
- const onApplyTemplate = jest.fn();
- const wrapper = shallowRender({ onApplyTemplate });
+ const wrapper = shallowRender();
click(wrapper.find('.js-apply-template'));
- expect(onApplyTemplate).toBeCalledWith(project);
+ expect(wrapper.find('ApplyTemplate')).toMatchSnapshot();
});
function shallowRender(props: Partial<Props> = {}) {
const wrapper = shallow(
<ProjectRowActions
currentUser={{ login: 'admin' }}
- onApplyTemplate={jest.fn()}
+ organization="org"
project={project}
{...props}
/>
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-/* eslint-disable import/first */
-jest.mock('../../permissions/project/views/ApplyTemplateView');
-
import * as React from 'react';
import { shallow } from 'enzyme';
import Projects from '../Projects';
-import ApplyTemplateView from '../../permissions/project/views/ApplyTemplateView';
import { Visibility } from '../../../app/types';
const organization = { key: 'org', name: 'org', projectVisibility: 'public' };
expect(onProjectDeselected).toBeCalledWith('a');
});
-it('opens modal to apply permission template', () => {
- const wrapper = shallowRender({ projects });
- wrapper
- .find('ProjectRow')
- .first()
- .prop<Function>('onApplyTemplate')(projects[0]);
- expect(ApplyTemplateView).toBeCalledWith({ organization, project: projects[0] });
-});
-
function shallowRender(props?: any) {
return shallow(
<Projects
"login": "foo",
}
}
- onApplyTemplate={[MockFunction]}
project={
Object {
"key": "project",
"login": "foo",
}
}
- onApplyTemplate={[MockFunction]}
project={
Object {
"key": "project",
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`applies permission template 1`] = `
+<ApplyTemplate
+ onClose={[Function]}
+ organization="org"
+ project={
+ Object {
+ "id": "",
+ "key": "project",
+ "name": "Project",
+ "organization": "org",
+ "qualifier": "TRK",
+ "visibility": "private",
+ }
+ }
+/>
+`;
+
exports[`restores access 1`] = `
<ActionsDropdown
onToggleClick={[Function]}
}
}
key="a"
- onApplyTemplate={[Function]}
onProjectCheck={[Function]}
+ organization="org"
project={
Object {
"key": "a",
}
}
key="b"
- onApplyTemplate={[Function]}
onProjectCheck={[Function]}
+ organization="org"
project={
Object {
"key": "b",