Commit 8e3dd95f authored by Chetanya Kandhari's avatar Chetanya Kandhari Committed by Jesse Hallam
Browse files

MM-12400: Custom terms of service feature: add Re-Acceptance interval settings...

MM-12400: Custom terms of service feature: add Re-Acceptance interval settings and remove header and footer (#1971)

* MM-12400: Custom terms of service feature Phase 2
- Remove Footer text and title
- Add reacceptance interval setting in the Admin Console
- Update names as used in the redux PR
- Remove redux actions to use BindActionCreators
- Update styles
- Update tests
- Update reacceptance flag

* MM-12413 (#3)

* Remove fixed footer
* Finish footer CSS changes
* MM-12411 Update help texts
* MM-12415 Remove Beta Labels
* Fix check-style

* Update terms of service page background colors
Add colons in Admin Console settings

* MM-12414 Remove redux hack

* Fix check-style

* Update styles

* Update tests

* Fix css height

* review feedback
parent a31d34d5
......@@ -470,24 +470,6 @@ export async function resendVerification(email, success, error) {
}
}
export async function updateTermsOfServiceStatus(termsOfServiceId, accepted, success, error) {
const {data, error: err} = await UserActions.updateMyTermsOfServiceStatus(termsOfServiceId, accepted)(dispatch, getState);
if (data && success) {
success(data);
} else if (err && error) {
error({id: err.server_error_id, ...err});
}
}
export async function getTermsOfService(success, error) {
const {data, error: err} = await UserActions.getTermsOfService()(dispatch, getState);
if (data && success) {
success(data);
} else if (err && error) {
error({id: err.server_error_id, ...err});
}
}
export function getAuthorizedApps(success, error) {
Client4.getAuthorizedOAuthApps(getState().entities.users.currentUserId).then(
(authorizedApps) => {
......
......@@ -17,6 +17,7 @@ import ElasticsearchSettings from 'components/admin_console/elasticsearch_settin
import EmailSettings from 'components/admin_console/email_settings.jsx';
import MessageExportSettings from 'components/admin_console/message_export_settings';
import PasswordSettings from 'components/admin_console/password_settings.jsx';
import CustomTermsOfServiceSettings from 'components/admin_console/custom_terms_of_service_settings';
import SchemaAdminSettings from 'components/admin_console/schema_admin_settings';
import PushSettings from 'components/admin_console/push_settings.jsx';
......@@ -483,6 +484,11 @@ export default class AdminConsole extends React.Component {
schema: AdminDefinition.settings.customization.legal_and_support.schema,
}}
/>
<SCRoute
path={`${props.match.url}/custom_terms_of_service`}
component={CustomTermsOfServiceSettings}
extraProps={extraProps}
/>
<SCRoute
path={`${props.match.url}/native_app_links`}
component={SchemaAdminSettings}
......
......@@ -2646,26 +2646,6 @@ export default {
help_text: t('admin.support.emailHelp'),
help_text_default: 'Email address displayed on email notifications and during tutorial for end users to ask support questions.',
},
{
type: Constants.SettingsTypes.TYPE_BOOL,
key: 'SupportSettings.CustomTermsOfServiceEnabled',
label: 'admin.support.enableTermsOfServiceTitle',
label_default: 'Enable Custom Terms of Service (Beta)',
help_text: 'admin.support.termsOfServiceHelp',
help_text_default: 'When true, new users must accept the terms of service before accessing any Mattermost teams on desktop, web or mobile. Existing users must accept them after login or a page refresh.\n \nUsers on mobile do not have to accept the terms of service with mobile support scheduled for an upcoming release.',
help_text_markdown: true,
isHidden: needsUtils.not(needsUtils.hasLicenseFeature('CustomTermsOfService')),
},
{
type: Constants.SettingsTypes.TYPE_LONG_TEXT,
key: 'SupportSettings.CustomTermsOfServiceText',
label: 'admin.support.termsOfServiceTextTitle',
label_default: 'Custom Terms of Service Text (Beta)',
isDisabled: needsUtils.stateValueFalse('SupportSettings.CustomTermsOfServiceEnabled'),
isHidden: needsUtils.not(needsUtils.hasLicenseFeature('CustomTermsOfService')),
help_text: 'admin.support.termsOfServiceTextHelp',
help_text_default: 'Text that will appear in your custom Terms of Service. Supports Markdown-formatted text.',
},
],
},
},
......
......@@ -75,6 +75,7 @@ export default class AdminSidebar extends React.Component {
let clusterSettings = null;
let metricsSettings = null;
let complianceSettings = null;
let customTermsOfServiceSettings = null;
let mfaSettings = null;
let messageExportSettings = null;
let complianceSection = null;
......@@ -168,6 +169,20 @@ export default class AdminSidebar extends React.Component {
);
}
if (this.props.license.CustomTermsOfService === 'true') {
customTermsOfServiceSettings = (
<AdminSidebarSection
name='custom_terms_of_service'
title={
<FormattedMessage
id='admin.sidebar.customTermsOfService'
defaultMessage='Custom Terms of Service'
/>
}
/>
);
}
if (this.props.license.MFA === 'true') {
mfaSettings = (
<AdminSidebarSection
......@@ -739,6 +754,7 @@ export default class AdminSidebar extends React.Component {
/>
}
/>
{customTermsOfServiceSettings}
<AdminSidebarSection
name='native_app_links'
title={
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/admin_console/CustomTermsOfServiceSettings should match snapshot 1`] = `
<div
className="wrapper--fixed"
>
<AdminHeader>
<FormattedMessage
defaultMessage="Custom Terms of Service"
id="admin.support.termsOfServiceTitle"
values={Object {}}
/>
</AdminHeader>
<form
className="form-horizontal"
onSubmit={[Function]}
role="form"
>
<LoadingScreen
position="relative"
style={Object {}}
/>
<div
className="admin-console-save"
>
<SaveButton
btnClass="btn-primary"
defaultMessage="Save"
disabled={true}
extraClasses=""
onClick={[Function]}
saving={false}
savingMessage="Saving Config..."
/>
<div
className="error-message"
onMouseOut={[Function]}
onMouseOver={[Function]}
>
<FormError
error={null}
errors={Array []}
/>
</div>
<Overlay
animation={[Function]}
delayShow={400}
placement="top"
rootClose={false}
show={false}
>
<Tooltip
bsClass="tooltip"
id="error-tooltip"
placement="right"
/>
</Overlay>
</div>
</form>
</div>
`;
exports[`components/admin_console/CustomTermsOfServiceSettings should match snapshot 2`] = `
<div
className="wrapper--fixed"
>
<AdminHeader>
<FormattedMessage
defaultMessage="Custom Terms of Service"
id="admin.support.termsOfServiceTitle"
values={Object {}}
/>
</AdminHeader>
<form
className="form-horizontal"
onSubmit={[Function]}
role="form"
>
<LoadingScreen
position="relative"
style={Object {}}
/>
<div
className="admin-console-save"
>
<SaveButton
btnClass="btn-primary"
defaultMessage="Save"
disabled={true}
extraClasses=""
onClick={[Function]}
saving={false}
savingMessage="Saving Config..."
/>
<div
className="error-message"
onMouseOut={[Function]}
onMouseOver={[Function]}
>
<FormError
error={null}
errors={Array []}
/>
</div>
<Overlay
animation={[Function]}
delayShow={400}
placement="top"
rootClose={false}
show={false}
>
<Tooltip
bsClass="tooltip"
id="error-tooltip"
placement="right"
/>
</Overlay>
</div>
</form>
</div>
`;
exports[`components/admin_console/CustomTermsOfServiceSettings should match snapshot 3`] = `
<div
className="wrapper--fixed"
>
<AdminHeader>
<FormattedMessage
defaultMessage="Custom Terms of Service"
id="admin.support.termsOfServiceTitle"
values={Object {}}
/>
</AdminHeader>
<form
className="form-horizontal"
onSubmit={[Function]}
role="form"
>
<LoadingScreen
position="relative"
style={Object {}}
/>
<div
className="admin-console-save"
>
<SaveButton
btnClass="btn-primary"
defaultMessage="Save"
disabled={true}
extraClasses=""
onClick={[Function]}
saving={false}
savingMessage="Saving Config..."
/>
<div
className="error-message"
onMouseOut={[Function]}
onMouseOver={[Function]}
>
<FormError
error={null}
errors={Array []}
/>
</div>
<Overlay
animation={[Function]}
delayShow={400}
placement="top"
rootClose={false}
show={false}
>
<Tooltip
bsClass="tooltip"
id="error-tooltip"
placement="right"
/>
</Overlay>
</div>
</form>
</div>
`;
exports[`components/admin_console/CustomTermsOfServiceSettings should match snapshot 4`] = `
<div
className="wrapper--fixed"
>
<AdminHeader>
<FormattedMessage
defaultMessage="Custom Terms of Service"
id="admin.support.termsOfServiceTitle"
values={Object {}}
/>
</AdminHeader>
<form
className="form-horizontal"
onSubmit={[Function]}
role="form"
>
<LoadingScreen
position="relative"
style={Object {}}
/>
<div
className="admin-console-save"
>
<SaveButton
btnClass="btn-primary"
defaultMessage="Save"
disabled={true}
extraClasses=""
onClick={[Function]}
saving={false}
savingMessage="Saving Config..."
/>
<div
className="error-message"
onMouseOut={[Function]}
onMouseOver={[Function]}
>
<FormError
error={null}
errors={Array []}
/>
</div>
<Overlay
animation={[Function]}
delayShow={400}
placement="top"
rootClose={false}
show={false}
>
<Tooltip
bsClass="tooltip"
id="error-tooltip"
placement="right"
/>
</Overlay>
</div>
</form>
</div>
`;
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {FormattedMessage} from 'react-intl';
import PropTypes from 'prop-types';
import {saveConfig} from 'actions/admin_actions.jsx';
import AdminSettings from 'components/admin_console/admin_settings.jsx';
import SettingsGroup from 'components/admin_console/settings_group.jsx';
import BooleanSetting from 'components/admin_console/boolean_setting.jsx';
import TextSetting from 'components/admin_console/text_setting.jsx';
import FormattedMarkdownMessage from 'components/formatted_markdown_message.jsx';
import LoadingScreen from 'components/loading_screen.jsx';
import {Constants} from 'utils/constants';
export default class CustomTermsOfServiceSettings extends AdminSettings {
static propTypes = {
actions: PropTypes.shape({
getTermsOfService: PropTypes.func.isRequired,
createTermsOfService: PropTypes.func.isRequired,
}).isRequired,
config: PropTypes.object,
license: PropTypes.object,
setNavigationBlocked: PropTypes.func,
};
constructor(props) {
super(props);
this.state = {
termsEnabled: props.config.SupportSettings.CustomTermsOfServiceEnabled,
reAcceptancePeriod: props.config.SupportSettings.CustomTermsOfServiceReAcceptancePeriod,
loadingTermsText: true,
receivedTermsText: '',
termsText: '',
saveNeeded: false,
saving: false,
serverError: null,
errorTooltip: false,
};
}
getStateFromConfig(config) {
return {
termsEnabled: config.SupportSettings.CustomTermsOfServiceEnabled,
reAcceptancePeriod: config.SupportSettings.CustomTermsOfServiceReAcceptancePeriod,
};
}
getConfigFromState = (config) => {
config.SupportSettings.CustomTermsOfServiceEnabled = this.state.termsEnabled;
config.SupportSettings.CustomTermsOfServiceReAcceptancePeriod = this.parseIntNonZero(this.state.reAcceptancePeriod, Constants.DEFAULT_TERMS_OF_SERVICE_RE_ACCEPTANCE_PERIOD);
return config;
}
componentDidMount() {
this.getTermsOfService();
}
doSubmit = async (callback) => {
this.setState({
saving: true,
serverError: null,
});
if (this.state.termsEnabled && (this.state.receivedTermsText !== this.state.termsText || !this.props.config.SupportSettings.CustomTermsOfServiceEnabled)) {
const result = await this.props.actions.createTermsOfService(this.state.termsText);
if (result.error) {
this.handleAPIError(result.error, callback);
return;
}
}
// clone config so that we aren't modifying data in the stores
let config = JSON.parse(JSON.stringify(this.props.config));
config = this.getConfigFromState(config);
saveConfig(
config,
(savedConfig) => {
this.setState(this.getStateFromConfig(savedConfig));
this.setState({
saveNeeded: false,
saving: false,
});
this.props.setNavigationBlocked(false);
if (callback) {
callback();
}
if (this.handleSaved) {
this.handleSaved(config);
}
},
(err) => {
this.handleAPIError(err, callback, config);
}
);
};
handleAPIError = (err, callback, config) => {
this.setState({
saving: false,
serverError: err.message,
serverErrorId: err.id,
});
if (callback) {
callback();
}
if (this.handleSaved && config) {
this.handleSaved(config);
}
};
getTermsOfService = async () => {
this.setState({loadingTermsText: true});
const res = await this.props.actions.getTermsOfService();
if (res.data) {
this.setState({
termsText: res.data.text,
receivedTermsText: res.data.text,
});
}
this.setState({loadingTermsText: false});
};
handleTermsTextChange = (id, value) => {
this.handleChange('termsText', value);
};
handleTermsEnabledChange = (id, value) => {
this.handleChange('termsEnabled', value);
};
handleReAcceptancePeriodChange = (id, value) => {
this.handleChange('reAcceptancePeriod', value);
};
renderTitle() {
return (
<FormattedMessage
id='admin.support.termsOfServiceTitle'
defaultMessage='Custom Terms of Service'
/>
);
}
renderSettings = () => {
if (this.state.loadingTermsText) {
return <LoadingScreen/>;
}
return (
<SettingsGroup>
<BooleanSetting
key={'customTermsOfServiceEnabled'}
id={'SupportSettings.CustomTermsOfServiceEnabled'}
label={
<FormattedMessage
id='admin.support.enableTermsOfServiceTitle'
defaultMessage='Enable Custom Terms of Service'
/>
}
helpText={
<FormattedMarkdownMessage
id='admin.support.enableTermsOfServiceHelp'
defaultMessage='When true, new users must accept the terms of service before accessing any Mattermost teams on desktop, web or mobile. Existing users must accept them after login or a page refresh.\n \nTo update terms of service link displayed in account creation and login pages, go to [Legal and Support](legal_and_support).'
/>
}
value={this.state.termsEnabled}
disabled={!(this.props.license.IsLicensed && this.props.license.CustomTermsOfService === 'true')}
onChange={this.handleTermsEnabledChange}
setByEnv={this.isSetByEnv('SupportSettings.CustomTermsOfServiceEnabled')}
/>
<TextSetting
key={'customTermsOfServiceText'}
id={'SupportSettings.CustomTermsOfServiceText'}
type={'textarea'}
label={
<FormattedMessage
id='admin.support.termsOfServiceTextTitle'
defaultMessage='Custom Terms of Service Text'
/>
}
helpText={
<FormattedMessage
id='admin.support.termsOfServiceTextHelp'
defaultMessage='Text that will appear in your custom Terms of Service. Supports Markdown-formatted text.'
/>
}
disabled={!this.state.termsEnabled}
onChange={this.handleTermsTextChange}
setByEnv={this.isSetByEnv('SupportSettings.CustomTermsOfServiceText')}
value={this.state.termsText}
maxLength={Constants.MAX_TERMS_OF_SERVICE_TEXT_LENGTH}
/>
<TextSetting
key={'customTermsOfServiceReAcceptancePeriod'}
id={'SupportSettings.CustomTermsOfServiceReAcceptancePeriod'}
type={'number'}
label={
<FormattedMessage
id='admin.support.termsOfServiceReAcceptanceTitle'
defaultMessage='Re-Acceptance Period:'
/>
}
helpText={
<FormattedMessage
id='admin.support.termsOfServiceReAcceptanceHelp'
defaultMessage='The number of days before Terms of Service acceptance expires, and the terms must be re-accepted.'
/>
}
disabled={!this.state.termsEnabled}