Commit 39c4519c authored by James Addison's avatar James Addison
Browse files

Merge branch 'master' into collabora

parents e32a6606 437ca913
Pipeline #28679 passed with stage
in 16 minutes and 14 seconds
......@@ -16,7 +16,7 @@ exports[`components/TextBox should match snapshot with additional, optional prop
inputComponent={[Function]}
isRHS={true}
listComponent={[Function]}
listStyle="style"
listPosition="top"
listenForMentionKeyClick={true}
onBlur={[Function]}
onChange={[Function]}
......@@ -128,7 +128,7 @@ exports[`components/TextBox should match snapshot with required props 1`] = `
inputComponent={[Function]}
isRHS={false}
listComponent={[Function]}
listStyle="top"
listPosition="top"
listenForMentionKeyClick={false}
onBlur={[Function]}
onChange={[Function]}
......@@ -224,7 +224,7 @@ exports[`components/TextBox should throw error when new property is too long 1`]
inputComponent={[Function]}
isRHS={false}
listComponent={[Function]}
listStyle="top"
listPosition="top"
listenForMentionKeyClick={false}
onBlur={[Function]}
onChange={[Function]}
......@@ -320,7 +320,7 @@ exports[`components/TextBox should throw error when value is too long 1`] = `
inputComponent={[Function]}
isRHS={false}
listComponent={[Function]}
listStyle="top"
listPosition="top"
listenForMentionKeyClick={false}
onBlur={[Function]}
onChange={[Function]}
......
......@@ -78,7 +78,7 @@ exports[`components/AddUserToChannelModal should match snapshot 1`] = `
forceSuggestionsWhenBlur={false}
isRHS={false}
listComponent={[Function]}
listStyle="bottom"
listPosition="bottom"
listenForMentionKeyClick={false}
maxLength="64"
onChange={[Function]}
......
......@@ -245,7 +245,7 @@ export default class AddUserToChannelModal extends React.PureComponent {
listComponent={ModalSuggestionList}
maxLength='64'
providers={this.suggestionProviders}
listStyle='bottom'
listPosition='bottom'
completeOnTab={false}
renderDividers={false}
delayInputUpdate={true}
......
......@@ -77,7 +77,6 @@ import {
GuestAccessFeatureDiscovery,
SystemRolesFeatureDiscovery,
GroupsFeatureDiscovery,
PermissionsFeatureDiscovery,
} from './feature_discovery/features';
import * as DefinitionConstants from './admin_definition_constants';
......@@ -605,7 +604,6 @@ const AdminDefinition = {
'admin.permissions.teamOverrideSchemesNewButton',
],
isHidden: it.any(
it.not(it.licensed),
it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.USER_MANAGEMENT.PERMISSIONS)),
),
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.USER_MANAGEMENT.PERMISSIONS)),
......@@ -614,29 +612,6 @@ const AdminDefinition = {
component: PermissionSchemesSettings,
},
},
permissions_feature_discovery: {
url: 'user_management/permissions/',
isDiscovery: true,
title: t('admin.sidebar.permissions'),
title_default: 'Permissions',
isHidden: it.any(
it.licensed,
it.not(it.enterpriseReady),
),
schema: {
id: 'PermissionSchemes',
name: t('admin.permissions.permissionSchemes'),
name_default: 'Permission Schemes',
settings: [
{
type: Constants.SettingsTypes.TYPE_CUSTOM,
component: PermissionsFeatureDiscovery,
key: 'PermissionsFeatureDiscovery',
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ABOUT.EDITION_AND_LICENSE)),
},
],
},
},
system_role: {
url: 'user_management/system_roles/:role_id',
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.USER_MANAGEMENT.SYSTEM_ROLES)),
......@@ -2099,17 +2074,6 @@ const AdminDefinition = {
name: t('admin.site.usersAndTeams'),
name_default: 'Users and Teams',
settings: [
{
type: Constants.SettingsTypes.TYPE_PERMISSION,
key: 'TeamSettings.EnableTeamCreation',
label: t('admin.team.teamCreationTitle'),
label_default: 'Enable Team Creation: ',
help_text: t('admin.team.teamCreationDescription'),
help_text_default: 'When false, only System Administrators can create teams.',
permissions_mapping_name: 'enableTeamCreation',
isHidden: it.licensed,
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)),
},
{
type: Constants.SettingsTypes.TYPE_NUMBER,
key: 'TeamSettings.MaxUsersPerTeam',
......@@ -2189,21 +2153,6 @@ const AdminDefinition = {
isHidden: it.not(it.licensedForFeature('LockTeammateNameDisplay')),
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)),
},
{
type: Constants.SettingsTypes.TYPE_PERMISSION,
key: 'TeamSettings.EditOthersPosts',
label: t('admin.team.editOthersPostsTitle'),
label_default: 'Allow Team Administrators to edit others\' posts:',
help_text: t('admin.team.editOthersPostsDesc'),
help_text_default: 'When **true**, both Team Admins and System Admins can edit other users\' posts. When **false**, only System Admins can edit other users\' posts. However, Team Admins and System Admins can always delete other users\' posts.',
help_text_markdown: true,
permissions_mapping_name: 'editOthersPosts',
isHidden: it.any(
it.licensed,
it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'),
),
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)),
},
{
type: Constants.SettingsTypes.TYPE_BOOL,
key: 'TeamSettings.ExperimentalViewArchivedChannels',
......
......@@ -171,20 +171,6 @@ exports[`components/AdminSidebar Plugins should match snapshot 1`] = `
/>
}
/>
<AdminSidebarSection
definitionKey="user_management.permissions_feature_discovery"
key="user_management.permissions_feature_discovery"
name="user_management/permissions/"
parentLink=""
subsection={false}
tag=""
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Permissions"
id="admin.sidebar.permissions"
/>
}
/>
<AdminSidebarSection
definitionKey="user_management.system_roles_feature_discovery"
key="user_management.system_roles_feature_discovery"
......@@ -558,6 +544,20 @@ exports[`components/AdminSidebar should match snapshot 1`] = `
/>
}
/>
<AdminSidebarSection
definitionKey="user_management.permissions"
key="user_management.permissions"
name="user_management/permissions/"
parentLink=""
subsection={false}
tag=""
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Permissions"
id="admin.sidebar.permissions"
/>
}
/>
</AdminSidebarCategory>
<AdminSidebarCategory
definitionKey="environment"
......
......@@ -181,7 +181,7 @@ const BillingSubscriptions: React.FC = () => {
<div className='wrapper--fixed BillingSubscriptions'>
<FormattedAdminHeader
id='admin.billing.subscription.title'
defaultMessage='Subscriptions'
defaultMessage='Subscription'
/>
<div className='admin-console__wrapper'>
<div className='admin-console__content'>
......
......@@ -11,7 +11,6 @@ import DataRetentionFeatureDiscovery from './data_retention';
import GuestAccessFeatureDiscovery from './guest_access';
import SystemRolesFeatureDiscovery from './system_roles';
import GroupsFeatureDiscovery from './groups';
import PermissionsFeatureDiscovery from './permissions';
export {
LDAPFeatureDiscovery,
......@@ -25,5 +24,4 @@ export {
GuestAccessFeatureDiscovery,
SystemRolesFeatureDiscovery,
GroupsFeatureDiscovery,
PermissionsFeatureDiscovery,
};
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {t} from 'utils/i18n';
import FeatureDiscovery from '../index';
import PermissionsSVG from './images/permissions_svg';
const PermissionsFeatureDiscovery: React.FC = () => {
return (
<FeatureDiscovery
featureName='permissions'
titleID='admin.permissions_feature_discovery.title'
titleDefault='Set role-based permissions with Mattermost Enterprise E10'
copyID='admin.permissions_feature_discovery.copy'
copyDefault={'Decide who can perform an array of actions such as creating channels, inviting people, managing and archiving channels, managing webhooks, and more in Permission Schemes.'}
learnMoreURL='https://docs.mattermost.com/deployment/advanced-permissions.html'
featureDiscoveryImage={<PermissionsSVG/>}
/>
);
};
t('admin.permissions_feature_discovery.title');
t('admin.permissions_feature_discovery.copy');
export default PermissionsFeatureDiscovery;
......@@ -67,11 +67,12 @@ export default class LicenseSettings extends React.PureComponent {
}
componentDidMount() {
if (!this.props.enterpriseReady) {
if (this.props.enterpriseReady) {
this.props.actions.getPrevTrialLicense();
} else {
this.reloadPercentage();
}
this.props.actions.getLicenseConfig();
this.props.actions.getPrevTrialLicense();
AdminActions.getStandardAnalytics();
}
......
......@@ -303,6 +303,7 @@ export default class PermissionSystemSchemeSettings extends React.PureComponent
if (!this.state.loaded) {
return <LoadingScreen/>;
}
const isLicensed = this.props.license?.IsLicensed === 'true';
return (
<div className='wrapper--fixed'>
<div className='admin-console__header with-back'>
......@@ -331,7 +332,7 @@ export default class PermissionSystemSchemeSettings extends React.PureComponent
</div>
</div>
{this.props.license && this.props.config.EnableGuestAccounts === 'true' &&
{isLicensed && this.props.config.EnableGuestAccounts === 'true' &&
<AdminPanelTogglable
className='permissions-block'
open={this.state.openRoles.guests}
......
......@@ -57,7 +57,7 @@ export default class UserAutocompleteSetting extends React.PureComponent {
onChange={this.handleChange}
onItemSelected={this.handleUserSelected}
listComponent={SuggestionList}
listStyle='bottom'
listPosition='bottom'
providers={this.userSuggestionProviders}
disabled={this.props.disabled}
requiredCharacters={0}
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AppsFormComponent should set match snapshot 1`] = `
<Modal
animation={true}
aria-labelledby="appsModalLabel"
autoFocus={true}
backdrop="static"
bsClass="modal"
dialogClassName="a11y__modal about-modal"
dialogComponentClass={[Function]}
enforceFocus={true}
id="appsModal"
keyboard={true}
manager={
ModalManager {
"add": [Function],
"containers": Array [],
"data": Array [],
"handleContainerOverflow": true,
"hideSiblingNodes": true,
"isTopModal": [Function],
"modals": Array [],
"remove": [Function],
}
}
onExited={[MockFunction]}
onHide={[Function]}
renderBackdrop={[Function]}
restoreFocus={true}
role="dialog"
show={true}
>
<form
autoComplete="off"
onSubmit={[Function]}
>
<ModalHeader
bsClass="modal-header"
closeButton={true}
closeLabel="Close"
style={
Object {
"borderBottom": "",
}
}
>
<ModalTitle
bsClass="modal-title"
componentClass="h1"
id="appsModalLabel"
>
<img
alt="modal title icon"
className="more-modal__image"
height="36"
id="appsModalIconUrl"
src="Icon"
width="36"
/>
Title
</ModalTitle>
</ModalHeader>
<ModalBody
bsClass="modal-body"
componentClass="div"
>
<AppsFormHeader
id="appsModalHeader"
value="Header"
/>
<Connect(AppsFormField)
autoFocus={true}
field={
Object {
"name": "bool1",
"type": "bool",
}
}
key="bool1"
listComponent={[Function]}
name="bool1"
onChange={[Function]}
performLookup={[Function]}
value={false}
/>
<Connect(AppsFormField)
autoFocus={false}
field={
Object {
"name": "bool2",
"type": "bool",
"value": false,
}
}
key="bool2"
listComponent={[Function]}
name="bool2"
onChange={[Function]}
performLookup={[Function]}
value={false}
/>
<Connect(AppsFormField)
autoFocus={false}
field={
Object {
"name": "bool3",
"type": "bool",
"value": true,
}
}
key="bool3"
listComponent={[Function]}
name="bool3"
onChange={[Function]}
performLookup={[Function]}
value={true}
/>
<Connect(AppsFormField)
autoFocus={false}
field={
Object {
"name": "text1",
"type": "text",
"value": "initial text",
}
}
key="text1"
listComponent={[Function]}
name="text1"
onChange={[Function]}
performLookup={[Function]}
value="initial text"
/>
<Connect(AppsFormField)
autoFocus={false}
field={
Object {
"name": "select1",
"options": Array [
Object {
"label": "Label1",
"value": "Value1",
},
Object {
"label": "Label2",
"value": "Value2",
},
],
"type": "static_select",
"value": Object {
"label": "Label1",
"value": "Value1",
},
}
}
key="select1"
listComponent={[Function]}
name="select1"
onChange={[Function]}
performLookup={[Function]}
value={
Object {
"label": "Label1",
"value": "Value1",
}
}
/>
</ModalBody>
<ModalFooter
bsClass="modal-footer"
componentClass="div"
>
<button
className="btn btn-link cancel-button"
id="appsModalCancel"
onClick={[Function]}
type="button"
>
<MemoizedFormattedMessage
defaultMessage="Cancel"
id="interactive_dialog.cancel"
/>
</button>
<SpinnerButton
autoFocus={false}
className="btn btn-primary save-button"
id="appsModalSubmit"
key="submit"
spinning={false}
spinningText="Submitting..."
type="submit"
>
<MemoizedFormattedMessage
defaultMessage="Submit"
id="interactive_dialog.submit"
/>
</SpinnerButton>
</ModalFooter>
</form>
</Modal>
`;
......@@ -2,32 +2,25 @@
// See LICENSE.txt for license information.
import React from 'react';
import {Modal} from 'react-bootstrap';
import {Provider} from 'react-redux';
import {shallow} from 'enzyme';
import configureStore from 'redux-mock-store';
import {AppCallResponseTypes} from 'mattermost-redux/constants/apps';
import {Modal} from 'react-bootstrap';
import EmojiMap from 'utils/emoji_map';
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
import {mountWithIntl, shallowWithIntl} from 'tests/helpers/intl-test-helper';
import {AppCallResponseTypes} from 'mattermost-redux/constants/apps';
import Markdown from 'components/markdown';
import AppsForm from './apps_form';
import {AppsForm, Props} from './apps_form_component';
describe('components/apps_form/AppsForm', () => {
const baseProps = {
form: {
fields: [{
name: 'field1',
type: 'text',
}],
},
call: {
path: '/submit_url',
},
onHide: () => {},
describe('AppsFormComponent', () => {
const baseProps: Props = {
intl: {} as any,
onHide: jest.fn(),
isEmbedded: false,
actions: {
performLookupCall: jest.fn(),
refreshOnSelect: jest.fn(),
......@@ -37,9 +30,107 @@ describe('components/apps_form/AppsForm', () => {
},
}),
},
emojiMap: new EmojiMap(new Map()),
form: {
title: 'Title',
footer: 'Footer',
header: 'Header',
icon: 'Icon',
call: {
path: '/create',
},
fields: [
{
name: 'bool1',
type: 'bool',
},
{
name: 'bool2',
type: 'bool',
value: false,
},
{
name: 'bool3',
type: 'bool',
value: true,
},
{
name: 'text1',
type: 'text',
value: 'initial text',
},
{
name: 'select1',
type: 'static_select',
options: [
{label: 'Label1', value: 'Value1'},
{label: 'Label2', value: 'Value2'},
],
value: {label: 'Label1', value: 'Value1'},
},
],
},
};
test('should set match snapshot', () => {
const wrapper = shallow<AppsForm>(
<AppsForm
{...baseProps}
/>,
);
expect(wrapper).toMatchSnapshot();
});
test('should set initial form values', () => {
const wrapper = shallow<AppsForm>(
<AppsForm
{...baseProps}
/>,
);
expect(wrapper.state().values).toEqual({
bool1: false,
bool2: false,
bool3: true,
text1: 'initial text',
select1: {label: 'Label1', value: 'Value1'},