From 5f7cb8cfbf879aa0b0d43a7b7068688368fda9fc Mon Sep 17 00:00:00 2001
From: Joram Wilander
Date: Wed, 6 Jul 2016 08:23:24 -0400
Subject: PLT-3346/PLT-3342/PLT-3360 EE: Add the ability to restrict channel
management permissions (#3453)
* EE: Add the ability to restrict channel management permissions
* Always allow last user in a channel to delete that channel
---
.../components/admin_console/policy_settings.jsx | 56 +++++++-
webapp/components/channel_header.jsx | 157 +++++++++++++--------
webapp/components/more_channels.jsx | 56 +++++---
webapp/components/navbar_dropdown.jsx | 4 +-
webapp/components/new_channel_modal.jsx | 65 ++++++---
webapp/components/sidebar.jsx | 77 ++++++----
webapp/components/sidebar_right_menu.jsx | 4 +-
.../components/tutorial/tutorial_intro_screens.jsx | 2 +-
8 files changed, 294 insertions(+), 127 deletions(-)
(limited to 'webapp/components')
diff --git a/webapp/components/admin_console/policy_settings.jsx b/webapp/components/admin_console/policy_settings.jsx
index 7fe8e9460..c7031af7b 100644
--- a/webapp/components/admin_console/policy_settings.jsx
+++ b/webapp/components/admin_console/policy_settings.jsx
@@ -21,12 +21,16 @@ export default class PolicySettings extends AdminSettings {
this.renderSettings = this.renderSettings.bind(this);
this.state = Object.assign(this.state, {
- restrictTeamInvite: props.config.TeamSettings.RestrictTeamInvite
+ restrictTeamInvite: props.config.TeamSettings.RestrictTeamInvite,
+ restrictPublicChannelManagement: props.config.TeamSettings.RestrictPublicChannelManagement,
+ restrictPrivateChannelManagement: props.config.TeamSettings.RestrictPrivateChannelManagement
});
}
getConfigFromState(config) {
config.TeamSettings.RestrictTeamInvite = this.state.restrictTeamInvite;
+ config.TeamSettings.RestrictPublicChannelManagement = this.state.restrictPublicChannelManagement;
+ config.TeamSettings.RestrictPrivateChannelManagement = this.state.restrictPrivateChannelManagement;
return config;
}
@@ -48,9 +52,9 @@ export default class PolicySettings extends AdminSettings {
}
/>
+
+ }
+ value={this.state.restrictPublicChannelManagement}
+ onChange={this.handleChange}
+ helpText={
+
+ }
+ />
+
+ }
+ value={this.state.restrictPrivateChannelManagement}
+ onChange={this.handleChange}
+ helpText={
+
+ }
+ />
);
}
diff --git a/webapp/components/channel_header.jsx b/webapp/components/channel_header.jsx
index 3449a0fd6..2b9b1e1cc 100644
--- a/webapp/components/channel_header.jsx
+++ b/webapp/components/channel_header.jsx
@@ -56,6 +56,7 @@ export default class ChannelHeader extends React.Component {
state.showRenameChannelModal = false;
this.state = state;
}
+
getStateFromStores() {
const extraInfo = ChannelStore.getExtraInfo(this.props.channelId);
@@ -67,6 +68,7 @@ export default class ChannelHeader extends React.Component {
currentUser: UserStore.getCurrentUser()
};
}
+
validState() {
if (!this.state.channel ||
!this.state.memberChannel ||
@@ -77,6 +79,7 @@ export default class ChannelHeader extends React.Component {
}
return true;
}
+
componentDidMount() {
ChannelStore.addChangeListener(this.onListenerChange);
ChannelStore.addExtraInfoChangeListener(this.onListenerChange);
@@ -87,6 +90,7 @@ export default class ChannelHeader extends React.Component {
$('.sidebar--left .dropdown-menu').perfectScrollbar();
document.addEventListener('keydown', this.openRecentMentions);
}
+
componentWillUnmount() {
ChannelStore.removeChangeListener(this.onListenerChange);
ChannelStore.removeExtraInfoChangeListener(this.onListenerChange);
@@ -96,6 +100,7 @@ export default class ChannelHeader extends React.Component {
UserStore.removeStatusesChangeListener(this.onListenerChange);
document.removeEventListener('keydown', this.openRecentMentions);
}
+
onListenerChange() {
const newState = this.getStateFromStores();
if (!Utils.areObjectsEqual(newState, this.state)) {
@@ -103,6 +108,7 @@ export default class ChannelHeader extends React.Component {
}
$('.channel-header__info .description').popover({placement: 'bottom', trigger: 'hover', html: true, delay: {show: 500, hide: 500}});
}
+
handleLeave() {
Client.leaveChannel(this.state.channel.id,
() => {
@@ -119,6 +125,7 @@ export default class ChannelHeader extends React.Component {
}
);
}
+
searchMentions(e) {
e.preventDefault();
@@ -146,12 +153,14 @@ export default class ChannelHeader extends React.Component {
is_mention_search: true
});
}
+
openRecentMentions(e) {
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.keyCode === Constants.KeyCodes.M) {
e.preventDefault();
this.searchMentions(e);
}
}
+
showRenameChannelModal(e) {
e.preventDefault();
@@ -159,6 +168,7 @@ export default class ChannelHeader extends React.Component {
showRenameChannelModal: true
});
}
+
hideRenameChannelModal() {
this.setState({
showRenameChannelModal: false
@@ -179,6 +189,30 @@ export default class ChannelHeader extends React.Component {
return null;
}
+ showManagementOptions(channel, isAdmin, isSystemAdmin) {
+ if (global.window.mm_license.IsLicensed !== 'true') {
+ return true;
+ }
+
+ if (channel.type === Constants.OPEN_CHANNEL) {
+ if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) {
+ return false;
+ }
+ if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) {
+ return false;
+ }
+ } else if (channel.type === Constants.PRIVATE_CHANNEL) {
+ if (global.window.mm_config.RestrictPrivateChannelManagement === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) {
+ return false;
+ }
+ if (global.window.mm_config.RestrictPrivateChannelManagement === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
render() {
if (!this.validState()) {
return null;
@@ -210,7 +244,8 @@ export default class ChannelHeader extends React.Component {
);
let channelTitle = channel.display_name;
const currentId = this.state.currentUser.id;
- const isAdmin = Utils.isAdmin(this.state.memberChannel.roles) || TeamStore.isTeamAdminForCurrentTeam() || UserStore.isSystemAdminForCurrentUser();
+ const isAdmin = TeamStore.isTeamAdminForCurrentTeam() || UserStore.isSystemAdminForCurrentUser();
+ const isSystemAdmin = UserStore.isSystemAdminForCurrentUser();
const isDirect = (this.state.channel.type === 'D');
if (isDirect) {
@@ -331,67 +366,90 @@ export default class ChannelHeader extends React.Component {
dropdownContents.push(
);
- dropdownContents.push(
+
+ const deleteOption = (
- this.setState({showEditChannelPurposeModal: true})}
+ dialogType={DeleteChannelModal}
+ dialogProps={{channel}}
>
-
-
- );
- dropdownContents.push(
-
-
-
);
- if (isAdmin) {
+ if (this.showManagementOptions(channel, isAdmin, isSystemAdmin)) {
+ dropdownContents.push(
+
+
+
+
+
+ );
+
+ dropdownContents.push(
+
+ this.setState({showEditChannelPurposeModal: true})}
+ >
+
+
+
+ );
+
dropdownContents.push(
-
-
-
-
- );
+ dropdownContents.push(deleteOption);
}
+ } else if (this.state.userCount === 1) {
+ dropdownContents.push(deleteOption);
}
const canLeave = channel.type === Constants.PRIVATE_CHANNEL ? this.state.userCount > 1 : true;
diff --git a/webapp/components/more_channels.jsx b/webapp/components/more_channels.jsx
index 54a06d0ae..b7ffff712 100644
--- a/webapp/components/more_channels.jsx
+++ b/webapp/components/more_channels.jsx
@@ -6,8 +6,11 @@ import LoadingScreen from './loading_screen.jsx';
import NewChannelFlow from './new_channel_flow.jsx';
import ChannelStore from 'stores/channel_store.jsx';
+import UserStore from 'stores/user_store.jsx';
+import TeamStore from 'stores/team_store.jsx';
import * as Utils from 'utils/utils.jsx';
+import Constants from 'utils/constants.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
@@ -132,6 +135,41 @@ export default class MoreChannels extends React.Component {
serverError = ;
}
+ let createNewChannelButton = (
+
+ );
+
+ let createChannelHelpText = (
+
+
+
+ );
+
+ const isAdmin = TeamStore.isTeamAdminForCurrentTeam() || UserStore.isSystemAdminForCurrentUser();
+ const isSystemAdmin = UserStore.isSystemAdminForCurrentUser();
+
+ if (global.window.mm_license.IsLicensed === 'true') {
+ if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) {
+ createNewChannelButton = null;
+ createChannelHelpText = null;
+ } else if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) {
+ createNewChannelButton = null;
+ createChannelHelpText = null;
+ }
+ }
+
var moreChannels;
if (this.state.channels != null) {
@@ -153,12 +191,7 @@ export default class MoreChannels extends React.Component {
defaultMessage='No more channels to join'
/>
-
-
-
+ {createChannelHelpText}
);
}
@@ -195,16 +228,7 @@ export default class MoreChannels extends React.Component {
defaultMessage='More Channels'
/>
-
+ {createNewChannelButton}
{this.props.serverError}
;
}
+ let createPublicChannelLink = (
+
+
+
+ );
+
+ let createPrivateChannelLink = (
+
+
+
+ );
+
+ const isAdmin = TeamStore.isTeamAdminForCurrentTeam() || UserStore.isSystemAdminForCurrentUser();
+ const isSystemAdmin = UserStore.isSystemAdminForCurrentUser();
+
+ if (global.window.mm_license.IsLicensed === 'true') {
+ if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) {
+ createPublicChannelLink = null;
+ } else if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) {
+ createPublicChannelLink = null;
+ }
+
+ if (global.window.mm_config.RestrictPrivateChannelManagement === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) {
+ createPrivateChannelLink = null;
+ } else if (global.window.mm_config.RestrictPrivateChannelManagement === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) {
+ createPrivateChannelLink = null;
+ }
+ }
+
var channelTerm = '';
var channelSwitchText = '';
switch (this.props.channelType) {
@@ -129,15 +174,7 @@ class NewChannelModal extends React.Component {
id='channel_modal.privateGroup1'
defaultMessage='Create a new private group with restricted membership. '
/>
-
-
-
+ {createPublicChannelLink}
);
break;
@@ -154,15 +191,7 @@ class NewChannelModal extends React.Component {
id='channel_modal.publicChannel2'
defaultMessage='Create a new public channel anyone can join. '
/>
-
-
-
+ {createPrivateChannelLink}
);
break;
diff --git a/webapp/components/sidebar.jsx b/webapp/components/sidebar.jsx
index 4f678274d..fdcae1dff 100644
--- a/webapp/components/sidebar.jsx
+++ b/webapp/components/sidebar.jsx
@@ -682,6 +682,55 @@ export default class Sidebar extends React.Component {
/>
);
+ const isAdmin = TeamStore.isTeamAdminForCurrentTeam() || UserStore.isSystemAdminForCurrentUser();
+ const isSystemAdmin = UserStore.isSystemAdminForCurrentUser();
+
+ let createPublicChannelIcon = (
+
+
+ {'+'}
+
+
+ );
+
+ let createPrivateChannelIcon = (
+
+
+ {'+'}
+
+
+ );
+
+ if (global.window.mm_license.IsLicensed === 'true') {
+ if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) {
+ createPublicChannelIcon = null;
+ } else if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) {
+ createPublicChannelIcon = null;
+ }
+
+ if (global.window.mm_config.RestrictPrivateChannelManagement === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) {
+ createPrivateChannelIcon = null;
+ } else if (global.window.mm_config.RestrictPrivateChannelManagement === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) {
+ createPrivateChannelIcon = null;
+ }
+ }
+
return (
-
-
- {'+'}
-
-
+ {createPublicChannelIcon}
{publicChannelItems}
@@ -765,19 +802,7 @@ export default class Sidebar extends React.Component {
id='sidebar.pg'
defaultMessage='Private Groups'
/>
-
-
- {'+'}
-
-
+ {createPrivateChannelIcon}
{privateChannelItems}
diff --git a/webapp/components/sidebar_right_menu.jsx b/webapp/components/sidebar_right_menu.jsx
index 2cf758f00..25136e8bc 100644
--- a/webapp/components/sidebar_right_menu.jsx
+++ b/webapp/components/sidebar_right_menu.jsx
@@ -186,10 +186,10 @@ export default class SidebarRightMenu extends React.Component {
}
if (global.window.mm_license.IsLicensed === 'true') {
- if (global.window.mm_config.RestrictTeamInvite === Constants.TEAM_INVITE_SYSTEM_ADMIN && !isSystemAdmin) {
+ if (global.window.mm_config.RestrictTeamInvite === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) {
teamLink = null;
inviteLink = null;
- } else if (global.window.mm_config.RestrictTeamInvite === Constants.TEAM_INVITE_TEAM_ADMIN && !isAdmin) {
+ } else if (global.window.mm_config.RestrictTeamInvite === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) {
teamLink = null;
inviteLink = null;
}
diff --git a/webapp/components/tutorial/tutorial_intro_screens.jsx b/webapp/components/tutorial/tutorial_intro_screens.jsx
index b0d831d96..639fa07b2 100644
--- a/webapp/components/tutorial/tutorial_intro_screens.jsx
+++ b/webapp/components/tutorial/tutorial_intro_screens.jsx
@@ -108,7 +108,7 @@ export default class TutorialIntroScreens extends React.Component {
let inviteModalLink;
let inviteText;
- if (global.window.mm_license.IsLicensed !== 'true' || global.window.mm_config.RestrictTeamInvite === Constants.TEAM_INVITE_ALL) {
+ if (global.window.mm_license.IsLicensed !== 'true' || global.window.mm_config.RestrictTeamInvite === Constants.PERMISSIONS_ALL) {
if (team.type === Constants.INVITE_TEAM) {
inviteModalLink = (