Admin console definition to typescript (#25232)

* Migrating admin_definition from jsx to tsx

* Fixing types and errors

* Fixing all the types errors

* Fixing linter errors

* Removing no longer needed yup dependency

* Moving admin_definition types to its own file

* Removing a couple of anys

* Fixing linter errors

* Fixing tests snapshots

* Fixing bug

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
Jesús Espino 2023-11-01 09:57:23 +01:00 committed by GitHub
parent 83bd2746c3
commit da43d75e78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 7364 additions and 7503 deletions

View File

@ -178,8 +178,7 @@
"stylelint-scss": "3.19.0",
"webpack-bundle-analyzer": "4.7.0",
"webpack-pwa-manifest": "4.3.0",
"yargs": "16.2.0",
"yup": "0.32.11"
"yargs": "16.2.0"
},
"scripts": {
"check": "eslint --ext .js,.jsx,.tsx,.ts ./src --quiet --cache && stylelint \"**/*.{css,scss}\" --cache",

View File

@ -4,8 +4,8 @@
import React from 'react';
import {Route, Switch, Redirect} from 'react-router-dom';
import type {CloudState, Product} from '@mattermost/types/cloud';
import type {AdminConfig, EnvironmentConfig, ClientLicense} from '@mattermost/types/config';
import type {CloudState} from '@mattermost/types/cloud';
import type {AdminConfig, EnvironmentConfig} from '@mattermost/types/config';
import type {Role} from '@mattermost/types/roles';
import type {DeepPartial} from '@mattermost/types/utilities';
@ -26,6 +26,7 @@ import {LhsItemType} from 'types/store/lhs';
import AdminSidebar from './admin_sidebar';
import Highlight from './highlight';
import type {AdminDefinitionSubSection, AdminDefinitionSection} from './types';
import type {PropsFromRedux} from './index';
@ -52,22 +53,6 @@ type ExtraProps = {
isCurrentUserSystemAdmin: boolean;
}
type ConsoleAccess = {
read: Record<string, boolean>;
write: Record<string, boolean>;
}
type Item = {
isHidden?: (config?: Record<string, any>, state?: Record<string, any>, license?: Record<string, any>, buildEnterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState, isCurrentUserSystemAdmin?: boolean) => boolean;
isDisabled?: (config?: Record<string, any>, state?: Record<string, any>, license?: Record<string, any>, buildEnterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState, isCurrentUserSystemAdmin?: boolean) => boolean;
schema: Record<string, any>;
url: string;
restrictedIndicator?: {
value: (cloud: CloudState) => React.ReactNode;
shouldDisplay: (license: ClientLicense, subscriptionProduct?: Product) => boolean;
};
}
export default class AdminConsole extends React.PureComponent<Props, State> {
public constructor(props: Props) {
super(props);
@ -116,30 +101,22 @@ export default class AdminConsole extends React.PureComponent<Props, State> {
private renderRoutes = (extraProps: ExtraProps) => {
const {adminDefinition, config, license, buildEnterpriseReady, consoleAccess, cloud, isCurrentUserSystemAdmin} = this.props;
const schemas: Item[] = Object.values(adminDefinition).reduce((acc, section) => {
let items: Item[] = [];
const schemas: AdminDefinitionSubSection[] = Object.values(adminDefinition).flatMap((section: AdminDefinitionSection) => {
let isSectionHidden = false;
Object.entries(section).find(([key, value]) => {
if (key === 'isHidden') {
if (typeof value === 'function') {
isSectionHidden = value(config, this.state, license, buildEnterpriseReady, consoleAccess, cloud, isCurrentUserSystemAdmin);
} else {
isSectionHidden = Boolean(value);
}
}
return null;
});
if (!isSectionHidden) {
items = Object.values(section).filter((item: Item) => Boolean(item.schema));
if (typeof section.isHidden === 'function') {
isSectionHidden = section.isHidden(config, this.state, license, buildEnterpriseReady, consoleAccess, cloud, isCurrentUserSystemAdmin);
} else {
isSectionHidden = Boolean(section.isHidden);
}
return acc.concat(items);
}, [] as Item[]);
if (isSectionHidden) {
return [];
}
return Object.values(section.subsections);
});
let defaultUrl = '';
const schemaRoutes = schemas.map((item: Item, index: number) => {
const schemaRoutes = schemas.map((item: AdminDefinitionSubSection, index: number) => {
if (typeof item.isHidden !== 'undefined') {
const isHidden = (typeof item.isHidden === 'function') ? item.isHidden(config, this.state, license, buildEnterpriseReady, consoleAccess, cloud, isCurrentUserSystemAdmin) : Boolean(item.isHidden);
if (isHidden) {

View File

@ -1,211 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import * as yup from 'yup';
import adminDefinition from 'components/admin_console/admin_definition.jsx';
import {Constants} from 'utils/constants';
const baseShape = {
label: yup.string().required(),
label_default: yup.string().required(),
needs_no_license: yup.boolean(),
needs_license: yup.boolean(),
needs: yup.array().of(yup.array().of(yup.string())),
needs_or: yup.array().of(yup.array().of(yup.string())),
};
const fieldShape = {
...baseShape,
key: yup.string().required(),
// help_text: yup.string(), // Commented out since this doesn't work when help_text is a ReactNode
help_text_default: yup.string(),
help_text_html: yup.boolean(),
help_text_values: yup.object(),
};
const option = yup.object().shape({
value: yup.string(),
display_name: yup.string().required(),
display_name_default: yup.string().required(),
});
const settingBanner = yup.object().shape({
...baseShape,
type: yup.mixed().oneOf([Constants.SettingsTypes.TYPE_BANNER]),
banner_type: yup.mixed().oneOf(['info', 'warning']),
});
const settingBool = yup.object().shape({
type: yup.mixed().oneOf([Constants.SettingsTypes.TYPE_BOOL]),
...fieldShape,
});
const settingNumber = yup.object().shape({
type: yup.mixed().oneOf([Constants.SettingsTypes.TYPE_NUMBER]),
...fieldShape,
});
const settingColor = yup.object().shape({
type: yup.mixed().oneOf([Constants.SettingsTypes.TYPE_COLOR]),
...fieldShape,
});
const settingText = yup.object().shape({
type: yup.mixed().oneOf([Constants.SettingsTypes.TYPE_TEXT]),
...fieldShape,
placeholder: yup.string(),
placeholder_default: yup.string(),
});
const settingButton = yup.object().shape({
type: yup.mixed().oneOf([Constants.SettingsTypes.TYPE_BUTTON]),
...fieldShape,
action: yup.object(),
error_message: yup.string().required(),
error_message_default: yup.string().required(),
});
const settingLanguage = yup.object().shape({
type: yup.mixed().oneOf([Constants.SettingsTypes.TYPE_LANGUAGE]),
...fieldShape,
});
const settingMultiLanguage = yup.object().shape({
type: yup.mixed().oneOf([Constants.SettingsTypes.TYPE_LANGUAGE]),
...fieldShape,
multiple: yup.boolean(),
no_result: yup.string().required(),
no_result_default: yup.string().required(),
not_present: yup.string().required(),
not_present_default: yup.string().required(),
});
const settingDropdown = yup.object().shape({
type: yup.mixed().oneOf([Constants.SettingsTypes.TYPE_DROPDOWN]),
...fieldShape,
options: yup.array().of(option),
});
const settingCustom = yup.object().shape({
type: yup.mixed().oneOf([Constants.SettingsTypes.TYPE_CUSTOM]),
...baseShape,
component: yup.object().required(),
});
const settingPermission = yup.object().shape({
type: yup.mixed().oneOf([Constants.SettingsTypes.TYPE_PERMISSION]),
...fieldShape,
permissions_mapping_name: yup.string().required(),
});
const settingJobsTable = yup.object().shape({
type: yup.mixed().oneOf([Constants.SettingsTypes.TYPE_JOBSTABLE]),
...baseShape,
job_type: yup.string().required(),
render_job: yup.object().required(),
});
const settingFileUploadButton = yup.object().shape({
type: yup.mixed().oneOf([Constants.SettingsTypes.TYPE_FILE_UPLOAD]),
...fieldShape,
action: yup.object(),
remove_help_text: yup.string().required(),
remove_help_text_default: yup.string().required(),
remove_button_text: yup.string().required(),
remove_button_text_default: yup.string().required(),
removing_text: yup.string().required(),
removing_text_default: yup.string().required(),
uploading_text: yup.string().required(),
uploading_text_default: yup.string().required(),
upload_action: yup.object().required(),
remove_action: yup.object().required(),
fileType: yup.string().required(),
});
// eslint-disable-next-line no-template-curly-in-string
const setting = yup.mixed().test('is-setting', 'not a valid setting: ${path}', (value) => {
let valid = false;
valid = valid || settingBanner.isValidSync(value);
valid = valid || settingBool.isValidSync(value);
valid = valid || settingNumber.isValidSync(value);
valid = valid || settingColor.isValidSync(value);
valid = valid || settingText.isValidSync(value);
valid = valid || settingButton.isValidSync(value);
valid = valid || settingLanguage.isValidSync(value);
valid = valid || settingMultiLanguage.isValidSync(value);
valid = valid || settingDropdown.isValidSync(value);
valid = valid || settingCustom.isValidSync(value);
valid = valid || settingJobsTable.isValidSync(value);
valid = valid || settingPermission.isValidSync(value);
valid = valid || settingFileUploadButton.isValidSync(value);
return valid;
});
var baseSchema = {
id: yup.string().required(),
name: yup.string().required(),
name_default: yup.string().required(),
};
var schema = yup.object(baseSchema).shape({
settings: yup.array().of(setting).required(),
});
var sectionSchema = yup.object(baseSchema).shape({
sections: yup.array().of(schema).required(),
});
var customComponentSchema = yup.object().shape({
id: yup.string().required(),
component: yup.object().required(),
});
var definition = yup.object().shape({
reporting: yup.object().shape({
system_analytics: yup.object().shape({schema: customComponentSchema}),
team_analytics: yup.object().shape({schema: customComponentSchema}),
system_users: yup.object().shape({schema: customComponentSchema}),
server_logs: yup.object().shape({schema: customComponentSchema}),
}),
authentication: yup.object().shape({
email: yup.object().shape({schema}),
ldap: yup.object().shape({sectionSchema}),
mfa: yup.object().shape({schema}),
saml: yup.object().shape({schema}),
}),
settings: yup.object().shape({
general: yup.object().shape({
configuration: yup.object().shape({schema}),
localization: yup.object().shape({schema}),
users_and_teams: yup.object().shape({schema}),
privacy: yup.object().shape({schema}),
compliance: yup.object().shape({schema}),
}),
security: yup.object().shape({}),
notifications: yup.object().shape({}),
integrations: yup.object().shape({
custom: yup.object().shape({schema}),
}),
plugins: yup.object().shape({}),
files: yup.object().shape({}),
customization: yup.object().shape({
announcement: yup.object().shape({schema}),
}),
compliance: yup.object().shape({}),
advanced: yup.object().shape({}),
}),
other: yup.object().shape({
license: yup.object().shape({schema: customComponentSchema}),
audits: yup.object().shape({schema: customComponentSchema}),
}),
});
describe('components/admin_console/admin_definition', () => {
it('should pass all validations checks', () => {
definition.strict().validateSync(adminDefinition);
});
});

File diff suppressed because it is too large Load Diff

View File

@ -53,7 +53,6 @@ exports[`components/AdminSidebar Plugins should filter plugins 1`] = `
definitionKey="plugins"
icon={
<PowerPlugOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -134,7 +133,6 @@ exports[`components/AdminSidebar Plugins should match snapshot 1`] = `
definitionKey="user_management"
icon={
<AccountMultipleOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -202,7 +200,6 @@ exports[`components/AdminSidebar Plugins should match snapshot 1`] = `
definitionKey="site"
icon={
<CogOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -246,7 +243,6 @@ exports[`components/AdminSidebar Plugins should match snapshot 1`] = `
definitionKey="authentication"
icon={
<ShieldOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -362,7 +358,6 @@ exports[`components/AdminSidebar Plugins should match snapshot 1`] = `
definitionKey="plugins"
icon={
<PowerPlugOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -398,7 +393,6 @@ exports[`components/AdminSidebar Plugins should match snapshot 1`] = `
definitionKey="compliance"
icon={
<FormatListBulletedIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -546,7 +540,6 @@ exports[`components/AdminSidebar should match snapshot 1`] = `
definitionKey="about"
icon={
<InformationOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -577,7 +570,6 @@ exports[`components/AdminSidebar should match snapshot 1`] = `
definitionKey="reporting"
icon={
<ChartBarIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -641,7 +633,6 @@ exports[`components/AdminSidebar should match snapshot 1`] = `
definitionKey="user_management"
icon={
<AccountMultipleOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -705,7 +696,6 @@ exports[`components/AdminSidebar should match snapshot 1`] = `
definitionKey="environment"
icon={
<ServerVariantIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -846,7 +836,6 @@ exports[`components/AdminSidebar should match snapshot 1`] = `
definitionKey="site"
icon={
<CogOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -965,7 +954,6 @@ exports[`components/AdminSidebar should match snapshot 1`] = `
definitionKey="authentication"
icon={
<ShieldOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -1040,7 +1028,6 @@ exports[`components/AdminSidebar should match snapshot 1`] = `
definitionKey="plugins"
icon={
<PowerPlugOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -1076,7 +1063,6 @@ exports[`components/AdminSidebar should match snapshot 1`] = `
definitionKey="integrations"
icon={
<SitemapIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -1140,7 +1126,6 @@ exports[`components/AdminSidebar should match snapshot 1`] = `
definitionKey="experimental"
icon={
<FlaskOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -1249,7 +1234,6 @@ exports[`components/AdminSidebar should match snapshot with workspace optimizati
definitionKey="about"
icon={
<InformationOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -1280,7 +1264,6 @@ exports[`components/AdminSidebar should match snapshot with workspace optimizati
definitionKey="reporting"
icon={
<ChartBarIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -1344,7 +1327,6 @@ exports[`components/AdminSidebar should match snapshot with workspace optimizati
definitionKey="user_management"
icon={
<AccountMultipleOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -1408,7 +1390,6 @@ exports[`components/AdminSidebar should match snapshot with workspace optimizati
definitionKey="environment"
icon={
<ServerVariantIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -1549,7 +1530,6 @@ exports[`components/AdminSidebar should match snapshot with workspace optimizati
definitionKey="site"
icon={
<CogOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -1668,7 +1648,6 @@ exports[`components/AdminSidebar should match snapshot with workspace optimizati
definitionKey="authentication"
icon={
<ShieldOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -1743,7 +1722,6 @@ exports[`components/AdminSidebar should match snapshot with workspace optimizati
definitionKey="plugins"
icon={
<PowerPlugOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -1779,7 +1757,6 @@ exports[`components/AdminSidebar should match snapshot with workspace optimizati
definitionKey="integrations"
icon={
<SitemapIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -1843,7 +1820,6 @@ exports[`components/AdminSidebar should match snapshot with workspace optimizati
definitionKey="experimental"
icon={
<FlaskOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -2007,7 +1983,6 @@ exports[`components/AdminSidebar should match snapshot, not prevent the console
definitionKey="about"
icon={
<InformationOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -2038,7 +2013,6 @@ exports[`components/AdminSidebar should match snapshot, not prevent the console
definitionKey="reporting"
icon={
<ChartBarIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -2102,7 +2076,6 @@ exports[`components/AdminSidebar should match snapshot, not prevent the console
definitionKey="user_management"
icon={
<AccountMultipleOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -2166,7 +2139,6 @@ exports[`components/AdminSidebar should match snapshot, not prevent the console
definitionKey="environment"
icon={
<ServerVariantIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -2307,7 +2279,6 @@ exports[`components/AdminSidebar should match snapshot, not prevent the console
definitionKey="site"
icon={
<CogOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -2426,7 +2397,6 @@ exports[`components/AdminSidebar should match snapshot, not prevent the console
definitionKey="authentication"
icon={
<ShieldOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -2501,7 +2471,6 @@ exports[`components/AdminSidebar should match snapshot, not prevent the console
definitionKey="plugins"
icon={
<PowerPlugOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -2537,7 +2506,6 @@ exports[`components/AdminSidebar should match snapshot, not prevent the console
definitionKey="integrations"
icon={
<SitemapIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -2601,7 +2569,6 @@ exports[`components/AdminSidebar should match snapshot, not prevent the console
definitionKey="experimental"
icon={
<FlaskOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -2710,7 +2677,6 @@ exports[`components/AdminSidebar should match snapshot, render plugins without a
definitionKey="about"
icon={
<InformationOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -2741,7 +2707,6 @@ exports[`components/AdminSidebar should match snapshot, render plugins without a
definitionKey="reporting"
icon={
<ChartBarIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -2805,7 +2770,6 @@ exports[`components/AdminSidebar should match snapshot, render plugins without a
definitionKey="user_management"
icon={
<AccountMultipleOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -2869,7 +2833,6 @@ exports[`components/AdminSidebar should match snapshot, render plugins without a
definitionKey="environment"
icon={
<ServerVariantIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -3010,7 +2973,6 @@ exports[`components/AdminSidebar should match snapshot, render plugins without a
definitionKey="site"
icon={
<CogOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -3129,7 +3091,6 @@ exports[`components/AdminSidebar should match snapshot, render plugins without a
definitionKey="authentication"
icon={
<ShieldOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -3204,7 +3165,6 @@ exports[`components/AdminSidebar should match snapshot, render plugins without a
definitionKey="plugins"
icon={
<PowerPlugOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -3240,7 +3200,6 @@ exports[`components/AdminSidebar should match snapshot, render plugins without a
definitionKey="integrations"
icon={
<SitemapIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -3304,7 +3263,6 @@ exports[`components/AdminSidebar should match snapshot, render plugins without a
definitionKey="experimental"
icon={
<FlaskOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -3413,7 +3371,6 @@ exports[`components/AdminSidebar should match snapshot, with license (with all f
definitionKey="about"
icon={
<InformationOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -3444,7 +3401,6 @@ exports[`components/AdminSidebar should match snapshot, with license (with all f
definitionKey="reporting"
icon={
<ChartBarIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -3508,7 +3464,6 @@ exports[`components/AdminSidebar should match snapshot, with license (with all f
definitionKey="user_management"
icon={
<AccountMultipleOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -3594,7 +3549,6 @@ exports[`components/AdminSidebar should match snapshot, with license (with all f
definitionKey="environment"
icon={
<ServerVariantIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -3757,7 +3711,6 @@ exports[`components/AdminSidebar should match snapshot, with license (with all f
definitionKey="site"
icon={
<CogOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -3887,7 +3840,6 @@ exports[`components/AdminSidebar should match snapshot, with license (with all f
definitionKey="authentication"
icon={
<ShieldOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -4006,7 +3958,6 @@ exports[`components/AdminSidebar should match snapshot, with license (with all f
definitionKey="plugins"
icon={
<PowerPlugOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -4042,7 +3993,6 @@ exports[`components/AdminSidebar should match snapshot, with license (with all f
definitionKey="integrations"
icon={
<SitemapIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -4106,7 +4056,6 @@ exports[`components/AdminSidebar should match snapshot, with license (with all f
definitionKey="compliance"
icon={
<FormatListBulletedIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -4170,7 +4119,6 @@ exports[`components/AdminSidebar should match snapshot, with license (with all f
definitionKey="experimental"
icon={
<FlaskOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -4279,7 +4227,6 @@ exports[`components/AdminSidebar should match snapshot, with license (without an
definitionKey="about"
icon={
<InformationOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -4310,7 +4257,6 @@ exports[`components/AdminSidebar should match snapshot, with license (without an
definitionKey="reporting"
icon={
<ChartBarIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -4374,7 +4320,6 @@ exports[`components/AdminSidebar should match snapshot, with license (without an
definitionKey="user_management"
icon={
<AccountMultipleOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -4486,7 +4431,6 @@ exports[`components/AdminSidebar should match snapshot, with license (without an
definitionKey="environment"
icon={
<ServerVariantIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -4627,7 +4571,6 @@ exports[`components/AdminSidebar should match snapshot, with license (without an
definitionKey="site"
icon={
<CogOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -4770,7 +4713,6 @@ exports[`components/AdminSidebar should match snapshot, with license (without an
definitionKey="authentication"
icon={
<ShieldOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -4941,7 +4883,6 @@ exports[`components/AdminSidebar should match snapshot, with license (without an
definitionKey="plugins"
icon={
<PowerPlugOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -4977,7 +4918,6 @@ exports[`components/AdminSidebar should match snapshot, with license (without an
definitionKey="integrations"
icon={
<SitemapIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -5041,7 +4981,6 @@ exports[`components/AdminSidebar should match snapshot, with license (without an
definitionKey="compliance"
icon={
<FormatListBulletedIcon
className="category-icon fa"
color="currentColor"
size={16}
/>
@ -5133,7 +5072,6 @@ exports[`components/AdminSidebar should match snapshot, with license (without an
definitionKey="experimental"
icon={
<FlaskOutlineIcon
className="category-icon fa"
color="currentColor"
size={16}
/>

View File

@ -161,7 +161,7 @@ class AdminSidebar extends React.PureComponent<Props, State> {
};
const result = new Set();
for (const section of Object.values(adminDefinition)) {
for (const item of Object.values(section)) {
for (const item of Object.values(section.subsections)) {
if (isVisible(item)) {
result.add(item.url);
}
@ -180,7 +180,7 @@ class AdminSidebar extends React.PureComponent<Props, State> {
}
if (!isSectionHidden) {
const sidebarItems: JSX.Element[] = [];
Object.entries(section).forEach(([subKey, item]) => {
Object.entries(section.subsections).forEach(([subKey, item]) => {
if (!item.title) {
return;
}

View File

@ -20,7 +20,7 @@ const AdminSidebarCategory = ({icon, title, action, children, definitionKey, nam
let link = parentLink;
let titleDiv = (
<div className='category-title category-title--active'>
{icon}
<span className='category-icon'>{icon}</span>
<span className='category-title__text'>
{title}
</span>

View File

@ -1,29 +1,19 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {PluginRedux} from '@mattermost/types/plugins';
import type {PluginRedux, PluginSetting} from '@mattermost/types/plugins';
import {Constants} from 'utils/constants';
import {t} from 'utils/i18n';
import SchemaAdminSettings from '../schema_admin_settings';
import type {AdminDefinitionSetting} from '../types';
export type EnabledPluginSetting = {
type: string;
key: string;
label: string;
label_default: string;
help_text: string;
help_text_default: string;
isDisabled?: () => boolean;
}
export default function getEnablePluginSetting(plugin: PluginRedux): EnabledPluginSetting {
export default function getEnablePluginSetting(plugin: PluginRedux): Partial<AdminDefinitionSetting & PluginSetting> {
const escapedPluginId = SchemaAdminSettings.escapePathPart(plugin.id);
const pluginEnabledConfigKey = 'PluginSettings.PluginStates.' + escapedPluginId + '.Enable';
return {
type: Constants.SettingsTypes.TYPE_BOOL,
type: 'bool',
key: pluginEnabledConfigKey,
label: t('admin.plugin.enable_plugin'),
label_default: 'Enable Plugin: ',

View File

@ -3,7 +3,7 @@
import {connect} from 'react-redux';
import type {PluginRedux} from '@mattermost/types/plugins';
import type {PluginRedux, PluginSetting} from '@mattermost/types/plugins';
import type {GlobalState} from '@mattermost/types/store';
import {createSelector} from 'mattermost-redux/selectors/create_selector';
@ -24,6 +24,7 @@ import getEnablePluginSetting from './enable_plugin_setting';
import {it} from '../admin_definition';
import SchemaAdminSettings from '../schema_admin_settings';
import type {AdminDefinitionSetting} from '../types';
type OwnProps = { match: { params: { plugin_id: string } } }
@ -42,7 +43,7 @@ function makeGetPluginSchema() {
const escapedPluginId = SchemaAdminSettings.escapePathPart(plugin.id);
const pluginEnabledConfigKey = 'PluginSettings.PluginStates.' + escapedPluginId + '.Enable';
let settings: Array<Partial<SchemaAdminSettings>> = [];
let settings: Array<Partial<AdminDefinitionSetting & PluginSetting>> = [];
if (plugin.settings_schema && plugin.settings_schema.settings) {
settings = plugin.settings_schema.settings.map((setting) => {
const key = setting.key.toLowerCase();
@ -81,17 +82,25 @@ function makeGetPluginSchema() {
component,
showTitle: customComponents[key] ? customComponents[key].options.showTitle : false,
};
});
}) as Array<Partial<AdminDefinitionSetting & PluginSetting>>;
}
if (plugin.id !== appsPluginID || appsFeatureFlagIsEnabled) {
const pluginEnableSetting = getEnablePluginSetting(plugin);
pluginEnableSetting.isDisabled = it.any(pluginEnableSetting.isDisabled, it.not(it.userHasWritePermissionOnResource('plugins')));
if (pluginEnableSetting.isDisabled) {
pluginEnableSetting.isDisabled = it.any(pluginEnableSetting.isDisabled, it.not(it.userHasWritePermissionOnResource('plugins')));
} else {
pluginEnableSetting.isDisabled = it.not(it.userHasWritePermissionOnResource('plugins'));
}
settings.unshift(pluginEnableSetting);
}
settings.forEach((s) => {
s.isDisabled = it.any(s.isDisabled, it.not(it.userHasWritePermissionOnResource('plugins')));
if (s.isDisabled) {
s.isDisabled = it.any(s.isDisabled, it.not(it.userHasWritePermissionOnResource('plugins')));
} else {
s.isDisabled = it.not(it.userHasWritePermissionOnResource('plugins'));
}
});
return {

View File

@ -0,0 +1,205 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {CloudState, Product} from '@mattermost/types/cloud';
import type {AdminConfig, ClientLicense} from '@mattermost/types/config';
import type {DeepPartial} from '@mattermost/types/utilities';
import type ValidationResult from './validation';
type Component = any
type AdminDefinitionConfigSchemaComponent = {
id: string;
component: Component;
}
export type ConsoleAccess = {read: {[key: string]: boolean}; write: {[key: string]: boolean}}
type Validator = (value: any) => ValidationResult
type AdminDefinitionSettingCustom = {
type: 'custom';
label?: string;
label_default?: string;
component: Component;
key: string;
isDisabled?: Check;
isHidden?: Check;
}
type AdminDefinitionSettingBase = {
key?: string;
label: string;
label_default: string;
onConfigLoad?: (configVal: any, config: any) => any;
onConfigSave?: (displayVal: any, previousVal?: any) => any;
isHidden?: Check;
isDisabled?: Check;
}
type AdminDefinitionSettingBanner = AdminDefinitionSettingBase & {
type: 'banner';
label_markdown?: boolean;
label_values?: {[key: string]: any};
banner_type: 'info' | 'warning';
}
type AdminDefinitionSettingInput = AdminDefinitionSettingBase & {
type: 'text' | 'bool' | 'longtext' | 'number' | 'color';
help_text?: string | JSX.Element;
help_text_default?: string | JSX.Element;
help_text_markdown?: boolean;
help_text_values?: {[key: string]: any};
disabled_help_text?: string;
disabled_help_text_default?: string;
disabled_help_text_markdown?: boolean;
placeholder?: string;
placeholder_default?: string;
validate?: Validator;
setFromMetadataField?: string;
dynamic_value?: (value: any, config: DeepPartial<AdminConfig>, state: any) => string;
max_length?: number;
}
type AdminDefinitionSettingGenerated = AdminDefinitionSettingBase & {
type: 'generated';
help_text: string | JSX.Element;
help_text_default: string | JSX.Element;
isDisabled: Check;
}
type AdminDefinitionSettingDropdownOption = {
value: string;
display_name: string;
display_name_default: string;
help_text?: string;
help_text_default?: string;
help_text_markdown?: boolean;
help_text_values?: {[key: string]: any};
isHidden?: Check;
}
type AdminDefinitionSettingDropdown = AdminDefinitionSettingBase & {
type: 'dropdown';
help_text?: string | JSX.Element;
help_text_default?: string | JSX.Element;
help_text_markdown?: boolean;
help_text_values?: {[key: string]: any};
disabled_help_text?: string;
disabled_help_text_default?: string;
disabled_help_text_markdown?: boolean;
options: AdminDefinitionSettingDropdownOption[];
isHelpHidden?: Check;
}
type AdminDefinitionSettingFileUpload = AdminDefinitionSettingBase & {
type: 'fileupload';
help_text: string;
help_text_default: string;
remove_help_text: string;
remove_help_text_default: string;
remove_button_text: string;
remove_button_text_default: string;
removing_text: string;
removing_text_default: string;
uploading_text: string;
uploading_text_default: string;
fileType: string;
upload_action: () => void;
set_action?: () => void;
setFromMetadataField?: string;
remove_action: () => void;
}
type AdminDefinitionSettingJobsTable = AdminDefinitionSettingBase & {
type: 'jobstable';
job_type: string;
help_text: string;
help_text_markdown: boolean;
help_text_default: string;
help_text_values?: {[key: string]: any};
render_job: Component;
};
type AdminDefinitionSettingLanguage = AdminDefinitionSettingBase & {
type: 'language';
help_text?: string;
help_text_markdown?: boolean;
help_text_default?: string;
help_text_values?: {[key: string]: any};
multiple?: boolean;
no_result?: string;
no_result_default?: string;
not_present?: string;
not_present_default?: string;
}
type AdminDefinitionSettingButton = AdminDefinitionSettingBase & {
type: 'button';
action: () => void;
loading?: string;
loading_default?: string;
help_text?: string | JSX.Element;
help_text_default?: string | JSX.Element;
help_text_markdown?: boolean;
help_text_values?: {[key: string]: any};
error_message: string;
error_message_default: string;
success_message?: string;
success_message_default?: string;
sourceUrlKey?: string;
}
export type AdminDefinitionSetting = AdminDefinitionSettingCustom |
AdminDefinitionSettingInput | AdminDefinitionSettingGenerated |
AdminDefinitionSettingBanner | AdminDefinitionSettingDropdown |
AdminDefinitionSettingButton | AdminDefinitionSettingFileUpload |
AdminDefinitionSettingJobsTable | AdminDefinitionSettingLanguage;
type AdminDefinitionConfigSchemaSettings = {
id: string;
name: string;
name_default: string;
isHidden?: Check;
onConfigLoad?: (configVal: any, config: any) => any;
onConfigSave?: (displayVal: any) => any;
settings?: AdminDefinitionSetting[];
sections?: AdminDefinitionConfigSchemaSection[];
}
type AdminDefinitionConfigSchemaSection = {
title: string;
subtitle?: string;
settings: AdminDefinitionSetting[];
}
type RestrictedIndicatorType = {
value: (cloud: CloudState) => JSX.Element;
shouldDisplay: (license: ClientLicense, subscriptionProduct: Product|undefined) => boolean;
}
export type AdminDefinitionSubSection = {
url: string;
title?: string;
title_default?: string;
searchableStrings?: Array<string|[string, {[key: string]: any}]>;
isHidden?: Check;
isDiscovery?: boolean;
isDisabled?: Check;
schema: AdminDefinitionConfigSchemaComponent | AdminDefinitionConfigSchemaSettings;
restrictedIndicator?: RestrictedIndicatorType;
}
export type AdminDefinitionSection = {
icon: JSX.Element;
sectionTitle: string;
sectionTitleDefault: string;
isHidden: Check;
id?: string;
subsections: {[key: string]: AdminDefinitionSubSection};
}
export type AdminDefinition = {[key: string]: AdminDefinitionSection}
export type Check = boolean | ((config: DeepPartial<AdminConfig>, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState, isSystemAdmin?: boolean) => boolean)

View File

@ -690,6 +690,10 @@
&.fa-users {
font-size: 13px;
}
>svg {
vertical-align: bottom;
}
}
}

View File

@ -7,7 +7,7 @@ import {ResourceToSysConsolePermissionsTable, RESOURCE_KEYS} from 'mattermost-re
import {createSelector} from 'mattermost-redux/selectors/create_selector';
import {getMySystemPermissions} from 'mattermost-redux/selectors/entities/roles_helpers';
import AdminDefinition from 'components/admin_console/admin_definition.jsx';
import AdminDefinition from 'components/admin_console/admin_definition';
export const getAdminDefinition = createSelector(
'getAdminDefinition',

View File

@ -3,7 +3,7 @@
import {getAdminDefinition} from 'selectors/admin_console.jsx';
import AdminDefinition from 'components/admin_console/admin_definition.jsx';
import AdminDefinition from 'components/admin_console/admin_definition';
describe('Selectors.AdminConsole', () => {
describe('get admin definitions', () => {

View File

@ -3,7 +3,7 @@
import {createIntl} from 'react-intl';
import AdminDefinition from 'components/admin_console/admin_definition.jsx';
import AdminDefinition from 'components/admin_console/admin_definition';
import {samplePlugin1, samplePlugin2} from 'tests/helpers/admin_console_plugin_index_sample_pluings';

View File

@ -96,7 +96,7 @@ export function adminDefinitionsToUrlsAndTexts(adminDefinition: typeof AdminDefi
adminDefinition.billing,
];
for (const section of sections) {
for (const item of Object.values(section)) {
for (const item of Object.values(section.subsections)) {
if (!item.isDiscovery) {
entries[item.url] = extractTextsFromSection(item, intl);
}

View File

@ -4,7 +4,7 @@
import type {PluginRedux, PluginSetting} from '@mattermost/types/plugins';
import getEnablePluginSetting from 'components/admin_console/custom_plugin_settings/enable_plugin_setting';
import type {EnabledPluginSetting} from 'components/admin_console/custom_plugin_settings/enable_plugin_setting';
import type {AdminDefinitionSetting} from 'components/admin_console/types';
import {stripMarkdown} from 'utils/markdown';
@ -28,7 +28,7 @@ function extractTextsFromPlugin(plugin: PluginRedux) {
const settings = Object.values(plugin.settings_schema.settings);
for (const setting of settings) {
const settingsTexts = extractTextFromSetting(setting);
const settingsTexts = extractTextFromSetting(setting as Partial<AdminDefinitionSetting & PluginSetting>);
texts.push(...settingsTexts);
}
}
@ -36,7 +36,7 @@ function extractTextsFromPlugin(plugin: PluginRedux) {
return texts;
}
function extractTextFromSetting(setting: Partial<EnabledPluginSetting & PluginSetting>) {
function extractTextFromSetting(setting: Partial<AdminDefinitionSetting & PluginSetting>) {
const texts = [];
if (setting.label) {
texts.push(setting.label);

View File

@ -225,8 +225,7 @@
"stylelint-scss": "3.19.0",
"webpack-bundle-analyzer": "4.7.0",
"webpack-pwa-manifest": "4.3.0",
"yargs": "16.2.0",
"yup": "0.32.11"
"yargs": "16.2.0"
}
},
"channels/node_modules/@mattermost/compass-icons": {
@ -16722,12 +16721,6 @@
"react-dom": "*"
}
},
"node_modules/nanoclone": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz",
"integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==",
"dev": true
},
"node_modules/nanoid": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
@ -18621,12 +18614,6 @@
"retry": "^0.10.0"
}
},
"node_modules/property-expr": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz",
"integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==",
"dev": true
},
"node_modules/proto-list": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
@ -22211,12 +22198,6 @@
"node": ">=0.6"
}
},
"node_modules/toposort": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==",
"dev": true
},
"node_modules/totalist": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz",
@ -23817,24 +23798,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/yup": {
"version": "0.32.11",
"resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz",
"integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.15.4",
"@types/lodash": "^4.14.175",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"nanoclone": "^0.2.1",
"property-expr": "^2.0.4",
"toposort": "^2.0.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/zen-observable": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.9.0.tgz",
@ -36215,7 +36178,6 @@
"webpack-bundle-analyzer": "4.7.0",
"webpack-pwa-manifest": "4.3.0",
"yargs": "16.2.0",
"yup": "0.32.11",
"zen-observable": "0.9.0"
},
"dependencies": {
@ -37020,12 +36982,6 @@
"stylis": "^4.0.6"
}
},
"nanoclone": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz",
"integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==",
"dev": true
},
"nanoid": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
@ -38469,12 +38425,6 @@
"retry": "^0.10.0"
}
},
"property-expr": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz",
"integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==",
"dev": true
},
"proto-list": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
@ -41256,12 +41206,6 @@
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"dev": true
},
"toposort": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==",
"dev": true
},
"totalist": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz",
@ -42409,21 +42353,6 @@
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true
},
"yup": {
"version": "0.32.11",
"resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz",
"integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==",
"dev": true,
"requires": {
"@babel/runtime": "^7.15.4",
"@types/lodash": "^4.14.175",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"nanoclone": "^0.2.1",
"property-expr": "^2.0.4",
"toposort": "^2.0.2"
}
},
"zen-observable": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.9.0.tgz",