Skip to content

Commit

Permalink
PLT-3346/PLT-3342/PLT-3360 EE: Add the ability to restrict channel ma…
Browse files Browse the repository at this point in the history
…nagement permissions (mattermost#3453)

* EE: Add the ability to restrict channel management permissions

* Always allow last user in a channel to delete that channel
  • Loading branch information
jwilander committed Jul 6, 2016
1 parent 691645c commit 38121d7
Show file tree
Hide file tree
Showing 12 changed files with 350 additions and 136 deletions.
56 changes: 52 additions & 4 deletions components/admin_console/policy_settings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -48,9 +52,9 @@ export default class PolicySettings extends AdminSettings {
<DropdownSetting
id='restrictTeamInvite'
values={[
{value: Constants.TEAM_INVITE_ALL, text: Utils.localizeMessage('admin.general.policy.teamInviteAll', 'All team members')},
{value: Constants.TEAM_INVITE_TEAM_ADMIN, text: Utils.localizeMessage('admin.general.policy.teamInviteAdmin', 'Team and System Admins')},
{value: Constants.TEAM_INVITE_SYSTEM_ADMIN, text: Utils.localizeMessage('admin.general.policy.teamInviteSystemAdmin', 'System Admins')}
{value: Constants.PERMISSIONS_ALL, text: Utils.localizeMessage('admin.general.policy.permissionsAll', 'All team members')},
{value: Constants.PERMISSIONS_TEAM_ADMIN, text: Utils.localizeMessage('admin.general.policy.permissionsAdmin', 'Team and System Admins')},
{value: Constants.PERMISSIONS_SYSTEM_ADMIN, text: Utils.localizeMessage('admin.general.policy.permissionsSystemAdmin', 'System Admins')}
]}
label={
<FormattedMessage
Expand All @@ -67,6 +71,50 @@ export default class PolicySettings extends AdminSettings {
/>
}
/>
<DropdownSetting
id='restrictPublicChannelManagement'
values={[
{value: Constants.PERMISSIONS_ALL, text: Utils.localizeMessage('admin.general.policy.permissionsAll', 'All team members')},
{value: Constants.PERMISSIONS_TEAM_ADMIN, text: Utils.localizeMessage('admin.general.policy.permissionsAdmin', 'Team and System Admins')},
{value: Constants.PERMISSIONS_SYSTEM_ADMIN, text: Utils.localizeMessage('admin.general.policy.permissionsSystemAdmin', 'System Admins')}
]}
label={
<FormattedMessage
id='admin.general.policy.restrictPublicChannelManagementTitle'
defaultMessage='Enable public channel management permissions for:'
/>
}
value={this.state.restrictPublicChannelManagement}
onChange={this.handleChange}
helpText={
<FormattedHTMLMessage
id='admin.general.policy.restrictPublicChannelManagementDescription'
defaultMessage='Selecting "All team members" allows any team members to create, delete, rename, and set the header or purpose for public channels.<br/><br/>Selecting "Team and System Admins" restricts channel management permissions for public channels to Team and System Admins, including creating, deleting, renaming, and setting the channel header or purpose.<br/><br/>Selecting "System Admins" restricts channel management permissions for public channels to System Admins, including creating, deleting, renaming, and setting the channel header or purpose.'
/>
}
/>
<DropdownSetting
id='restrictPrivateChannelManagement'
values={[
{value: Constants.PERMISSIONS_ALL, text: Utils.localizeMessage('admin.general.policy.permissionsAll', 'All team members')},
{value: Constants.PERMISSIONS_TEAM_ADMIN, text: Utils.localizeMessage('admin.general.policy.permissionsAdmin', 'Team and System Admins')},
{value: Constants.PERMISSIONS_SYSTEM_ADMIN, text: Utils.localizeMessage('admin.general.policy.permissionsSystemAdmin', 'System Admins')}
]}
label={
<FormattedMessage
id='admin.general.policy.restrictPrivateChannelManagementTitle'
defaultMessage='Enable private group management permissions for:'
/>
}
value={this.state.restrictPrivateChannelManagement}
onChange={this.handleChange}
helpText={
<FormattedHTMLMessage
id='admin.general.policy.restrictPrivateChannelManagementDescription'
defaultMessage='Selecting "All team members" allows any team members to create, delete, rename, and set the header or purpose for private groups.<br/><br/>Selecting "Team and System Admins" restricts group management permissions for private groups to Team and System Admins, including creating, deleting, renaming, and setting the group header or purpose.<br/><br/>Selecting "System Admins" restricts group management permissions for private groups to System Admins, including creating, deleting, renaming, and setting the group header or purpose.'
/>
}
/>
</SettingsGroup>
);
}
Expand Down
157 changes: 99 additions & 58 deletions components/channel_header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -67,6 +68,7 @@ export default class ChannelHeader extends React.Component {
currentUser: UserStore.getCurrentUser()
};
}

