import { Paging } from '../app/types';
import throwGlobalError from '../app/utils/throwGlobalError';
+export interface ProfileActions {
+ copy?: boolean;
+ edit?: boolean;
+ setAsDefault?: boolean;
+}
+
+export interface Actions {
+ create?: boolean;
+}
+
export interface Profile {
+ actions?: ProfileActions;
key: string;
name: string;
language: string;
qualityProfile?: string;
}
+export interface SearchQualityProfilesResponse {
+ actions?: Actions;
+ profiles: Profile[];
+}
+
export function searchQualityProfiles(
parameters: SearchQualityProfilesParameters
-): Promise<Profile[]> {
- return getJSON('/api/qualityprofiles/search', parameters).then(r => r.profiles);
+): Promise<SearchQualityProfilesResponse> {
+ return getJSON('/api/qualityprofiles/search', parameters);
}
export function getQualityProfile(data: {
const { component } = this.props;
const organization = this.props.customOrganizations ? component.organization : undefined;
Promise.all([
- searchQualityProfiles({ organization }),
- searchQualityProfiles({ organization, project: component.key })
+ searchQualityProfiles({ organization }).then(r => r.profiles),
+ searchQualityProfiles({ organization, project: component.key }).then(r => r.profiles)
]).then(
([allProfiles, profiles]) => {
if (this.mounted) {
jest.mock('../../../api/quality-profiles', () => ({
associateProject: jest.fn(() => Promise.resolve()),
dissociateProject: jest.fn(() => Promise.resolve()),
- searchQualityProfiles: jest.fn()
+ searchQualityProfiles: jest.fn(() => Promise.resolve())
}));
jest.mock('../../../app/utils/addGlobalSuccessMessage', () => ({
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { searchQualityProfiles, getExporters } from '../../../api/quality-profiles';
+import { searchQualityProfiles, getExporters, Actions } from '../../../api/quality-profiles';
import { sortProfiles } from '../utils';
import { translate } from '../../../helpers/l10n';
import OrganizationHelmet from '../../../components/common/OrganizationHelmet';
interface Props {
children: React.ReactElement<any>;
- currentUser: { permissions: { global: Array<string> } };
languages: Array<{}>;
onRequestFail: (reasong: any) => void;
- organization: { name: string; canAdmin?: boolean; key: string } | null;
+ organization: { name: string; key: string } | null;
}
interface State {
+ actions?: Actions;
loading: boolean;
exporters?: Exporter[];
profiles?: Profile[];
this.setState({ loading: true });
Promise.all([getExporters(), this.fetchProfiles()]).then(responses => {
if (this.mounted) {
- const [exporters, profiles] = responses;
+ const [exporters, profilesResponse] = responses;
this.setState({
+ actions: profilesResponse.actions,
exporters,
- profiles: sortProfiles(profiles),
+ profiles: sortProfiles(profilesResponse.profiles),
loading: false
});
}
}
updateProfiles = () => {
- return this.fetchProfiles().then((profiles: any) => {
+ return this.fetchProfiles().then(r => {
if (this.mounted) {
- this.setState({ profiles: sortProfiles(profiles) });
+ this.setState({ profiles: sortProfiles(r.profiles) });
}
});
};
const { organization } = this.props;
const finalLanguages = Object.values(this.props.languages);
- const canAdmin = organization
- ? organization.canAdmin
- : this.props.currentUser.permissions.global.includes('profileadmin');
-
return React.cloneElement(this.props.children, {
+ actions: this.state.actions || {},
profiles: this.state.profiles,
languages: finalLanguages,
exporters: this.state.exporters,
updateProfiles: this.updateProfiles,
onRequestFail: this.props.onRequestFail,
- organization: organization ? organization.key : null,
- canAdmin
+ organization: organization ? organization.key : null
});
}
import { Profile } from '../types';
interface Props {
- canAdmin: boolean;
fromList?: boolean;
onRequestFail: (reasong: any) => void;
organization: string | null;
}
export default class ProfileActions extends React.PureComponent<Props, State> {
- static defaultProps = {
- fromList: false
- };
-
static contextTypes = {
router: PropTypes.object
};
};
render() {
- const { profile, canAdmin } = this.props;
+ const { profile } = this.props;
+ const { actions = {} } = profile;
// FIXME use org, name and lang
const backupUrl =
return (
<ul className="dropdown-menu dropdown-menu-right">
- {canAdmin &&
+ {actions.edit &&
!profile.isBuiltIn && (
<li>
<Link to={activateMoreUrl}>{translate('quality_profiles.activate_more_rules')}</Link>
{translate('compare')}
</Link>
</li>
- {canAdmin && (
+ {actions.copy && (
<li>
<a id="quality-profile-copy" href="#" onClick={this.handleCopyClick}>
{translate('copy')}
</a>
</li>
)}
- {canAdmin &&
+ {actions.edit &&
!profile.isBuiltIn && (
<li>
<a id="quality-profile-rename" href="#" onClick={this.handleRenameClick}>
</a>
</li>
)}
- {canAdmin &&
+ {actions.setAsDefault &&
!profile.isDefault && (
<li>
<a id="quality-profile-set-as-default" href="#" onClick={this.handleSetDefaultClick}>
</a>
</li>
)}
- {canAdmin &&
+ {actions.edit &&
!profile.isDefault &&
!profile.isBuiltIn && (
<li>
import { Profile } from '../types';
interface Props {
- canAdmin: boolean;
children: React.ReactElement<any>;
location: {
pathname: string;
<div id="quality-profile">
<Helmet title={profile.name} />
<ProfileHeader
- canAdmin={this.props.canAdmin}
onRequestFail={this.props.onRequestFail}
organization={organization}
profile={profile}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 { shallow } from 'enzyme';
+import ProfileActions from '../ProfileActions';
+
+const PROFILE = {
+ activeRuleCount: 68,
+ activeDeprecatedRuleCount: 0,
+ childrenCount: 0,
+ depth: 0,
+ isBuiltIn: false,
+ isDefault: false,
+ isInherited: false,
+ key: 'foo',
+ language: 'java',
+ languageName: 'Java',
+ name: 'Foo',
+ organization: 'org',
+ rulesUpdatedAt: '2017-06-28T12:58:44+0000'
+};
+
+it('renders with no permissions', () => {
+ expect(
+ shallow(
+ <ProfileActions
+ onRequestFail={jest.fn()}
+ organization="org"
+ profile={PROFILE}
+ updateProfiles={jest.fn()}
+ />
+ )
+ ).toMatchSnapshot();
+});
+
+it('renders with permission to edit', () => {
+ expect(
+ shallow(
+ <ProfileActions
+ onRequestFail={jest.fn()}
+ organization="org"
+ profile={{ ...PROFILE, actions: { edit: true } }}
+ updateProfiles={jest.fn()}
+ />
+ )
+ ).toMatchSnapshot();
+});
+
+it('renders with all permissions', () => {
+ expect(
+ shallow(
+ <ProfileActions
+ onRequestFail={jest.fn()}
+ organization="org"
+ profile={{ ...PROFILE, actions: { copy: true, edit: true, setAsDefault: true } }}
+ updateProfiles={jest.fn()}
+ />
+ )
+ ).toMatchSnapshot();
+});
const updateProfiles = jest.fn();
const output = shallow(
<ProfileContainer
- canAdmin={false}
location={{ pathname: '', query: { language: 'js', name: 'fake' } }}
onRequestFail={jest.fn()}
organization={null}
const header = output.find(ProfileHeader);
expect(header.length).toBe(1);
expect(header.prop('profile')).toBe(targetProfile);
- expect(header.prop('canAdmin')).toBe(false);
expect(header.prop('updateProfiles')).toBe(updateProfiles);
});
];
const output = shallow(
<ProfileContainer
- canAdmin={false}
location={{ pathname: '', query: { language: 'js', name: 'random' } }}
onRequestFail={jest.fn()}
organization={null}
const updateProfiles = jest.fn();
const output = shallow(
<ProfileContainer
- canAdmin={false}
location={{ pathname: '', query: { language: 'js', name: 'First Profile' } }}
onRequestFail={jest.fn()}
organization={null}
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders with all permissions 1`] = `
+<ul
+ className="dropdown-menu dropdown-menu-right"
+>
+ <li>
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/organizations/org/rules#qprofile=foo|activation=false"
+ >
+ quality_profiles.activate_more_rules
+ </Link>
+ </li>
+ <li>
+ <a
+ href="/api/qualityprofiles/backup?profileKey=foo"
+ id="quality-profile-backup"
+ >
+ backup_verb
+ </a>
+ </li>
+ <li>
+ <Link
+ id="quality-profile-compare"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/organizations/org/quality_profiles/compare",
+ "query": Object {
+ "language": "java",
+ "name": "Foo",
+ },
+ }
+ }
+ >
+ compare
+ </Link>
+ </li>
+ <li>
+ <a
+ href="#"
+ id="quality-profile-copy"
+ onClick={[Function]}
+ >
+ copy
+ </a>
+ </li>
+ <li>
+ <a
+ href="#"
+ id="quality-profile-rename"
+ onClick={[Function]}
+ >
+ rename
+ </a>
+ </li>
+ <li>
+ <a
+ href="#"
+ id="quality-profile-set-as-default"
+ onClick={[Function]}
+ >
+ set_as_default
+ </a>
+ </li>
+ <li>
+ <a
+ href="#"
+ id="quality-profile-delete"
+ onClick={[Function]}
+ >
+ delete
+ </a>
+ </li>
+</ul>
+`;
+
+exports[`renders with no permissions 1`] = `
+<ul
+ className="dropdown-menu dropdown-menu-right"
+>
+ <li>
+ <a
+ href="/api/qualityprofiles/backup?profileKey=foo"
+ id="quality-profile-backup"
+ >
+ backup_verb
+ </a>
+ </li>
+ <li>
+ <Link
+ id="quality-profile-compare"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/organizations/org/quality_profiles/compare",
+ "query": Object {
+ "language": "java",
+ "name": "Foo",
+ },
+ }
+ }
+ >
+ compare
+ </Link>
+ </li>
+</ul>
+`;
+
+exports[`renders with permission to edit 1`] = `
+<ul
+ className="dropdown-menu dropdown-menu-right"
+>
+ <li>
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/organizations/org/rules#qprofile=foo|activation=false"
+ >
+ quality_profiles.activate_more_rules
+ </Link>
+ </li>
+ <li>
+ <a
+ href="/api/qualityprofiles/backup?profileKey=foo"
+ id="quality-profile-backup"
+ >
+ backup_verb
+ </a>
+ </li>
+ <li>
+ <Link
+ id="quality-profile-compare"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/organizations/org/quality_profiles/compare",
+ "query": Object {
+ "language": "java",
+ "name": "Foo",
+ },
+ }
+ }
+ >
+ compare
+ </Link>
+ </li>
+ <li>
+ <a
+ href="#"
+ id="quality-profile-rename"
+ onClick={[Function]}
+ >
+ rename
+ </a>
+ </li>
+ <li>
+ <a
+ href="#"
+ id="quality-profile-delete"
+ onClick={[Function]}
+ >
+ delete
+ </a>
+ </li>
+</ul>
+`;
import { Exporter, Profile } from '../types';
interface Props {
- canAdmin: boolean;
exporters: Exporter[];
onRequestFail: (reasong: any) => void;
organization: string | null;
}
export default function ProfileDetails(props: Props) {
+ const { profile } = props;
return (
<div>
<div className="quality-profile-grid">
<div className="quality-profile-grid-left">
<ProfileRules {...props} />
<ProfileExporters {...props} />
- {props.canAdmin &&
- !props.profile.isBuiltIn && (
- <ProfilePermissions
- organization={props.organization || undefined}
- profile={props.profile}
- />
+ {profile.actions &&
+ profile.actions.edit &&
+ !profile.isBuiltIn && (
+ <ProfilePermissions organization={props.organization || undefined} profile={profile} />
)}
</div>
<div className="quality-profile-grid-right">
import { Profile } from '../types';
interface Props {
- canAdmin: boolean;
onRequestFail: (reasong: any) => void;
profile: Profile;
organization: string | null;
{translate('actions')} <i className="icon-dropdown" />
</button>
<ProfileActions
- canAdmin={this.props.canAdmin}
onRequestFail={this.props.onRequestFail}
organization={organization}
profile={profile}
import { Profile } from '../types';
interface Props {
- canAdmin: boolean;
onRequestFail: (reason: any) => void;
organization: string | null;
profile: Profile;
return (
<div className="boxed-group quality-profile-inheritance">
- {this.props.canAdmin &&
- !this.props.profile.isBuiltIn && (
+ {profile.actions &&
+ profile.actions.edit &&
+ !profile.isBuiltIn && (
<div className="boxed-group-actions">
<button className="pull-right js-change-parent" onClick={this.handleChangeParentClick}>
{translate('quality_profiles.change_parent')}
import { Profile } from '../types';
interface Props {
- canAdmin: boolean;
organization: string | null;
profile: Profile;
updateProfiles: () => Promise<void>;
}
render() {
+ const { profile } = this.props;
return (
<div className="boxed-group quality-profile-projects">
- {this.props.canAdmin &&
- !this.props.profile.isDefault && (
+ {profile.actions &&
+ profile.actions.edit &&
+ !profile.isDefault && (
<div className="boxed-group-actions">
<button className="js-change-projects" onClick={this.handleChangeClick}>
{translate('quality_profiles.change_projects')}
<div className="boxed-group-inner">
{this.state.loading ? (
<i className="spinner" />
- ) : this.props.profile.isDefault ? (
+ ) : profile.isDefault ? (
this.renderDefault()
) : (
this.renderProjects()
<ChangeProjectsForm
onClose={this.closeForm}
organization={this.props.organization}
- profile={this.props.profile}
+ profile={profile}
/>
)}
</div>
const TYPES = ['BUG', 'VULNERABILITY', 'CODE_SMELL'];
interface Props {
- canAdmin: boolean;
organization: string | null;
profile: Profile;
}
</tbody>
</table>
- {this.props.canAdmin &&
+ {profile.actions &&
+ profile.actions.edit &&
!profile.isBuiltIn && (
<div className="text-right big-spacer-top">
<Link to={activateMoreUrl} className="button js-activate-rules">
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 { shallow } from 'enzyme';
+import ProfileDetails from '../ProfileDetails';
+import { Profile } from '../../types';
+
+it('renders without permissions', () => {
+ expect(
+ shallow(
+ <ProfileDetails
+ exporters={[]}
+ onRequestFail={jest.fn()}
+ organization="org"
+ profile={{} as Profile}
+ profiles={[]}
+ updateProfiles={jest.fn()}
+ />
+ )
+ ).toMatchSnapshot();
+});
+
+it('renders with edit permission', () => {
+ expect(
+ shallow(
+ <ProfileDetails
+ exporters={[]}
+ onRequestFail={jest.fn()}
+ organization="org"
+ profile={{ actions: { edit: true } } as Profile}
+ profiles={[]}
+ updateProfiles={jest.fn()}
+ />
+ )
+ ).toMatchSnapshot();
+});
rulesUpdatedAt: '2017-06-28T12:58:44+0000'
};
+const EDITABLE_PROFILE = { ...PROFILE, actions: { edit: true } };
+
const apiResponseAll = {
total: 243,
facets: [
});
it('should render the quality profiles rules with sonarway comparison', () => {
- const wrapper = shallow(<ProfileRules canAdmin={false} organization="foo" profile={PROFILE} />);
+ const wrapper = shallow(<ProfileRules organization="foo" profile={PROFILE} />);
const instance = wrapper.instance() as any;
instance.mounted = true;
instance.loadRules();
});
it('should show a button to activate more rules for admins', () => {
- const wrapper = shallow(<ProfileRules canAdmin={true} organization="foo" profile={PROFILE} />);
+ const wrapper = shallow(<ProfileRules organization="foo" profile={EDITABLE_PROFILE} />);
expect(wrapper.find('.js-activate-rules')).toMatchSnapshot();
});
it('should show a deprecated rules warning message', () => {
const wrapper = shallow(
<ProfileRules
- canAdmin={true}
organization="foo"
- profile={{ ...PROFILE, activeDeprecatedRuleCount: 8 }}
+ profile={{ ...EDITABLE_PROFILE, activeDeprecatedRuleCount: 8 }}
/>
);
expect(wrapper.find('ProfileRulesDeprecatedWarning')).toMatchSnapshot();
it('should not show a button to activate more rules on built in profiles', () => {
const wrapper = shallow(
- <ProfileRules canAdmin={true} organization={null} profile={{ ...PROFILE, isBuiltIn: true }} />
- );
- expect(wrapper.find('.js-activate-rules')).toHaveLength(0);
-});
-
-it('should not show a button to activate more rules on built in profiles', () => {
- const wrapper = shallow(
- <ProfileRules canAdmin={true} organization={null} profile={{ ...PROFILE, isBuiltIn: true }} />
+ <ProfileRules organization={null} profile={{ ...EDITABLE_PROFILE, isBuiltIn: true }} />
);
expect(wrapper.find('.js-activate-rules')).toHaveLength(0);
});
it('should not show sonarway comparison for built in profiles', () => {
(apiQP as any).getQualityProfile = jest.fn(() => Promise.resolve());
const wrapper = shallow(
- <ProfileRules canAdmin={true} organization={null} profile={{ ...PROFILE, isBuiltIn: true }} />
+ <ProfileRules organization={null} profile={{ ...PROFILE, isBuiltIn: true }} />
);
const instance = wrapper.instance() as any;
instance.mounted = true;
}
})
);
- const wrapper = shallow(<ProfileRules canAdmin={true} organization={null} profile={PROFILE} />);
+ const wrapper = shallow(<ProfileRules organization={null} profile={PROFILE} />);
const instance = wrapper.instance() as any;
instance.mounted = true;
instance.loadRules();
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders with edit permission 1`] = `
+<div>
+ <div
+ className="quality-profile-grid"
+ >
+ <div
+ className="quality-profile-grid-left"
+ >
+ <ProfileRules
+ exporters={Array []}
+ onRequestFail={[Function]}
+ organization="org"
+ profile={
+ Object {
+ "actions": Object {
+ "edit": true,
+ },
+ }
+ }
+ profiles={Array []}
+ updateProfiles={[Function]}
+ />
+ <ProfileExporters
+ exporters={Array []}
+ onRequestFail={[Function]}
+ organization="org"
+ profile={
+ Object {
+ "actions": Object {
+ "edit": true,
+ },
+ }
+ }
+ profiles={Array []}
+ updateProfiles={[Function]}
+ />
+ <ProfilePermissions
+ organization="org"
+ profile={
+ Object {
+ "actions": Object {
+ "edit": true,
+ },
+ }
+ }
+ />
+ </div>
+ <div
+ className="quality-profile-grid-right"
+ >
+ <ProfileInheritance
+ exporters={Array []}
+ onRequestFail={[Function]}
+ organization="org"
+ profile={
+ Object {
+ "actions": Object {
+ "edit": true,
+ },
+ }
+ }
+ profiles={Array []}
+ updateProfiles={[Function]}
+ />
+ <ProfileProjects
+ exporters={Array []}
+ onRequestFail={[Function]}
+ organization="org"
+ profile={
+ Object {
+ "actions": Object {
+ "edit": true,
+ },
+ }
+ }
+ profiles={Array []}
+ updateProfiles={[Function]}
+ />
+ </div>
+ </div>
+</div>
+`;
+
+exports[`renders without permissions 1`] = `
+<div>
+ <div
+ className="quality-profile-grid"
+ >
+ <div
+ className="quality-profile-grid-left"
+ >
+ <ProfileRules
+ exporters={Array []}
+ onRequestFail={[Function]}
+ organization="org"
+ profile={Object {}}
+ profiles={Array []}
+ updateProfiles={[Function]}
+ />
+ <ProfileExporters
+ exporters={Array []}
+ onRequestFail={[Function]}
+ organization="org"
+ profile={Object {}}
+ profiles={Array []}
+ updateProfiles={[Function]}
+ />
+ </div>
+ <div
+ className="quality-profile-grid-right"
+ >
+ <ProfileInheritance
+ exporters={Array []}
+ onRequestFail={[Function]}
+ organization="org"
+ profile={Object {}}
+ profiles={Array []}
+ updateProfiles={[Function]}
+ />
+ <ProfileProjects
+ exporters={Array []}
+ onRequestFail={[Function]}
+ organization="org"
+ profile={Object {}}
+ profiles={Array []}
+ updateProfiles={[Function]}
+ />
+ </div>
+ </div>
+</div>
+`;
import Evolution from './Evolution';
import ProfilesList from './ProfilesList';
import { Profile } from '../types';
+import { Actions } from '../../../api/quality-profiles';
interface Props {
- canAdmin: boolean;
+ actions: Actions;
languages: Array<{ key: string; name: string }>;
location: { query: { [p: string]: string } };
onRequestFail: (reason: any) => void;
import { getProfilePath } from '../utils';
import { translate } from '../../../helpers/l10n';
import { Profile } from '../types';
+import { Actions } from '../../../api/quality-profiles';
interface Props {
- canAdmin: boolean;
+ actions: Actions;
languages: Array<{ key: string; name: string }>;
onRequestFail: (reason: any) => void;
organization: string | null;
<header className="page-header">
<h1 className="page-title">{translate('quality_profiles.page')}</h1>
- {this.props.canAdmin && (
+ {this.props.actions.create && (
<div className="page-actions button-group dropdown">
<button id="quality-profiles-create" onClick={this.handleCreateClick}>
{translate('create')}
import { Profile } from '../types';
interface Props {
- canAdmin: boolean;
languages: Array<{ key: string; name: string }>;
location: { query: { [p: string]: string } };
onRequestFail: (reason: any) => void;
renderProfiles(profiles: Profile[]) {
return profiles.map(profile => (
<ProfilesListRow
- canAdmin={this.props.canAdmin}
key={profile.key}
onRequestFail={this.props.onRequestFail}
organization={this.props.organization}
<th className="text-right nowrap">{translate('quality_profiles.list.rules')}</th>
<th className="text-right nowrap">{translate('quality_profiles.list.updated')}</th>
<th className="text-right nowrap">{translate('quality_profiles.list.used')}</th>
- {this.props.canAdmin && <th> </th>}
+ <th> </th>
</tr>
</thead>
);
import Tooltip from '../../../components/controls/Tooltip';
interface Props {
- canAdmin: boolean;
onRequestFail: (reason: any) => void;
organization: string | null;
profile: Profile;
<td className="quality-profiles-table-date thin nowrap text-right">
{this.renderUsageDate()}
</td>
- {this.props.canAdmin && (
- <td className="quality-profiles-table-actions thin nowrap text-right">
- <div className="dropdown">
- <button className="dropdown-toggle" data-toggle="dropdown">
- <i className="icon-dropdown" />
- </button>
- <ProfileActions
- canAdmin={this.props.canAdmin}
- fromList={true}
- onRequestFail={this.props.onRequestFail}
- organization={this.props.organization}
- profile={this.props.profile}
- updateProfiles={this.props.updateProfiles}
- />
- </div>
- </td>
- )}
+ <td className="quality-profiles-table-actions thin nowrap text-right">
+ <div className="dropdown">
+ <button className="dropdown-toggle" data-toggle="dropdown">
+ <i className="icon-dropdown" />
+ </button>
+ <ProfileActions
+ fromList={true}
+ onRequestFail={this.props.onRequestFail}
+ organization={this.props.organization}
+ profile={this.props.profile}
+ updateProfiles={this.props.updateProfiles}
+ />
+ </div>
+ </td>
</tr>
);
}