Commit d01eb033 authored by George Goldberg's avatar George Goldberg
Browse files

Merge branch 'advanced-permissions-phase-2'

parents ec3e625a 221bc126
......@@ -24,7 +24,6 @@ import OAuthSettings from 'components/admin_console/oauth_settings.jsx';
import PasswordSettings from 'components/admin_console/password_settings.jsx';
import PluginManagement from 'components/admin_console/plugin_management';
import CustomPluginSettings from 'components/admin_console/custom_plugin_settings';
import PolicySettings from 'components/admin_console/policy_settings';
import CustomIntegrationSettings from 'components/admin_console/custom_integrations_settings';
import UsersAndTeamsSettings from 'components/admin_console/users_and_teams_settings';
......@@ -208,11 +207,6 @@ export default class AdminConsole extends React.Component {
schema: AdminDefinition.settings.general.privacy.schema,
}}
/>
<SCRoute
path={`${props.match.url}/policy`}
component={PolicySettings}
extraProps={extraProps}
/>
<SCRoute
path={`${props.match.url}/compliance`}
component={SchemaAdminSettings}
......@@ -233,6 +227,45 @@ export default class AdminConsole extends React.Component {
</Switch>
)}
/>
<Route
path={`${this.props.match.url}/permissions`}
render={(props) => (
<Switch>
<SCRoute
path={`${props.match.url}/schemes`}
component={SchemaAdminSettings}
extraProps={{
...extraProps,
schema: AdminDefinition.settings.permissions.schemes.schema,
}}
/>
<SCRoute
path={`${props.match.url}/system-scheme`}
component={SchemaAdminSettings}
extraProps={{
...extraProps,
schema: AdminDefinition.settings.permissions.systemScheme.schema,
}}
/>
<SCRoute
path={`${props.match.url}/team-override-scheme/:scheme_id`}
component={SchemaAdminSettings}
extraProps={{
...extraProps,
schema: AdminDefinition.settings.permissions.teamScheme.schema,
}}
/>
<SCRoute
path={`${props.match.url}/team-override-scheme`}
component={SchemaAdminSettings}
extraProps={{
...extraProps,
schema: AdminDefinition.settings.permissions.teamScheme.schema,
}}
/>
</Switch>
)}
/>
<Route
path={`${this.props.match.url}/authentication`}
render={(props) => (
......@@ -425,6 +458,14 @@ export default class AdminConsole extends React.Component {
component={CustomBrandSettings}
extraProps={extraProps}
/>
<SCRoute
path={`${props.match.url}/announcement`}
component={SchemaAdminSettings}
extraProps={{
...extraProps,
schema: AdminDefinition.settings.customization.announcement.schema,
}}
/>
<SCRoute
path={`${props.match.url}/emoji`}
component={CustomEmojiSettings}
......
......@@ -13,6 +13,9 @@ import SystemUsers from './system_users';
import ServerLogs from './server_logs';
import Audits from './audits';
import LicenseSettings from './license_settings';
import PermissionSchemesSettings from './permission_schemes_settings';
import PermissionSystemSchemeSettings from './permission_schemes_settings/permission_system_scheme_settings';
import PermissionTeamSchemeSettings from './permission_schemes_settings/permission_team_scheme_settings';
import * as DefinitionConstants from './admin_definition_constants';
......@@ -58,6 +61,8 @@ const FILE_STORAGE_DRIVER_S3 = 'amazons3';
//
// Number Widget (extends from Setting Widget)
//
// Color Widget (extends from Setting Widget)
//
// Text Widget (extends from Setting Widget)
// - placeholder (and placeholder_default): Placeholder text to show in the input.
//
......@@ -529,6 +534,26 @@ export default {
},
},
},
permissions: {
schemes: {
schema: {
id: 'PermissionSchemes',
component: PermissionSchemesSettings,
},
},
systemScheme: {
schema: {
id: 'PermissionSystemScheme',
component: PermissionSystemSchemeSettings,
},
},
teamScheme: {
schema: {
id: 'PermissionSystemScheme',
component: PermissionTeamSchemeSettings,
},
},
},
authentication: {
email: {
schema: {
......@@ -1413,6 +1438,55 @@ export default {
},
},
customization: {
announcement: {
schema: {
id: 'AnnouncementSettings',
name: 'admin.customization.announcement',
name_default: 'Announcement Banner',
settings: [
{
type: Constants.SettingsTypes.TYPE_BOOL,
key: 'AnnouncementSettings.EnableBanner',
label: 'admin.customization.announcement.enableBannerTitle',
label_default: 'Enable Announcement Banner:',
help_text: 'admin.customization.announcement.enableBannerDesc',
help_text_default: 'Enable an announcement banner across all teams.',
},
{
type: Constants.SettingsTypes.TYPE_TEXT,
key: 'AnnouncementSettings.BannerText',
label: 'admin.customization.announcement.bannerTextTitle',
label_default: 'Banner Text:',
help_text: 'admin.customization.announcement.bannerTextDesc',
help_text_default: 'Text that will appear in the announcement banner.',
needs: [['EnableBanner', true]],
},
{
type: Constants.SettingsTypes.TYPE_COLOR,
key: 'AnnouncementSettings.BannerColor',
label: 'admin.customization.announcement.bannerColorTitle',
label_default: 'Banner Color:',
needs: [['EnableBanner', true]],
},
{
type: Constants.SettingsTypes.TYPE_COLOR,
key: 'AnnouncementSettings.BannerTextColor',
label: 'admin.customization.announcement.bannerTextColorTitle',
label_default: 'Banner Text Color:',
needs: [['EnableBanner', true]],
},
{
type: Constants.SettingsTypes.TYPE_BOOL,
key: 'AnnouncementSettings.AllowBannerDismissal',
label: 'admin.customization.announcement.allowBannerDismissalTitle',
label_default: 'Allow Banner Dismissal:',
help_text: 'admin.customization.announcement.allowBannerDismissalDesc',
help_text_default: 'When true, users can dismiss the banner until its next update. When false, the banner is permanently visible until it is turned off by the System Admin.',
needs: [['EnableBanner', true]],
},
],
},
},
link_previews: {
schema: {
id: 'LinkPreviewsSettings',
......
......@@ -3,11 +3,13 @@
import PropTypes from 'prop-types';
import React from 'react';
import {Overlay, Tooltip} from 'react-bootstrap';
import {saveConfig} from 'actions/admin_actions.jsx';
import {localizeMessage} from 'utils/utils.jsx';
import SaveButton from 'components/save_button.jsx';
import FormError from 'components/form_error.jsx';
import Constants from 'utils/constants.jsx';
export default class AdminSettings extends React.Component {
static propTypes = {
......@@ -35,9 +37,20 @@ export default class AdminSettings extends React.Component {
saveNeeded: false,
saving: false,
serverError: null,
errorTooltip: false,
});
}
closeTooltip = () => {
this.setState({errorTooltip: false});
}
openTooltip = (e) => {
const elm = e.currentTarget.querySelector('.control-label');
const isElipsis = elm.offsetWidth < elm.scrollWidth;
this.setState({errorTooltip: isElipsis});
}
handleChange = (id, value) => {
this.setState({
saveNeeded: true,
......@@ -173,18 +186,31 @@ export default class AdminSettings extends React.Component {
onSubmit={this.handleSubmit}
>
{this.renderSettings()}
<div className='form-group'>
<FormError error={this.state.serverError}/>
</div>
<div className='form-group'>
<div className='col-sm-12'>
<SaveButton
saving={this.state.saving}
disabled={!this.state.saveNeeded || (this.canSave && !this.canSave())}
onClick={this.handleSubmit}
savingMessage={localizeMessage('admin.saving', 'Saving Config...')}
/>
<div className='admin-console-save'>
<SaveButton
saving={this.state.saving}
disabled={!this.state.saveNeeded || (this.canSave && !this.canSave())}
onClick={this.handleSubmit}
savingMessage={localizeMessage('admin.saving', 'Saving Config...')}
/>
<div
className='error-message'
ref='errorMessage'
onMouseOver={this.openTooltip}
onMouseOut={this.closeTooltip}
>
<FormError error={this.state.serverError}/>
</div>
<Overlay
show={this.state.errorTooltip}
delayShow={Constants.OVERLAY_TIME_DELAY}
placement='top'
target={this.refs.errorMessage}
>
<Tooltip id='error-tooltip' >
{this.state.serverError}
</Tooltip>
</Overlay>
</div>
</form>
</div>
......
......@@ -81,7 +81,7 @@ export default class AdminSidebar extends React.Component {
let license = null;
let audits = null;
let policy = null;
let announcement = null;
if (this.props.buildEnterpriseReady) {
license = (
......@@ -207,14 +207,13 @@ export default class AdminSidebar extends React.Component {
}
/>
);
policy = (
announcement = (
<AdminSidebarSection
name='policy'
name='announcement'
title={
<FormattedMessage
id='admin.sidebar.policy'
defaultMessage='Policy'
id='admin.sidebar.announcement'
defaultMessage='Announcement Banner'
/>
}
/>
......@@ -475,7 +474,6 @@ export default class AdminSidebar extends React.Component {
/>
}
/>
{policy}
<AdminSidebarSection
name='privacy'
title={
......@@ -496,6 +494,38 @@ export default class AdminSidebar extends React.Component {
}
/>
</AdminSidebarSection>
{this.props.license.IsLicensed === 'true' &&
<AdminSidebarSection
name='permissions'
type='text'
title={
<FormattedMessage
id='admin.sidebar.permissions'
defaultMessage='Permissions'
/>
}
>
{this.props.license.CustomPermissionsSchemes !== 'true' &&
<AdminSidebarSection
name='system-scheme'
title={
<FormattedMessage
id='admin.sidebar.system-scheme'
defaultMessage='System scheme'
/>
}
/>}
{this.props.license.CustomPermissionsSchemes === 'true' &&
<AdminSidebarSection
name='schemes'
title={
<FormattedMessage
id='admin.sidebar.schemes'
defaultMessage='Permission Schemes'
/>
}
/>}
</AdminSidebarSection>}
<AdminSidebarSection
name='authentication'
type='text'
......@@ -698,6 +728,7 @@ export default class AdminSidebar extends React.Component {
}
>
{customBranding}
{announcement}
<AdminSidebarSection
name='emoji'
title={
......
......@@ -71,7 +71,7 @@ export default class Audits extends React.PureComponent {
}
return (
<div>
<div className='wrapper--admin'>
<ComplianceReports/>
<div className='panel audit-panel'>
......
......@@ -64,8 +64,9 @@ export default class ColorSetting extends React.PureComponent {
togglePicker = () => {
if (this.props.disabled) {
this.setState({showPicker: false});
} else {
this.setState({showPicker: !this.state.showPicker});
}
this.setState({showPicker: !this.state.showPicker});
}
closePicker = (e) => {
......
......@@ -4,11 +4,8 @@
import React from 'react';
import {FormattedMessage} from 'react-intl';
import * as Utils from 'utils/utils.jsx';
import AdminSettings from './admin_settings.jsx';
import BooleanSetting from './boolean_setting.jsx';
import DropdownSetting from './dropdown_setting.jsx';
import SettingsGroup from './settings_group.jsx';
export default class CustomEmojiSettings extends AdminSettings {
......@@ -24,10 +21,6 @@ export default class CustomEmojiSettings extends AdminSettings {
config.ServiceSettings.EnableCustomEmoji = this.state.enableCustomEmoji;
config.ServiceSettings.EnableEmojiPicker = this.state.enableEmojiPicker;
if (this.props.license.IsLicensed === 'true') {
config.ServiceSettings.RestrictCustomEmojiCreation = this.state.restrictCustomEmojiCreation;
}
return config;
}
......@@ -35,7 +28,6 @@ export default class CustomEmojiSettings extends AdminSettings {
return {
enableCustomEmoji: config.ServiceSettings.EnableCustomEmoji,
enableEmojiPicker: config.ServiceSettings.EnableEmojiPicker,
restrictCustomEmojiCreation: config.ServiceSettings.RestrictCustomEmojiCreation,
};
}
......@@ -49,36 +41,6 @@ export default class CustomEmojiSettings extends AdminSettings {
}
renderSettings() {
let restrictSetting = null;
if (this.props.license.IsLicensed === 'true') {
restrictSetting = (
<DropdownSetting
id='restrictCustomEmojiCreation'
values={[
{value: 'all', text: Utils.localizeMessage('admin.customization.restrictCustomEmojiCreationAll', 'Allow everyone to create custom emoji')},
{value: 'admin', text: Utils.localizeMessage('admin.customization.restrictCustomEmojiCreationAdmin', 'Allow System and Team Admins to create custom emoji')},
{value: 'system_admin', text: Utils.localizeMessage('admin.customization.restrictCustomEmojiCreationSystemAdmin', 'Only allow System Admins to create custom emoji')},
]}
label={
<FormattedMessage
id='admin.customization.restrictCustomEmojiCreationTitle'
defaultMessage='Restrict Custom Emoji Creation:'
/>
}
helpText={
<FormattedMessage
id='admin.customization.restrictCustomEmojiCreationDesc'
defaultMessage='Restrict the creation of custom emoji to certain users.'
/>
}
value={this.state.restrictCustomEmojiCreation}
onChange={this.handleChange}
disabled={!this.state.enableCustomEmoji}
setByEnv={this.isSetByEnv('ServiceSettings.RestrictCustomEmojiCreation')}
/>
);
}
return (
<SettingsGroup>
<BooleanSetting
......@@ -117,7 +79,6 @@ export default class CustomEmojiSettings extends AdminSettings {
onChange={this.handleChange}
setByEnv={this.isSetByEnv('ServiceSettings.EnableCustomEmoji')}
/>
{restrictSetting}
</SettingsGroup>
);
}
......
......@@ -303,24 +303,25 @@ export class WebhookSettings extends AdminSettings {
onChange={this.handleChange}
setByEnv={this.isSetByEnv('ServiceSettings.EnableOAuthServiceProvider')}
/>
<BooleanSetting
id='enableOnlyAdminIntegrations'
label={
<FormattedMessage
id='admin.service.integrationAdmin'
defaultMessage='Restrict managing integrations to Admins:'
/>
}
helpText={
<FormattedMessage
id='admin.service.integrationAdminDesc'
defaultMessage='When true, webhooks and slash commands can only be created, edited and viewed by Team and System Admins, and OAuth 2.0 applications by System Admins. Integrations are available to all users after they have been created by the Admin.'
/>
}
value={this.state.enableOnlyAdminIntegrations}
onChange={this.handleChange}
setByEnv={false}
/>
{this.props.license.IsLicensed === 'false' &&
<BooleanSetting
id='enableOnlyAdminIntegrations'
label={
<FormattedMessage
id='admin.service.integrationAdmin'
defaultMessage='Restrict managing integrations to Admins:'
/>
}
helpText={
<FormattedMessage
id='admin.service.integrationAdminDesc'
defaultMessage='When true, webhooks and slash commands can only be created, edited and viewed by Team and System Admins, and OAuth 2.0 applications by System Admins. Integrations are available to all users after they have been created by the Admin.'
/>
}
value={this.state.enableOnlyAdminIntegrations}
onChange={this.handleChange}
setByEnv={false}
/>}
<BooleanSetting
id='enablePostUsernameOverride'
label={
......
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import PropTypes from 'prop-types';
import {FormattedMessage} from 'react-intl';
import {Constants} from 'utils/constants';
export default class EditPostTimeLimitButton extends React.Component {
static propTypes = {
timeLimit: PropTypes.number.isRequired,
onClick: PropTypes.func,
};
render = () => {
let messageID;
if (this.props.timeLimit === Constants.UNSET_POST_EDIT_TIME_LIMIT) {
messageID = 'edit_post.time_limit_button.no_limit';
} else {
messageID = 'edit_post.time_limit_button.for_n_seconds';
}
return (
<button
className='edit-post-time-limit-button'
onClick={this.props.onClick}
>
<FormattedMessage
id={messageID}
values={{n: this.props.timeLimit}}
/>
</button>
);
};
}
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {connect} from 'react-redux';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import EditPostTimeLimitButton from './edit_post_time_limit_button';
function mapStateToProps(state, ownProps) {
const {PostEditTimeLimit} = getConfig(state);
return {
...ownProps,
timeLimit: parseInt(PostEditTimeLimit, 10),
};
}
export default connect(mapStateToProps)(EditPostTimeLimitButton);
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import PropTypes from 'prop-types';
import {FormattedMessage} from 'react-intl';
import {Modal} from 'react-bootstrap';
import {Constants} from 'utils/constants';
import {localizeMessage} from 'utils/utils.jsx';
const INT32_MAX = 2147483647;
export default class EditPostTimeLimitModal extends React.Component {
static propTypes = {
config: PropTypes.object.isRequired,
show: PropTypes.bool,
onClose: PropTypes.func.isRequired,
actions: PropTypes.shape({
updateConfig: PropTypes.func.isRequired,
getConfig: PropTypes.func.isRequired,
}).isRequired,
};
constructor(props) {
super(props);
this.state = {
postEditTimeLimit: parseInt(props.config.ServiceSettings.PostEditTimeLimit, 10),
saving: false,
errorMessage: '',
};
}
UNSAFE_componentWillMount() { // eslint-disable-line camelcase
this.props.actions.getConfig();
}
save = async () => {
this.setState({saving: true, errorMessage: ''});