validState() {
if (!this.state.channel ||
!this.state.memberChannel ||
Expand All @@ -77,6 +79,7 @@ export default class ChannelHeader extends React.Component {
}
return true;
}

componentDidMount() {
ChannelStore.addChangeListener(this.onListenerChange);
ChannelStore.addExtraInfoChangeListener(this.onListenerChange);
Expand All @@ -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);
Expand All @@ -96,13 +100,15 @@ 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)) {
this.setState(newState);
}
$('.channel-header__info .description').popover({placement: 'bottom', trigger: 'hover', html: true, delay: {show: 500, hide: 500}});
}

handleLeave() {
Client.leaveChannel(this.state.channel.id,
() => {
Expand All @@ -119,6 +125,7 @@ export default class ChannelHeader extends React.Component {
}
);
}

searchMentions(e) {
e.preventDefault();

Expand Down Expand Up @@ -146,19 +153,22 @@ 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();

this.setState({
showRenameChannelModal: true
});
}

hideRenameChannelModal() {
this.setState({
showRenameChannelModal: false
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -331,67 +366,90 @@ export default class ChannelHeader extends React.Component {

dropdownContents.push(
<li
key='set_channel_header'
key='notification_preferences'
role='presentation'
>
<ToggleModalButton
role='menuitem'
dialogType={EditChannelHeaderModal}
dialogProps={{channel}}
dialogType={ChannelNotificationsModal}
dialogProps={{
channel,
channelMember: this.state.memberChannel,
currentUser: this.state.currentUser
}}
>
<FormattedMessage
id='channel_header.setHeader'
defaultMessage='Set {term} Header...'
values={{
term: (channelTerm)
}}
id='channel_header.notificationPreferences'
defaultMessage='Notification Preferences'
/>
</ToggleModalButton>
</li>
);
dropdownContents.push(

const deleteOption = (
<li
key='set_channel_purpose'
key='delete_channel'
role='presentation'
>
<a
<ToggleModalButton
role='menuitem'
href='#'
onClick={() => this.setState({showEditChannelPurposeModal: true})}
dialogType={DeleteChannelModal}
dialogProps={{channel}}
>
<FormattedMessage
id='channel_header.setPurpose'
defaultMessage='Set {term} Purpose...'
id='channel_header.delete'
defaultMessage='Delete {term}...'
values={{
term: (channelTerm)
}}
/>
</a>
</li>
);
dropdownContents.push(
<li
key='notification_preferences'
role='presentation'
>
<ToggleModalButton
role='menuitem'
dialogType={ChannelNotificationsModal}
dialogProps={{
channel,
channelMember: this.state.memberChannel,
currentUser: this.state.currentUser
}}
>
<FormattedMessage
id='channel_header.notificationPreferences'
defaultMessage='Notification Preferences'
/>
</ToggleModalButton>
</li>
);

if (isAdmin) {
if (this.showManagementOptions(channel, isAdmin, isSystemAdmin)) {
dropdownContents.push(
<li
key='set_channel_header'
role='presentation'
>
<ToggleModalButton
role='menuitem'
dialogType={EditChannelHeaderModal}
dialogProps={{channel}}
>
<FormattedMessage
id='channel_header.setHeader'
defaultMessage='Set {term} Header...'
values={{
term: (channelTerm)
}}
/>
</ToggleModalButton>
</li>
);

dropdownContents.push(
<li
key='set_channel_purpose'
role='presentation'
>
<a
role='menuitem'
href='#'
onClick={() => this.setState({showEditChannelPurposeModal: true})}
>
<FormattedMessage
id='channel_header.setPurpose'
defaultMessage='Set {term} Purpose...'
values={{
term: (channelTerm)
}}
/>
</a>
</li>
);

dropdownContents.push(
<li
key='rename_channel'
Expand All @@ -414,27 +472,10 @@ export default class ChannelHeader extends React.Component {
);

if (!ChannelStore.isDefault(channel)) {
dropdownContents.push(
<li
key='delete_channel'
role='presentation'
>
<ToggleModalButton
role='menuitem'
dialogType={DeleteChannelModal}
dialogProps={{channel}}
>
<FormattedMessage
id='channel_header.delete'
defaultMessage='Delete {term}...'
values={{
term: (channelTerm)
}}
/>
</ToggleModalButton>
</li>
);
dropdownContents.push(deleteOption);
}
} else if (this.state.userCount === 1) {
dropdownContents.push(deleteOption);
}

const canLeave = channel.type === Constants.PRIVATE_CHANNEL ? this.state.userCount > 1 : true;
Expand Down
Loading

0 comments on commit 38121d7

Please sign in to comment.