import { includes } from 'lodash'; import React, { PureComponent } from 'react'; import { connect, ConnectedProps } from 'react-redux'; import { NavModel } from '@grafana/data'; import { featureEnabled } from '@grafana/runtime'; import { Themeable2, withTheme2 } from '@grafana/ui'; import Page from 'app/core/components/Page/Page'; import { UpgradeBox } from 'app/core/components/Upgrade/UpgradeBox'; import config from 'app/core/config'; import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; import { getNavModel } from 'app/core/selectors/navModel'; import { contextSrv } from 'app/core/services/context_srv'; import { AccessControlAction, StoreState } from 'app/types'; import TeamGroupSync, { TeamSyncUpgradeContent } from './TeamGroupSync'; import TeamMembers from './TeamMembers'; import TeamPermissions from './TeamPermissions'; import TeamSettings from './TeamSettings'; import { loadTeam, loadTeamMembers } from './state/actions'; import { getTeamLoadingNav } from './state/navModel'; import { getTeam, getTeamMembers, isSignedInUserTeamAdmin } from './state/selectors'; interface TeamPageRouteParams { id: string; page: string | null; } export interface OwnProps extends GrafanaRouteComponentProps, Themeable2 {} interface State { isSyncEnabled: boolean; isLoading: boolean; } enum PageTypes { Members = 'members', Settings = 'settings', GroupSync = 'groupsync', } function mapStateToProps(state: StoreState, props: OwnProps) { const teamId = parseInt(props.match.params.id, 10); const team = getTeam(state.team, teamId); let defaultPage = 'members'; if (contextSrv.accessControlEnabled()) { // With RBAC the settings page will always be available if (!team || !contextSrv.hasPermissionInMetadata(AccessControlAction.ActionTeamsPermissionsRead, team)) { defaultPage = 'settings'; } } const pageName = props.match.params.page ?? defaultPage; const teamLoadingNav = getTeamLoadingNav(pageName as string); const navModel = getNavModel(state.navIndex, `team-${pageName}-${teamId}`, teamLoadingNav); const members = getTeamMembers(state.team); return { navModel, teamId: teamId, pageName: pageName, team, members, editorsCanAdmin: config.editorsCanAdmin, // this makes the feature toggle mockable/controllable from tests, signedInUser: contextSrv.user, // this makes the feature toggle mockable/controllable from tests, }; } const mapDispatchToProps = { loadTeam, loadTeamMembers, }; const connector = connect(mapStateToProps, mapDispatchToProps); export type Props = OwnProps & ConnectedProps; export class TeamPages extends PureComponent { constructor(props: Props) { super(props); this.state = { isLoading: false, isSyncEnabled: featureEnabled('teamsync'), }; } async componentDidMount() { await this.fetchTeam(); } async fetchTeam() { const { loadTeam, teamId } = this.props; this.setState({ isLoading: true }); const team = await loadTeam(teamId); // With accesscontrol, the TeamPermissions will fetch team members if (!contextSrv.accessControlEnabled()) { await this.props.loadTeamMembers(); } this.setState({ isLoading: false }); return team; } getCurrentPage() { const pages = ['members', 'settings', 'groupsync']; const currentPage = this.props.pageName; return includes(pages, currentPage) ? currentPage : pages[0]; } textsAreEqual = (text1: string, text2: string) => { if (!text1 && !text2) { return true; } if (!text1 || !text2) { return false; } return text1.toLocaleLowerCase() === text2.toLocaleLowerCase(); }; hideTabsFromNonTeamAdmin = (navModel: NavModel, isSignedInUserTeamAdmin: boolean) => { if (contextSrv.accessControlEnabled()) { return navModel; } if (!isSignedInUserTeamAdmin && navModel.main && navModel.main.children) { navModel.main.children .filter((navItem) => !this.textsAreEqual(navItem.text, PageTypes.Members)) .map((navItem) => { navItem.hideFromTabs = true; }); } return navModel; }; renderPage(isSignedInUserTeamAdmin: boolean): React.ReactNode { const { isSyncEnabled } = this.state; const { members, team } = this.props; const currentPage = this.getCurrentPage(); const canReadTeam = contextSrv.hasAccessInMetadata( AccessControlAction.ActionTeamsRead, team!, isSignedInUserTeamAdmin ); const canReadTeamPermissions = contextSrv.hasAccessInMetadata( AccessControlAction.ActionTeamsPermissionsRead, team!, isSignedInUserTeamAdmin ); const canWriteTeamPermissions = contextSrv.hasAccessInMetadata( AccessControlAction.ActionTeamsPermissionsWrite, team!, isSignedInUserTeamAdmin ); switch (currentPage) { case PageTypes.Members: if (contextSrv.accessControlEnabled()) { return ; } else { return ; } case PageTypes.Settings: return canReadTeam && ; case PageTypes.GroupSync: if (isSyncEnabled) { if (canReadTeamPermissions) { return ; } } else if (config.featureToggles.featureHighlights) { return ( <> ); } } return null; } render() { const { team, navModel, members, editorsCanAdmin, signedInUser } = this.props; const isTeamAdmin = isSignedInUserTeamAdmin({ members, editorsCanAdmin, signedInUser }); return ( {team && Object.keys(team).length !== 0 && this.renderPage(isTeamAdmin)} ); } } export default connector(withTheme2(TeamPages));