From 086e6d9df61cdd9f56a512a9c53110949e8a2dd0 Mon Sep 17 00:00:00 2001 From: Claudio Costa Date: Mon, 13 Jan 2025 11:19:59 +0100 Subject: [PATCH] [MM-62066] Allow using plugin settings defined in manifest as fallback when using custom sections and plugin is disabled (#29500) * Allow using plugin settings defined in manifest as fallback when using custom sections and plugin is disabled * Update server/public/model/manifest.go Co-authored-by: Christopher Poile * Add custom setting case --------- Co-authored-by: Christopher Poile Co-authored-by: Mattermost Build --- server/public/model/manifest.go | 3 + server/public/model/manifest_test.go | 12 +++ .../custom_plugin_settings/index.test.tsx | 83 +++++++++++++++++++ .../custom_plugin_settings/index.ts | 11 ++- webapp/platform/types/src/plugins.ts | 1 + 5 files changed, 107 insertions(+), 3 deletions(-) diff --git a/server/public/model/manifest.go b/server/public/model/manifest.go index 87443603fb..c3e46d8245 100644 --- a/server/public/model/manifest.go +++ b/server/public/model/manifest.go @@ -116,6 +116,9 @@ type PluginSettingsSection struct { // If true, the section will load the custom component registered using `registry.registerAdminConsoleCustomSection` Custom bool `json:"custom" yaml:"custom"` + + // If true and Custom = true, the settings defined under this section will still render as fallback (unless the individual setting is type 'custom') when the plugin is disabled. + Fallback bool `json:"fallback" yaml:"fallback"` } type PluginSettingsSchema struct { diff --git a/server/public/model/manifest_test.go b/server/public/model/manifest_test.go index 4702dea6d3..d5fee3d6b9 100644 --- a/server/public/model/manifest_test.go +++ b/server/public/model/manifest_test.go @@ -95,6 +95,18 @@ func TestIsValid(t *testing.T) { }, }, }, + { + Key: "section3", + Custom: true, + Fallback: true, + Settings: []*PluginSetting{ + { + Key: "section3setting1", + DisplayName: "thedisplayname", + Type: "custom", + }, + }, + }, }, }, }, false}, diff --git a/webapp/channels/src/components/admin_console/custom_plugin_settings/index.test.tsx b/webapp/channels/src/components/admin_console/custom_plugin_settings/index.test.tsx index 2638680af0..a26d2d1074 100644 --- a/webapp/channels/src/components/admin_console/custom_plugin_settings/index.test.tsx +++ b/webapp/channels/src/components/admin_console/custom_plugin_settings/index.test.tsx @@ -167,6 +167,89 @@ describe('custom plugin sections and settings', () => { expect(screen.queryByText('Custom Section 2')).not.toBeInTheDocument(); }); + it('all custom sections with plugin disabled and fallback enabled should render available settings', () => { + const state = { + ...baseState, + entities: { + admin: { + plugins: { + testplugin: { + ...plugin, + settings_schema: { + ...plugin.settings_schema, + sections: [ + { + key: 'section1', + title: 'Custom Section 1', + settings: [ + { + key: 'customsection1numbersetting', + label: 'Custom Section Number Setting', + type: 'number' as const, + help_text: 'Custom Section Number Setting Help Text', + }, + ], + custom: true, + fallback: true, + }, + { + key: 'section2', + title: 'Custom Section 2', + settings: [ + { + key: 'customsection2numbersetting', + label: 'Custom Section Bool Setting', + type: 'bool' as const, + help_text: 'Custom Section Bool Setting Help Text', + }, + { + key: 'customsection2customsetting', + label: 'Custom Section Custom Setting', + type: 'custom' as const, + help_text: 'Custom Section Custom Setting Help Text', + }, + ], + custom: true, + fallback: true, + }, + ], + }, + }, + }, + }, + }, + }; + + const props = { + ...baseProps, + config: { + ...baseProps.config, + PluginStates: { + testplugin: { + Enabled: false, + }, + }, + }, + }; + + renderWithContext( + + , {...state}); + + expect(screen.getByText('testplugin')).toBeInTheDocument(); + expect(screen.getByTestId('PluginSettings.PluginStates.testplugin.Enable')).toBeInTheDocument(); + expect(screen.queryByText('In order to view and configure plugin settings, enable the plugin and click Save.')).not.toBeInTheDocument(); + expect(screen.queryByText('Custom Section 1')).toBeInTheDocument(); + expect(screen.queryByText('Custom Section 2')).toBeInTheDocument(); + expect(screen.getByText('Custom Section Number Setting Help Text')).toBeInTheDocument(); + expect(screen.getByText('Custom Section Bool Setting Help Text')).toBeInTheDocument(); + expect(screen.queryByText('Custom Section Custom Setting Help Text')).not.toBeInTheDocument(); + expect(screen.getByText('In order to view this setting, enable the plugin and click Save.')).toBeInTheDocument(); + }); + it('custom sections with plugin enabled should render as expected', () => { const CustomSection1 = () => { return ( diff --git a/webapp/channels/src/components/admin_console/custom_plugin_settings/index.ts b/webapp/channels/src/components/admin_console/custom_plugin_settings/index.ts index 8913779c80..3294a33311 100644 --- a/webapp/channels/src/components/admin_console/custom_plugin_settings/index.ts +++ b/webapp/channels/src/components/admin_console/custom_plugin_settings/index.ts @@ -95,8 +95,10 @@ function makeGetPluginSchema() { if (customSections[key]) { component = customSections[key]?.component; settings = parsePluginSettings(section.settings); + } else if (section.fallback) { + settings = parsePluginSettings(section.settings); } else { - // Show warning banner for custom sections when the plugin is disabled. + // Show warning banner for custom sections when the plugin is disabled and there's no fallback. settings = [{ key: key + 'disabledWarning', type: Constants.SettingsTypes.TYPE_BANNER, @@ -134,8 +136,11 @@ function makeGetPluginSchema() { if (plugin.id !== appsPluginID || appsFeatureFlagIsEnabled) { const pluginEnableSetting = getEnablePluginSetting(plugin) as AdminDefinitionSetting; - if (plugin.settings_schema && plugin.settings_schema.sections?.every((s) => s.custom && !customSections[s.key.toLowerCase()])) { - // If the plugin is composed of purely custom sections (e.g. Calls) and it's disabled (custom components are not found), we show a single warning. + const hasAllCustomSectionsDisabled = plugin.settings_schema?.sections?.every((s) => s.custom && !customSections[s.key.toLowerCase()]); + const allCustomSectionsAllowFallback = plugin.settings_schema?.sections?.every((s) => s.custom && s.fallback); + + if (plugin.settings_schema && hasAllCustomSectionsDisabled && !allCustomSectionsAllowFallback) { + // If the plugin is composed of purely custom sections (e.g. Calls), it's disabled (custom components are not found), and they don't allow a fallback, we show a single warning. const warningBanner = { key: 'admin.plugin.customSections.pluginDisabledWarning', type: Constants.SettingsTypes.TYPE_BANNER, diff --git a/webapp/platform/types/src/plugins.ts b/webapp/platform/types/src/plugins.ts index 7fb70b1df5..3284a6f5dd 100644 --- a/webapp/platform/types/src/plugins.ts +++ b/webapp/platform/types/src/plugins.ts @@ -55,6 +55,7 @@ export type PluginSettingSection = { header?: string; footer?: string; custom?: boolean; + fallback?: boolean; }; export type PluginSetting = {