From 8818141dae5aa980340d3a94a9ebe923eb4f9403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Espino=20Garc=C3=ADa?= Date: Tue, 16 Jan 2024 10:10:24 +0100 Subject: [PATCH] Remove t function from admin_definition.tsx (and more) (#25406) * Remove t function from schema_admin_settings * i18n * Fix import * Use always string or descriptor and more improvements * i18n-extract * Remove t function for roles * Remove more t functions * Address feedback * i18n-extract fix * Fix tests * Fix tests * Remove translation of team edition notice * Remove unexpected file in the commit * Address feedback * Fix search in admin console * Fix test --- .../bleve_settings.test.tsx.snap | 24 +- .../cluster_settings.test.jsx.snap | 120 +- .../database_settings.test.tsx.snap | 42 +- .../elasticsearch_settings.test.tsx.snap | 146 +- .../message_export_settings.test.jsx.snap | 94 +- .../__snapshots__/push_settings.test.tsx.snap | 28 +- .../schema_admin_settings.test.jsx.snap | 165 +- .../admin_console/admin_console.tsx | 17 +- .../admin_console/admin_definition.tsx | 3374 ++++++----------- .../admin_definition_constants.tsx | 11 +- .../__snapshots__/admin_sidebar.test.tsx.snap | 12 +- .../admin_sidebar/admin_sidebar.test.tsx | 14 +- .../admin_sidebar/admin_sidebar.tsx | 23 +- .../admin_sidebar/admin_sidebar_category.tsx | 2 +- .../admin_console/audits/audits.tsx | 15 +- .../admin_console/billing/billing_history.tsx | 13 +- .../billing/billing_subscriptions/index.tsx | 15 +- .../admin_console/billing/company_info.tsx | 15 +- .../delete_workspace/delete_workspace_cta.tsx | 10 +- .../delete_workspace_modal.tsx | 11 +- .../admin_console/billing/payment_info.tsx | 15 +- .../admin_console/bleve_settings.tsx | 116 +- .../admin_console/boolean_setting.tsx | 2 +- .../admin_console/cluster_settings.test.jsx | 2 +- ...ster_settings.jsx => cluster_settings.tsx} | 208 +- .../admin_console/cluster_table_container.tsx | 87 +- .../custom_plugin_settings.test.tsx.snap | 33 +- .../custom_plugin_settings.test.tsx | 50 +- .../custom_plugin_settings.tsx | 2 +- .../enable_plugin_setting.ts | 14 +- .../custom_plugin_settings/index.ts | 21 +- .../custom_terms_of_service_settings.tsx | 71 +- .../data_retention_settings.test.tsx | 11 +- .../data_retention_settings.tsx | 114 +- .../admin_console/database_settings.tsx | 209 +- .../elasticsearch_settings.test.tsx | 6 +- .../admin_console/elasticsearch_settings.tsx | 247 +- .../admin_console/feature_flags.tsx | 11 +- .../admin_console/file_upload_setting.tsx | 2 +- .../group_settings.test.tsx.snap | 17 +- .../__snapshots__/group_details.test.tsx.snap | 136 +- .../group_profile_and_settings.test.tsx.snap | 58 +- .../group_details/group_details.test.tsx | 47 +- .../group_details/group_details.tsx | 56 +- .../group_details/group_profile.test.tsx | 3 +- .../group_details/group_profile.tsx | 10 +- .../group_profile_and_settings.tsx | 20 +- .../group_settings/group_settings.tsx | 9 +- .../components/admin_console/jobs/table.tsx | 2 +- .../enterprise_edition_left_panel.tsx | 14 +- .../license_settings/license_settings.tsx | 21 +- .../starter_edition/starter_left_panel.tsx | 15 +- .../message_export_settings.test.jsx | 13 +- .../admin_console/message_export_settings.tsx | 179 +- .../admin_console/password_settings.tsx | 201 +- .../permission_group.test.tsx.snap | 72 +- .../permission_row.test.tsx.snap | 24 +- .../permission_schemes_settings.test.tsx.snap | 269 +- .../permission_description.tsx | 36 +- .../permission_group.tsx | 8 +- .../permission_row.tsx | 45 +- .../permission_schemes_settings.test.tsx | 15 +- .../permission_schemes_settings.tsx | 287 +- ...ssion_system_scheme_settings.test.tsx.snap | 384 +- ...permission_system_scheme_settings.test.tsx | 90 +- .../permission_system_scheme_settings.tsx | 49 +- ...mission_team_scheme_settings.test.tsx.snap | 519 ++- .../permission_team_scheme_settings.test.tsx | 80 +- .../permission_team_scheme_settings.tsx | 64 +- .../strings/groups.tsx | 258 ++ .../strings/permissions.tsx | 618 +++ .../strings/roles.tsx | 36 + .../plugin_management.test.tsx.snap | 54 +- .../plugin_management.test.tsx | 33 +- .../plugin_management/plugin_management.tsx | 154 +- .../admin_console/push_settings.test.tsx | 7 +- .../admin_console/push_settings.tsx | 59 +- .../request_button.test.tsx.snap | 35 +- .../request_button/request_button.tsx | 89 +- .../schema_admin_settings.test.jsx | 38 +- ...settings.jsx => schema_admin_settings.tsx} | 619 +-- .../admin_console/schema_text.test.tsx | 6 +- .../components/admin_console/schema_text.tsx | 80 +- .../admin_console/server_logs/logs.tsx | 27 +- .../admin_console/session_length_settings.tsx | 164 +- .../src/components/admin_console/setting.tsx | 2 +- .../__snapshots__/system_roles.test.tsx.snap | 20 +- .../admin_console/system_roles/strings.tsx | 77 + .../__snapshots__/system_role.test.tsx.snap | 10 +- .../system_role_permission.test.tsx.snap | 14 +- .../system_role_permissions.test.tsx.snap | 34 +- .../add_users_to_role_modal.test.tsx.snap | 30 +- .../add_users_to_role_modal.tsx | 9 +- .../system_roles/system_role/strings.tsx | 433 +++ .../system_roles/system_role/system_role.tsx | 9 +- .../system_role_permission.test.tsx | 2 +- .../system_role/system_role_permission.tsx | 15 +- .../system_role/system_role_permissions.tsx | 84 +- .../system_role_users.test.tsx.snap | 34 +- .../system_role_users/system_role_users.tsx | 9 +- .../system_roles/system_roles.tsx | 33 +- .../system_user_detail.test.tsx.snap | 102 +- .../system_user_detail.test.tsx | 16 +- .../system_user_detail/system_user_detail.tsx | 36 +- .../system_users/system_users.tsx | 12 +- .../channel_settings.test.tsx.snap | 17 +- .../channel/channel_settings.tsx | 16 +- .../channel_groups.test.tsx.snap | 17 +- .../channel_moderation.test.tsx.snap | 102 +- .../__snapshots__/channel_modes.test.tsx.snap | 34 +- .../channel_profile.test.tsx.snap | 34 +- .../channel/details/channel_groups.tsx | 15 +- .../channel_members.test.tsx.snap | 34 +- .../channel_members/channel_members.tsx | 9 +- .../channel/details/channel_moderation.tsx | 12 +- .../channel/details/channel_modes.tsx | 10 +- .../channel/details/channel_profile.tsx | 31 +- .../channel/list/channel_list.tsx | 22 +- .../__snapshots__/team_settings.test.tsx.snap | 17 +- .../__snapshots__/team_groups.test.tsx.snap | 17 +- .../__snapshots__/team_modes.test.tsx.snap | 17 +- .../__snapshots__/team_profile.test.tsx.snap | 51 +- .../team/details/team_groups.tsx | 17 +- .../__snapshots__/team_members.test.tsx.snap | 34 +- .../details/team_members/team_members.tsx | 9 +- .../team/details/team_modes.tsx | 10 +- .../team/details/team_profile.tsx | 36 +- .../team/team_settings.tsx | 10 +- .../components/admin_console/text_setting.tsx | 18 +- .../src/components/admin_console/types.ts | 167 +- .../user_autocomplete_setting.tsx | 2 +- .../components/admin_console/validation.tsx | 15 +- .../analytics/activated_users_card/index.tsx | 9 +- .../system_analytics/system_analytics.tsx | 146 +- .../team_analytics/team_analytics.tsx | 88 +- .../channel_header/channel_header.tsx | 10 +- .../components/formatted_markdown_message.tsx | 3 +- .../components/quick_input/quick_input.tsx | 2 +- .../reset_status_modal.test.tsx.snap | 76 +- .../reset_status_modal/reset_status_modal.tsx | 175 +- .../status_dropdown.test.tsx.snap | 90 +- .../status_dropdown/status_dropdown.tsx | 106 +- .../admin_console/admin_panel.test.tsx | 6 +- .../widgets/admin_console/admin_panel.tsx | 42 +- .../admin_panel_togglable.test.tsx | 88 +- .../admin_console/admin_panel_togglable.tsx | 32 +- .../admin_panel_with_button.test.tsx | 47 +- .../admin_console/admin_panel_with_button.tsx | 44 +- .../admin_panel_with_link.test.tsx | 47 +- .../admin_console/admin_panel_with_link.tsx | 55 +- webapp/channels/src/i18n/en.json | 44 +- .../admin_console_plugin_index.test.ts.snap | 12 +- .../src/utils/admin_console_index.tsx | 57 +- .../utils/admin_console_plugin_index.test.ts | 28 +- .../src/utils/admin_console_plugin_index.ts | 45 +- webapp/channels/src/utils/constants.tsx | 34 +- .../src/utils/policy_roles_adapter.test.ts | 2 +- .../src/utils/policy_roles_adapter.ts | 4 +- webapp/platform/types/src/plugins.ts | 8 +- 159 files changed, 7096 insertions(+), 6350 deletions(-) rename webapp/channels/src/components/admin_console/{cluster_settings.jsx => cluster_settings.tsx} (57%) create mode 100644 webapp/channels/src/components/admin_console/permission_schemes_settings/strings/groups.tsx create mode 100644 webapp/channels/src/components/admin_console/permission_schemes_settings/strings/permissions.tsx create mode 100644 webapp/channels/src/components/admin_console/permission_schemes_settings/strings/roles.tsx rename webapp/channels/src/components/admin_console/{schema_admin_settings.jsx => schema_admin_settings.tsx} (67%) create mode 100644 webapp/channels/src/components/admin_console/system_roles/strings.tsx create mode 100644 webapp/channels/src/components/admin_console/system_roles/system_role/strings.tsx diff --git a/webapp/channels/src/components/admin_console/__snapshots__/bleve_settings.test.tsx.snap b/webapp/channels/src/components/admin_console/__snapshots__/bleve_settings.test.tsx.snap index de40f35397..d6e3f18d96 100644 --- a/webapp/channels/src/components/admin_console/__snapshots__/bleve_settings.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/__snapshots__/bleve_settings.test.tsx.snap @@ -26,19 +26,11 @@ exports[`components/BleveSettings should match snapshot, disabled 1`] = ` } helpText={ Learn more about Bleve in our documentation." id="admin.bleve.enableIndexingDescription" values={ Object { - "documentationLink": - - , + "link": [Function], } } /> @@ -285,19 +277,11 @@ exports[`components/BleveSettings should match snapshot, enabled 1`] = ` } helpText={ Learn more about Bleve in our documentation." id="admin.bleve.enableIndexingDescription" values={ Object { - "documentationLink": - - , + "link": [Function], } } /> diff --git a/webapp/channels/src/components/admin_console/__snapshots__/cluster_settings.test.jsx.snap b/webapp/channels/src/components/admin_console/__snapshots__/cluster_settings.test.jsx.snap index e3eb92716f..f66fa9c552 100644 --- a/webapp/channels/src/components/admin_console/__snapshots__/cluster_settings.test.jsx.snap +++ b/webapp/channels/src/components/admin_console/__snapshots__/cluster_settings.test.jsx.snap @@ -16,7 +16,7 @@ exports[`components/ClusterSettings should match snapshot, compression disabled /> - +
@@ -76,7 +76,12 @@ exports[`components/ClusterSettings should match snapshot, compression disabled /> } onChange={[Function]} - placeholder="E.g.: \\"Production\\" or \\"Staging\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"Production\\" or \\"Staging\\"", + "id": "admin.cluster.ClusterNameEx", + } + } setByEnv={false} value="test" /> @@ -95,7 +100,12 @@ exports[`components/ClusterSettings should match snapshot, compression disabled /> } onChange={[Function]} - placeholder="E.g.: \\"app-server-01\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"app-server-01\\"", + "id": "admin.cluster.OverrideHostnameEx", + } + } setByEnv={false} value="" /> @@ -207,7 +217,12 @@ exports[`components/ClusterSettings should match snapshot, compression disabled /> } onChange={[Function]} - placeholder="E.g.: \\"8074\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"8074\\"", + "id": "admin.cluster.GossipPortEx", + } + } setByEnv={false} value={8074} /> @@ -226,7 +241,12 @@ exports[`components/ClusterSettings should match snapshot, compression disabled /> } onChange={[Function]} - placeholder="E.g.: \\"8075\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"8075\\"", + "id": "admin.cluster.StreamingPortEx", + } + } setByEnv={false} /> @@ -289,7 +309,7 @@ exports[`components/ClusterSettings should match snapshot, compression enabled 1 /> - +
@@ -349,7 +369,12 @@ exports[`components/ClusterSettings should match snapshot, compression enabled 1 /> } onChange={[Function]} - placeholder="E.g.: \\"Production\\" or \\"Staging\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"Production\\" or \\"Staging\\"", + "id": "admin.cluster.ClusterNameEx", + } + } setByEnv={false} value="test" /> @@ -368,7 +393,12 @@ exports[`components/ClusterSettings should match snapshot, compression enabled 1 /> } onChange={[Function]} - placeholder="E.g.: \\"app-server-01\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"app-server-01\\"", + "id": "admin.cluster.OverrideHostnameEx", + } + } setByEnv={false} value="" /> @@ -480,7 +510,12 @@ exports[`components/ClusterSettings should match snapshot, compression enabled 1 /> } onChange={[Function]} - placeholder="E.g.: \\"8074\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"8074\\"", + "id": "admin.cluster.GossipPortEx", + } + } setByEnv={false} value={8074} /> @@ -499,7 +534,12 @@ exports[`components/ClusterSettings should match snapshot, compression enabled 1 /> } onChange={[Function]} - placeholder="E.g.: \\"8075\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"8075\\"", + "id": "admin.cluster.StreamingPortEx", + } + } setByEnv={false} /> @@ -562,7 +602,7 @@ exports[`components/ClusterSettings should match snapshot, encryption disabled 1 /> - +
@@ -622,7 +662,12 @@ exports[`components/ClusterSettings should match snapshot, encryption disabled 1 /> } onChange={[Function]} - placeholder="E.g.: \\"Production\\" or \\"Staging\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"Production\\" or \\"Staging\\"", + "id": "admin.cluster.ClusterNameEx", + } + } setByEnv={false} value="test" /> @@ -641,7 +686,12 @@ exports[`components/ClusterSettings should match snapshot, encryption disabled 1 /> } onChange={[Function]} - placeholder="E.g.: \\"app-server-01\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"app-server-01\\"", + "id": "admin.cluster.OverrideHostnameEx", + } + } setByEnv={false} value="" /> @@ -753,7 +803,12 @@ exports[`components/ClusterSettings should match snapshot, encryption disabled 1 /> } onChange={[Function]} - placeholder="E.g.: \\"8074\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"8074\\"", + "id": "admin.cluster.GossipPortEx", + } + } setByEnv={false} value={8074} /> @@ -772,7 +827,12 @@ exports[`components/ClusterSettings should match snapshot, encryption disabled 1 /> } onChange={[Function]} - placeholder="E.g.: \\"8075\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"8075\\"", + "id": "admin.cluster.StreamingPortEx", + } + } setByEnv={false} /> @@ -835,7 +895,7 @@ exports[`components/ClusterSettings should match snapshot, encryption enabled 1` /> - +
@@ -895,7 +955,12 @@ exports[`components/ClusterSettings should match snapshot, encryption enabled 1` /> } onChange={[Function]} - placeholder="E.g.: \\"Production\\" or \\"Staging\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"Production\\" or \\"Staging\\"", + "id": "admin.cluster.ClusterNameEx", + } + } setByEnv={false} value="test" /> @@ -914,7 +979,12 @@ exports[`components/ClusterSettings should match snapshot, encryption enabled 1` /> } onChange={[Function]} - placeholder="E.g.: \\"app-server-01\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"app-server-01\\"", + "id": "admin.cluster.OverrideHostnameEx", + } + } setByEnv={false} value="" /> @@ -1026,7 +1096,12 @@ exports[`components/ClusterSettings should match snapshot, encryption enabled 1` /> } onChange={[Function]} - placeholder="E.g.: \\"8074\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"8074\\"", + "id": "admin.cluster.GossipPortEx", + } + } setByEnv={false} value={8074} /> @@ -1045,7 +1120,12 @@ exports[`components/ClusterSettings should match snapshot, encryption enabled 1` /> } onChange={[Function]} - placeholder="E.g.: \\"8075\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"8075\\"", + "id": "admin.cluster.StreamingPortEx", + } + } setByEnv={false} /> diff --git a/webapp/channels/src/components/admin_console/__snapshots__/database_settings.test.tsx.snap b/webapp/channels/src/components/admin_console/__snapshots__/database_settings.test.tsx.snap index 6c45e21628..1ae6bd6c3f 100644 --- a/webapp/channels/src/components/admin_console/__snapshots__/database_settings.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/__snapshots__/database_settings.test.tsx.snap @@ -101,7 +101,12 @@ exports[`components/DatabaseSettings should match snapshot 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"10\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"10\\"", + "id": "admin.sql.maxConnectionsExample", + } + } setByEnv={false} type="text" value={10} @@ -122,7 +127,12 @@ exports[`components/DatabaseSettings should match snapshot 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"10\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"10\\"", + "id": "admin.sql.maxOpenExample", + } + } setByEnv={false} type="text" value={100} @@ -143,7 +153,12 @@ exports[`components/DatabaseSettings should match snapshot 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"30\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"30\\"", + "id": "admin.sql.queryTimeoutExample", + } + } setByEnv={false} type="text" value={10} @@ -164,7 +179,12 @@ exports[`components/DatabaseSettings should match snapshot 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"3600000\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"3600000\\"", + "id": "admin.sql.connMaxLifetimeExample", + } + } setByEnv={false} type="text" value={10} @@ -185,7 +205,12 @@ exports[`components/DatabaseSettings should match snapshot 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"300000\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"300000\\"", + "id": "admin.sql.connMaxIdleTimeExample", + } + } setByEnv={false} type="text" value={20} @@ -211,7 +236,12 @@ exports[`components/DatabaseSettings should match snapshot 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"3\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"3\\"", + "id": "admin.service.minimumHashtagLengthExample", + } + } setByEnv={false} type="text" value={10} diff --git a/webapp/channels/src/components/admin_console/__snapshots__/elasticsearch_settings.test.tsx.snap b/webapp/channels/src/components/admin_console/__snapshots__/elasticsearch_settings.test.tsx.snap index 6a9c616cdf..d4739229ba 100644 --- a/webapp/channels/src/components/admin_console/__snapshots__/elasticsearch_settings.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/__snapshots__/elasticsearch_settings.test.tsx.snap @@ -26,19 +26,11 @@ exports[`components/ElasticSearchSettings should match snapshot, disabled 1`] = } helpText={ Learn more about Elasticsearch in our documentation." id="admin.elasticsearch.enableIndexingDescription" values={ Object { - "documentationLink": - - , + "link": [Function], } } /> @@ -64,19 +56,11 @@ exports[`components/ElasticSearchSettings should match snapshot, disabled 1`] = disabled={true} helpText={ - - , + "link": [Function], } } /> @@ -89,7 +73,12 @@ exports[`components/ElasticSearchSettings should match snapshot, disabled 1`] = /> } onChange={[Function]} - placeholder="E.g.: \\"https://elasticsearch.example.org:9200\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"https://elasticsearch.example.org:9200\\"", + "id": "admin.elasticsearch.connectionUrlExample", + } + } setByEnv={false} value="test" /> @@ -109,7 +98,12 @@ exports[`components/ElasticSearchSettings should match snapshot, disabled 1`] = /> } onChange={[Function]} - placeholder="E.g.: \\"./elasticsearch/ca.pem\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"./elasticsearch/ca.pem\\"", + "id": "admin.elasticsearch.caExample", + } + } setByEnv={false} value="test.ca" /> @@ -129,7 +123,12 @@ exports[`components/ElasticSearchSettings should match snapshot, disabled 1`] = /> } onChange={[Function]} - placeholder="E.g.: \\"./elasticsearch/client-cert.pem\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"./elasticsearch/client-cert.pem\\"", + "id": "admin.elasticsearch.clientCertExample", + } + } setByEnv={false} value="test.crt" /> @@ -149,7 +148,12 @@ exports[`components/ElasticSearchSettings should match snapshot, disabled 1`] = /> } onChange={[Function]} - placeholder="E.g.: \\"./elasticsearch/client-key.pem\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"./elasticsearch/client-key.pem\\"", + "id": "admin.elasticsearch.clientKeyExample", + } + } setByEnv={false} value="test.key" /> @@ -200,7 +204,12 @@ exports[`components/ElasticSearchSettings should match snapshot, disabled 1`] = /> } onChange={[Function]} - placeholder="E.g.: \\"elastic\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"elastic\\"", + "id": "admin.elasticsearch.usernameExample", + } + } setByEnv={false} value="test" /> @@ -220,7 +229,12 @@ exports[`components/ElasticSearchSettings should match snapshot, disabled 1`] = /> } onChange={[Function]} - placeholder="E.g.: \\"yourpassword\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"yourpassword\\"", + "id": "admin.elasticsearch.password", + } + } setByEnv={false} value="test" /> @@ -378,7 +392,12 @@ exports[`components/ElasticSearchSettings should match snapshot, disabled 1`] = /> } onChange={[Function]} - placeholder="E.g.: .opendistro*,.security*" + placeholder={ + Object { + "defaultMessage": "E.g.: .opendistro*,.security*", + "id": "admin.elasticsearch.ignoredPurgeIndexesDescription.example", + } + } setByEnv={false} /> Learn more about Elasticsearch in our documentation." id="admin.elasticsearch.enableIndexingDescription" values={ Object { - "documentationLink": - - , + "link": [Function], } } /> @@ -551,19 +562,11 @@ exports[`components/ElasticSearchSettings should match snapshot, enabled 1`] = ` disabled={false} helpText={ - - , + "link": [Function], } } /> @@ -576,7 +579,12 @@ exports[`components/ElasticSearchSettings should match snapshot, enabled 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"https://elasticsearch.example.org:9200\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"https://elasticsearch.example.org:9200\\"", + "id": "admin.elasticsearch.connectionUrlExample", + } + } setByEnv={false} value="test" /> @@ -596,7 +604,12 @@ exports[`components/ElasticSearchSettings should match snapshot, enabled 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"./elasticsearch/ca.pem\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"./elasticsearch/ca.pem\\"", + "id": "admin.elasticsearch.caExample", + } + } setByEnv={false} value="test.ca" /> @@ -616,7 +629,12 @@ exports[`components/ElasticSearchSettings should match snapshot, enabled 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"./elasticsearch/client-cert.pem\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"./elasticsearch/client-cert.pem\\"", + "id": "admin.elasticsearch.clientCertExample", + } + } setByEnv={false} value="test.crt" /> @@ -636,7 +654,12 @@ exports[`components/ElasticSearchSettings should match snapshot, enabled 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"./elasticsearch/client-key.pem\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"./elasticsearch/client-key.pem\\"", + "id": "admin.elasticsearch.clientKeyExample", + } + } setByEnv={false} value="test.key" /> @@ -687,7 +710,12 @@ exports[`components/ElasticSearchSettings should match snapshot, enabled 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"elastic\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"elastic\\"", + "id": "admin.elasticsearch.usernameExample", + } + } setByEnv={false} value="test" /> @@ -707,7 +735,12 @@ exports[`components/ElasticSearchSettings should match snapshot, enabled 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"yourpassword\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"yourpassword\\"", + "id": "admin.elasticsearch.password", + } + } setByEnv={false} value="test" /> @@ -864,7 +897,12 @@ exports[`components/ElasticSearchSettings should match snapshot, enabled 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: .opendistro*,.security*" + placeholder={ + Object { + "defaultMessage": "E.g.: .opendistro*,.security*", + "id": "admin.elasticsearch.ignoredPurgeIndexesDescription.example", + } + } setByEnv={false} /> } onChange={[Function]} - placeholder="E.g.: \\"02:00\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"02:00\\"", + "id": "admin.complianceExport.exportJobStartTime.example", + } + } setByEnv={false} value="01:00" /> @@ -76,11 +81,13 @@ exports[`components/MessageExportSettings should match snapshot, disabled, actia disabled={true} helpText={ @@ -241,7 +248,12 @@ exports[`components/MessageExportSettings should match snapshot, disabled, globa /> } onChange={[Function]} - placeholder="E.g.: \\"02:00\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"02:00\\"", + "id": "admin.complianceExport.exportJobStartTime.example", + } + } setByEnv={false} value="01:00" /> @@ -249,11 +261,13 @@ exports[`components/MessageExportSettings should match snapshot, disabled, globa disabled={true} helpText={ @@ -339,7 +353,12 @@ exports[`components/MessageExportSettings should match snapshot, disabled, globa /> } onChange={[Function]} - placeholder="E.g.: \\"globalRelayUser\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"globalRelayUser\\"", + "id": "admin.complianceExport.globalRelaySMTPUsername.example", + } + } setByEnv={false} value="globalRelayUser" /> @@ -359,7 +378,12 @@ exports[`components/MessageExportSettings should match snapshot, disabled, globa /> } onChange={[Function]} - placeholder="E.g.: \\"globalRelayPassword\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"globalRelayPassword\\"", + "id": "admin.complianceExport.globalRelaySMTPPassword.example", + } + } setByEnv={false} value="globalRelayPassword" /> @@ -379,7 +403,12 @@ exports[`components/MessageExportSettings should match snapshot, disabled, globa /> } onChange={[Function]} - placeholder="E.g.: \\"globalrelay@mattermost.com\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"globalrelay@mattermost.com\\"", + "id": "admin.complianceExport.globalRelayEmailAddress.example", + } + } setByEnv={false} value="globalRelay@mattermost.com" /> @@ -513,7 +542,12 @@ exports[`components/MessageExportSettings should match snapshot, enabled, actian /> } onChange={[Function]} - placeholder="E.g.: \\"02:00\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"02:00\\"", + "id": "admin.complianceExport.exportJobStartTime.example", + } + } setByEnv={false} value="01:00" /> @@ -521,11 +555,13 @@ exports[`components/MessageExportSettings should match snapshot, enabled, actian disabled={false} helpText={ @@ -686,7 +722,12 @@ exports[`components/MessageExportSettings should match snapshot, enabled, global /> } onChange={[Function]} - placeholder="E.g.: \\"02:00\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"02:00\\"", + "id": "admin.complianceExport.exportJobStartTime.example", + } + } setByEnv={false} value="01:00" /> @@ -694,11 +735,13 @@ exports[`components/MessageExportSettings should match snapshot, enabled, global disabled={false} helpText={ @@ -784,7 +827,12 @@ exports[`components/MessageExportSettings should match snapshot, enabled, global /> } onChange={[Function]} - placeholder="E.g.: \\"globalRelayUser\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"globalRelayUser\\"", + "id": "admin.complianceExport.globalRelaySMTPUsername.example", + } + } setByEnv={false} value="globalRelayUser" /> @@ -804,7 +852,12 @@ exports[`components/MessageExportSettings should match snapshot, enabled, global /> } onChange={[Function]} - placeholder="E.g.: \\"globalRelayPassword\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"globalRelayPassword\\"", + "id": "admin.complianceExport.globalRelaySMTPPassword.example", + } + } setByEnv={false} value="globalRelayPassword" /> @@ -824,7 +877,12 @@ exports[`components/MessageExportSettings should match snapshot, enabled, global /> } onChange={[Function]} - placeholder="E.g.: \\"globalrelay@mattermost.com\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"globalrelay@mattermost.com\\"", + "id": "admin.complianceExport.globalRelayEmailAddress.example", + } + } setByEnv={false} value="globalRelay@mattermost.com" /> diff --git a/webapp/channels/src/components/admin_console/__snapshots__/push_settings.test.tsx.snap b/webapp/channels/src/components/admin_console/__snapshots__/push_settings.test.tsx.snap index 660cf32064..96976b7e49 100644 --- a/webapp/channels/src/components/admin_console/__snapshots__/push_settings.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/__snapshots__/push_settings.test.tsx.snap @@ -122,7 +122,12 @@ exports[`components/PushSettings should match snapshot, licensed 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"https://push-test.mattermost.com\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"https://push-test.mattermost.com\\"", + "id": "admin.email.pushServerEx", + } + } setByEnv={false} value="https://push.mattermost.com" /> @@ -141,7 +146,12 @@ exports[`components/PushSettings should match snapshot, licensed 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"1000\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"1000\\"", + "id": "admin.team.maxNotificationsPerChannelExample", + } + } setByEnv={false} type="number" value={1000} @@ -256,7 +266,12 @@ exports[`components/PushSettings should match snapshot, unlicensed 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"https://push-test.mattermost.com\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"https://push-test.mattermost.com\\"", + "id": "admin.email.pushServerEx", + } + } setByEnv={false} value="https://push.mattermost.com" /> @@ -275,7 +290,12 @@ exports[`components/PushSettings should match snapshot, unlicensed 1`] = ` /> } onChange={[Function]} - placeholder="E.g.: \\"1000\\"" + placeholder={ + Object { + "defaultMessage": "E.g.: \\"1000\\"", + "id": "admin.team.maxNotificationsPerChannelExample", + } + } setByEnv={false} type="number" value={1000} diff --git a/webapp/channels/src/components/admin_console/__snapshots__/schema_admin_settings.test.jsx.snap b/webapp/channels/src/components/admin_console/__snapshots__/schema_admin_settings.test.jsx.snap index 494e17918a..0cb6e2bbdf 100644 --- a/webapp/channels/src/components/admin_console/__snapshots__/schema_admin_settings.test.jsx.snap +++ b/webapp/channels/src/components/admin_console/__snapshots__/schema_admin_settings.test.jsx.snap @@ -2,6 +2,7 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with custom component 1`] = ` `; @@ -44,12 +92,7 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with
- - - + config
@@ -69,16 +112,14 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with footer={null} helpText={ } id="FirstSettings.settinga" key="Config_text_FirstSettings.settinga" - label="Setting One" + label="label-a" onChange={[Function]} - placeholder="e.g. some setting" + placeholder="placeholder-a" setByEnv={false} type="text" value="fsdsdg" @@ -93,14 +134,12 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with } helpText={ } id="FirstSettings.settingb" key="Config_bool_FirstSettings.settingb" - label="Setting Two" + label="label-b" onChange={[Function]} setByEnv={false} trueText={ @@ -109,20 +148,18 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with id="admin.true" /> } - value={false} + value={true} /> } id="FirstSettings.settingc" key="Config_dropdown_FirstSettings.settingc" - label="Setting Three" + label="label-c" onChange={[Function]} setByEnv={false} value="option3" @@ -147,14 +184,12 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with disabled={false} helpText={ } id="SecondSettings.settingd" key="Config_radio_SecondSettings.settingd" - label="Setting Four" + label="label-d" onChange={[Function]} setByEnv={false} value="option1" @@ -179,16 +214,14 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with disabled={false} helpText={ } id="SecondSettings.settinge" key="Config_generated_SecondSettings.settinge" - label="Setting Five" + label="label-e" onChange={[Function]} - placeholder="e.g. 47KyfOxtk5+ovi1MDHFyzMDHIA6esMWb" + placeholder="placeholder-e" regenerateHelpText="This is help text for the regenerate button." regenerateText={ } id="SecondSettings.settingf" key="Config_userautocomplete_SecondSettings.settingf" - label="Setting Six" + label="label-f" onChange={[Function]} - placeholder="Type a username here" + placeholder="placeholder-f" value="3xz3r6n7dtbbmgref3yw4zg7sr" /> } id="SecondSettings.settingg" key="Config_text_SecondSettings.settingg" - label="Setting Seven" + label="label-g" onChange={[Function]} - placeholder="e.g. some setting" + placeholder="placeholder-g" setByEnv={false} type="number" value={7} @@ -239,16 +268,14 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with footer={null} helpText={ } id="SecondSettings.settingh" key="Config_text_SecondSettings.settingh" - label="Setting Eight" + label="label-h" onChange={[Function]} - placeholder="e.g. some setting" + placeholder="placeholder-h" setByEnv={false} type="number" value={10} @@ -261,10 +288,9 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with className="banner__content" > - + + label-h +
@@ -272,14 +298,12 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with disabled={false} helpText={ } id="SecondSettings.settingi" key="Config_language_SecondSettings.settingi" - label="Setting Nine" + label="label-i" onChange={[Function]} setByEnv={false} value="de" @@ -402,20 +426,13 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with disabled={false} helpText={ } id="SecondSettings.settingj" key="Config_language_SecondSettings.settingj" - label="Setting Nine" - noResultText={ - - } + label="label-j" + noResultText="no-result-j" onChange={[Function]} selected={Array []} setByEnv={false} @@ -537,21 +554,14 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with - Setting Eleven + label-k } disabled={false} - errorMessage={ - Object { - "defaultMessage": "Reload unsuccessful: {error}", - "id": "admin.reload.reloadFail", - } - } + errorMessage="admin.reload.reloadFail" helpText={ } id="SecondSettings.settingk" @@ -577,14 +587,12 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with } helpText={ } id="FirstSettings.settingl" key="Config_bool_FirstSettings.settingl" - label="Setting Twelve" + label="label-l" onChange={[Function]} setByEnv={true} trueText={ @@ -599,14 +607,12 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with disabled={false} helpText={ } id="FirstSettings.settingm" key="Config_text_FirstSettings.settingm" - label="Setting Thirteen" + label="label-m" onChange={[Function]} value="" /> @@ -635,9 +641,15 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with } } disabled={false} - helpText={null} + helpText={ + + + + } id="custom" key="Config_custom_custom" + label="" + license={Object {}} onChange={[Function]} registerSaveAction={[Function]} setByEnv={false} @@ -646,16 +658,10 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with unRegisterSaveAction={[Function]} /> - } + createJobButtonText="label-l" createJobHelpText={ - } disabled={false} @@ -673,14 +679,12 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with } helpText={ } id="EscapedSettings.com+example+setting.a" key="Config_bool_EscapedSettings.com+example+setting.a" - label="Escaped Setting A" + label="escaped-label-a" onChange={[Function]} setByEnv={false} trueText={ @@ -731,7 +735,6 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with
; - config?: DeepPartial; - environmentConfig?: Partial; - setNavigationBlocked?: () => void; - roles?: Record; - editRole?: (role: Role) => void; - updateConfig?: (config: AdminConfig) => Promise; + license: ClientLicense; + config: Partial; + environmentConfig: Partial; + setNavigationBlocked: (blocked: boolean) => void; + roles: Record; + editRole: (role: Role) => void; + updateConfig: (config: AdminConfig) => Promise; cloud: CloudState; isCurrentUserSystemAdmin: boolean; } diff --git a/webapp/channels/src/components/admin_console/admin_definition.tsx b/webapp/channels/src/components/admin_console/admin_definition.tsx index d3719d80c1..81a46e67b2 100644 --- a/webapp/channels/src/components/admin_console/admin_definition.tsx +++ b/webapp/channels/src/components/admin_console/admin_definition.tsx @@ -4,13 +4,13 @@ /* eslint-disable max-lines */ import React from 'react'; -import {FormattedMessage} from 'react-intl'; +import type {MessageDescriptor} from 'react-intl'; +import {FormattedMessage, defineMessage, defineMessages} from 'react-intl'; import {AccountMultipleOutlineIcon, ChartBarIcon, CogOutlineIcon, CreditCardOutlineIcon, FlaskOutlineIcon, FormatListBulletedIcon, InformationOutlineIcon, PowerPlugOutlineIcon, ServerVariantIcon, ShieldOutlineIcon, SitemapIcon} from '@mattermost/compass-icons/components'; import type {CloudState, Product} from '@mattermost/types/cloud'; import type {AdminConfig, ClientLicense} from '@mattermost/types/config'; import type {Job} from '@mattermost/types/jobs'; -import type {DeepPartial} from '@mattermost/types/utilities'; import {RESOURCE_KEYS} from 'mattermost-redux/constants/permissions_sysconsole'; @@ -28,34 +28,38 @@ import {trackEvent} from 'actions/telemetry_actions.jsx'; import CustomPluginSettings from 'components/admin_console/custom_plugin_settings'; import PluginManagement from 'components/admin_console/plugin_management'; import SystemAnalytics from 'components/analytics/system_analytics'; +import {searchableStrings as systemAnalyticsSearchableStrings} from 'components/analytics/system_analytics/system_analytics'; import TeamAnalytics from 'components/analytics/team_analytics'; +import {searchableStrings as teamAnalyticsSearchableStrings} from 'components/analytics/team_analytics/team_analytics'; import ExternalLink from 'components/external_link'; import RestrictedIndicator from 'components/widgets/menu/menu_items/restricted_indicator'; import {Constants, CloudProducts, LicenseSkus, AboutLinks, DocLinks, DeveloperLinks} from 'utils/constants'; -import {t} from 'utils/i18n'; import {isCloudLicense} from 'utils/license_utils'; import {getSiteURL} from 'utils/url'; import * as DefinitionConstants from './admin_definition_constants'; import Audits from './audits'; -import BillingHistory from './billing/billing_history'; -import BillingSubscriptions from './billing/billing_subscriptions'; -import CompanyInfo from './billing/company_info'; +import {searchableStrings as auditSearchableStrings} from './audits/audits'; +import BillingHistory, {searchableStrings as billingHistorySearchableStrings} from './billing/billing_history'; +import BillingSubscriptions, {searchableStrings as billingSubscriptionSearchableStrings} from './billing/billing_subscriptions'; +import CompanyInfo, {searchableStrings as billingCompanyInfoSearchableStrings} from './billing/company_info'; import CompanyInfoEdit from './billing/company_info_edit'; -import PaymentInfo from './billing/payment_info'; +import PaymentInfo, {searchableStrings as billingPaymentInfoSearchableStrings} from './billing/payment_info'; import PaymentInfoEdit from './billing/payment_info_edit'; -import BleveSettings from './bleve_settings'; +import BleveSettings, {searchableStrings as bleveSearchableStrings} from './bleve_settings'; import BrandImageSetting from './brand_image_setting/brand_image_setting'; -import ClusterSettings from './cluster_settings.jsx'; +import ClusterSettings, {searchableStrings as clusterSearchableStrings} from './cluster_settings'; import CustomEnableDisableGuestAccountsSetting from './custom_enable_disable_guest_accounts_setting'; import CustomTermsOfServiceSettings from './custom_terms_of_service_settings'; +import {messages as customTermsOfServiceMessages, searchableStrings as customTermsOfServiceSearchableStrings} from './custom_terms_of_service_settings/custom_terms_of_service_settings'; import CustomURLSchemesSetting from './custom_url_schemes_setting'; import DataRetentionSettings from './data_retention_settings'; import CustomDataRetentionForm from './data_retention_settings/custom_policy_form'; +import {searchableStrings as dataRetentionSearchableStrings} from './data_retention_settings/data_retention_settings'; import GlobalDataRetentionForm from './data_retention_settings/global_policy_form'; -import DatabaseSettings from './database_settings'; -import ElasticSearchSettings from './elasticsearch_settings'; +import DatabaseSettings, {searchableStrings as databaseSearchableStrings} from './database_settings'; +import ElasticSearchSettings, {searchableStrings as elasticSearchSearchableStrings} from './elasticsearch_settings'; import { LDAPFeatureDiscovery, SAMLFeatureDiscovery, @@ -69,24 +73,29 @@ import { SystemRolesFeatureDiscovery, GroupsFeatureDiscovery, } from './feature_discovery/features'; -import FeatureFlags from './feature_flags'; +import FeatureFlags, {messages as featureFlagsMessages} from './feature_flags'; import GroupDetails from './group_settings/group_details'; import GroupSettings from './group_settings/group_settings'; import IPFiltering from './ip_filtering'; import LicenseSettings from './license_settings'; -import MessageExportSettings from './message_export_settings'; +import {searchableStrings as licenseSettingsSearchableStrings} from './license_settings/license_settings'; +import MessageExportSettings, {searchableStrings as messageExportSearchableStrings} from './message_export_settings'; import OpenIdConvert from './openid_convert'; -import PasswordSettings from './password_settings'; +import PasswordSettings, {searchableStrings as passwordSearchableStrings} from './password_settings'; import PermissionSchemesSettings from './permission_schemes_settings'; +import {searchableStrings as PermissionSchemeSearchableStrings} from './permission_schemes_settings/permission_schemes_settings'; import PermissionSystemSchemeSettings from './permission_schemes_settings/permission_system_scheme_settings'; import PermissionTeamSchemeSettings from './permission_schemes_settings/permission_team_scheme_settings'; -import PushNotificationsSettings from './push_settings'; +import {searchableStrings as pluginManagementSearchableStrings} from './plugin_management/plugin_management'; +import PushNotificationsSettings, {searchableStrings as pushSearchableStrings} from './push_settings'; import ServerLogs from './server_logs'; -import SessionLengthSettings from './session_length_settings'; +import {searchableStrings as serverLogsSearchableStrings} from './server_logs/logs'; +import SessionLengthSettings, {searchableStrings as sessionLengthSearchableStrings} from './session_length_settings'; import SystemRoles from './system_roles'; import SystemRole from './system_roles/system_role'; import SystemUserDetail from './system_user_detail'; import SystemUsers from './system_users'; +import {searchableStrings as systemUsersSearchableStrings} from './system_users/system_users'; import ChannelSettings from './team_channel_settings/channel'; import ChannelDetails from './team_channel_settings/channel/details'; import TeamSettings from './team_channel_settings/team'; @@ -181,10 +190,10 @@ const SAML_SETTINGS_CANONICAL_ALGORITHM_C14N11 = 'Canonical1.1'; // - fileType: A list of extensions separated by ",". E.g. ".jpg,.png,.gif". export const it = { - not: (func: Check) => (config: DeepPartial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState, isSystemAdmin?: boolean) => { + not: (func: Check) => (config: Partial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState, isSystemAdmin?: boolean) => { return typeof func === 'function' ? !func(config, state, license, enterpriseReady, consoleAccess, cloud, isSystemAdmin) : !func; }, - all: (...funcs: Check[]) => (config: DeepPartial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState, isSystemAdmin?: boolean) => { + all: (...funcs: Check[]) => (config: Partial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState, isSystemAdmin?: boolean) => { for (const func of funcs) { if (typeof func === 'function' ? !func(config, state, license, enterpriseReady, consoleAccess, cloud, isSystemAdmin) : !func) { return false; @@ -192,7 +201,7 @@ export const it = { } return true; }, - any: (...funcs: Check[]) => (config: DeepPartial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState, isSystemAdmin?: boolean) => { + any: (...funcs: Check[]) => (config: Partial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState, isSystemAdmin?: boolean) => { for (const func of funcs) { if (typeof func === 'function' ? func(config, state, license, enterpriseReady, consoleAccess, cloud, isSystemAdmin) : func) { return true; @@ -200,20 +209,20 @@ export const it = { } return false; }, - stateMatches: (key: string, regex: RegExp) => (config: DeepPartial, state: any) => state[key].match(regex), - stateEquals: (key: string, value: any) => (config: DeepPartial, state: any) => state[key] === value, - stateIsTrue: (key: string) => (config: DeepPartial, state: any) => Boolean(state[key]), - stateIsFalse: (key: string) => (config: DeepPartial, state: any) => !state[key], - configIsTrue: (group: keyof DeepPartial, setting: string) => (config: DeepPartial) => Boolean((config[group] as any)?.[setting]), - configIsFalse: (group: keyof DeepPartial, setting: string) => (config: DeepPartial) => !(config[group] as any)?.[setting], - configContains: (group: keyof DeepPartial, setting: string, word: string) => (config: DeepPartial) => Boolean((config[group] as any)?.[setting]?.includes(word)), - enterpriseReady: (config: DeepPartial, state: any, license?: ClientLicense, enterpriseReady?: boolean) => Boolean(enterpriseReady), - licensed: (config: DeepPartial, state: any, license?: ClientLicense) => license?.IsLicensed === 'true', - cloudLicensed: (config: DeepPartial, state: any, license?: ClientLicense) => Boolean(license?.IsLicenced && isCloudLicense(license)), - licensedForFeature: (feature: string) => (config: DeepPartial, state: any, license?: ClientLicense) => Boolean(license?.IsLicensed && license[feature] === 'true'), - licensedForSku: (skuName: string) => (config: DeepPartial, state: any, license?: ClientLicense) => Boolean(license?.IsLicensed && license.SkuShortName === skuName), - licensedForCloudStarter: (config: DeepPartial, state: any, license?: ClientLicense) => Boolean(license?.IsLicensed && isCloudLicense(license) && license.SkuShortName === LicenseSkus.Starter), - hidePaymentInfo: (config: DeepPartial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState) => { + stateMatches: (key: string, regex: RegExp) => (config: Partial, state: any) => state[key].match(regex), + stateEquals: (key: string, value: any) => (config: Partial, state: any) => state[key] === value, + stateIsTrue: (key: string) => (config: Partial, state: any) => Boolean(state[key]), + stateIsFalse: (key: string) => (config: Partial, state: any) => !state[key], + configIsTrue: (group: keyof Partial, setting: string) => (config: Partial) => Boolean((config[group] as any)?.[setting]), + configIsFalse: (group: keyof Partial, setting: string) => (config: Partial) => !(config[group] as any)?.[setting], + configContains: (group: keyof Partial, setting: string, word: string) => (config: Partial) => Boolean((config[group] as any)?.[setting]?.includes(word)), + enterpriseReady: (config: Partial, state: any, license?: ClientLicense, enterpriseReady?: boolean) => Boolean(enterpriseReady), + licensed: (config: Partial, state: any, license?: ClientLicense) => license?.IsLicensed === 'true', + cloudLicensed: (config: Partial, state: any, license?: ClientLicense) => Boolean(license?.IsLicenced && isCloudLicense(license)), + licensedForFeature: (feature: string) => (config: Partial, state: any, license?: ClientLicense) => Boolean(license?.IsLicensed && license[feature] === 'true'), + licensedForSku: (skuName: string) => (config: Partial, state: any, license?: ClientLicense) => Boolean(license?.IsLicensed && license.SkuShortName === skuName), + licensedForCloudStarter: (config: Partial, state: any, license?: ClientLicense) => Boolean(license?.IsLicensed && isCloudLicense(license) && license.SkuShortName === LicenseSkus.Starter), + hidePaymentInfo: (config: Partial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState) => { if (!cloud) { return true; } @@ -223,19 +232,19 @@ export const it = { } return cloud?.subscription?.is_free_trial === 'true'; }, - userHasReadPermissionOnResource: (key: string) => (config: DeepPartial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess) => (consoleAccess?.read as any)?.[key], + userHasReadPermissionOnResource: (key: string) => (config: Partial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess) => (consoleAccess?.read as any)?.[key], userHasReadPermissionOnSomeResources: (key: string | {[key: string]: string}) => Object.values(key).some((resource) => it.userHasReadPermissionOnResource(resource)), - userHasWritePermissionOnResource: (key: string) => (config: DeepPartial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess) => (consoleAccess?.write as any)?.[key], - isSystemAdmin: (config: DeepPartial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState, isSystemAdmin?: boolean) => Boolean(isSystemAdmin), + userHasWritePermissionOnResource: (key: string) => (config: Partial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess) => (consoleAccess?.write as any)?.[key], + isSystemAdmin: (config: Partial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState, isSystemAdmin?: boolean) => Boolean(isSystemAdmin), }; export const validators = { - isRequired: (text: string, textDefault: string) => (value: string) => new ValidationResult(Boolean(value), text, textDefault), - minValue: (min: number, text: string, textDefault: string) => (value: number) => new ValidationResult((value >= min), text, textDefault), - maxValue: (max: number, text: string, textDefault: string) => (value: number) => new ValidationResult((value <= max), text, textDefault), + isRequired: (text: MessageDescriptor | string) => (value: string) => new ValidationResult(Boolean(value), text), + minValue: (min: number, text: MessageDescriptor | string) => (value: number) => new ValidationResult((value >= min), text), + maxValue: (max: number, text: MessageDescriptor | string) => (value: number) => new ValidationResult((value <= max), text), }; -const usesLegacyOauth = (config: DeepPartial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState) => { +const usesLegacyOauth = (config: Partial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState) => { if (!config.GitLabSettings || !config.GoogleSettings || !config.Office365Settings) { return false; } @@ -271,15 +280,19 @@ const getRestrictedIndicator = (displayBlocked = false, minimumPlanRequiredForFe useModal={false} blocked={displayBlocked || !(cloud?.subscription?.is_free_trial === 'true')} minimumPlanRequiredForFeature={minimumPlanRequiredForFeature} - tooltipMessageBlocked={{ - id: t('admin.sidebar.restricted_indicator.tooltip.message.blocked'), + tooltipMessageBlocked={defineMessage({ + id: 'admin.sidebar.restricted_indicator.tooltip.message.blocked', defaultMessage: 'This is {article} {minimumPlanRequiredForFeature} feature, available with an upgrade or free {trialLength}-day trial', - }} + })} /> ), shouldDisplay: (license: ClientLicense, subscriptionProduct: Product|undefined) => displayBlocked || (isCloudLicense(license) && subscriptionProduct?.sku === CloudProducts.STARTER), }); +const adminDefinitionMessages = defineMessages({ + data_retention_title: {id: 'admin.data_retention.title', defaultMessage: 'Data Retention Policy'}, + ip_filtering_title: {id: 'admin.sidebar.ip_filtering', defaultMessage: 'IP Filtering'}, +}); const AdminDefinition: AdminDefinitionType = { about: { icon: ( @@ -288,8 +301,7 @@ const AdminDefinition: AdminDefinitionType = { color={'currentColor'} /> ), - sectionTitle: t('admin.sidebar.about'), - sectionTitleDefault: 'About', + sectionTitle: defineMessage({id: 'admin.sidebar.about', defaultMessage: 'About'}), isHidden: it.any( it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), it.not(it.userHasReadPermissionOnSomeResources(RESOURCE_KEYS.ABOUT)), @@ -297,18 +309,8 @@ const AdminDefinition: AdminDefinitionType = { subsections: { license: { url: 'about/license', - title: t('admin.sidebar.license'), - title_default: 'Edition and License', - searchableStrings: [ - 'admin.license.title', - 'admin.license.uploadDesc', - 'admin.license.keyRemove', - 'admin.license.edition', - 'admin.license.type', - 'admin.license.key', - 'Mattermost Enterprise Edition. Unlock enterprise features in this software through the purchase of a subscription from ', - 'This software is offered under a commercial license.\n\nSee ENTERPRISE-EDITION-LICENSE.txt in your root install directory for details. See NOTICE.txt for information about open source software used in this system.', - ], + title: defineMessage({id: 'admin.sidebar.license', defaultMessage: 'Edition and License'}), + searchableStrings: licenseSettingsSearchableStrings, isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.ABOUT.EDITION_AND_LICENSE)), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ABOUT.EDITION_AND_LICENSE)), schema: { @@ -325,8 +327,7 @@ const AdminDefinition: AdminDefinitionType = { color={'currentColor'} /> ), - sectionTitle: t('admin.sidebar.billing'), - sectionTitleDefault: 'Billing & Account', + sectionTitle: defineMessage({id: 'admin.sidebar.billing', defaultMessage: 'Billing & Account'}), isHidden: it.any( it.not(it.enterpriseReady), it.not(it.userHasReadPermissionOnResource('billing')), @@ -339,13 +340,8 @@ const AdminDefinition: AdminDefinitionType = { subsections: { subscription: { url: 'billing/subscription', - title: t('admin.sidebar.subscription'), - title_default: 'Subscription', - searchableStrings: [ - 'admin.billing.subscription.title', - 'admin.billing.subscription.deleteWorkspaceSection.title', - 'admin.billing.subscription.deleteWorkspaceModal.deleteButton', - ], + title: defineMessage({id: 'admin.sidebar.subscription', defaultMessage: 'Subscription'}), + searchableStrings: billingSubscriptionSearchableStrings, schema: { id: 'BillingSubscriptions', component: BillingSubscriptions, @@ -357,11 +353,8 @@ const AdminDefinition: AdminDefinitionType = { }, billing_history: { url: 'billing/billing_history', - title: t('admin.sidebar.billing_history'), - title_default: 'Billing History', - searchableStrings: [ - 'admin.billing.history.title', - ], + title: defineMessage({id: 'admin.sidebar.billing_history', defaultMessage: 'Billing History'}), + searchableStrings: billingHistorySearchableStrings, schema: { id: 'BillingHistory', component: BillingHistory, @@ -370,11 +363,8 @@ const AdminDefinition: AdminDefinitionType = { }, company_info: { url: 'billing/company_info', - title: t('admin.sidebar.company_info'), - title_default: 'Company Information', - searchableStrings: [ - 'admin.billing.company_info.title', - ], + title: defineMessage({id: 'admin.sidebar.company_info', defaultMessage: 'Company Information'}), + searchableStrings: billingCompanyInfoSearchableStrings, schema: { id: 'CompanyInfo', component: CompanyInfo, @@ -397,17 +387,14 @@ const AdminDefinition: AdminDefinitionType = { }, payment_info: { url: 'billing/payment_info', - title: t('admin.sidebar.payment_info'), - title_default: 'Payment Information', + title: defineMessage({id: 'admin.sidebar.payment_info', defaultMessage: 'Payment Information'}), isHidden: it.any( it.hidePaymentInfo, // cloud only view it.not(it.licensedForFeature('Cloud')), ), - searchableStrings: [ - 'admin.billing.payment_info.title', - ], + searchableStrings: billingPaymentInfoSearchableStrings, schema: { id: 'PaymentInfo', component: PaymentInfo, @@ -431,14 +418,12 @@ const AdminDefinition: AdminDefinitionType = { color={'currentColor'} /> ), - sectionTitle: t('admin.sidebar.reporting'), - sectionTitleDefault: 'Reporting', + sectionTitle: defineMessage({id: 'admin.sidebar.reporting', defaultMessage: 'Reporting'}), isHidden: it.not(it.userHasReadPermissionOnSomeResources(RESOURCE_KEYS.REPORTING)), subsections: { workspace_optimization: { url: 'reporting/workspace_optimization', - title: t('admin.sidebar.workspaceOptimization'), - title_default: 'Workspace Optimization', + title: defineMessage({id: 'admin.sidebar.workspaceOptimization', defaultMessage: 'Workspace Optimization'}), schema: { id: 'WorkspaceOptimizationDashboard', component: WorkspaceOptimizationDashboard, @@ -448,27 +433,8 @@ const AdminDefinition: AdminDefinitionType = { }, system_analytics: { url: 'reporting/system_analytics', - title: t('admin.sidebar.siteStatistics'), - title_default: 'Site Statistics', - searchableStrings: [ - 'analytics.system.title', - 'analytics.system.totalPosts', - 'analytics.system.activeUsers', - 'analytics.system.totalSessions', - 'analytics.system.totalCommands', - 'analytics.system.totalIncomingWebhooks', - 'analytics.system.totalOutgoingWebhooks', - 'analytics.system.totalWebsockets', - 'analytics.system.totalMasterDbConnections', - 'analytics.system.totalReadDbConnections', - 'analytics.system.postTypes', - 'analytics.system.channelTypes', - 'analytics.system.totalUsers', - 'analytics.system.totalTeams', - 'analytics.system.totalChannels', - 'analytics.system.dailyActiveUsers', - 'analytics.system.monthlyActiveUsers', - ], + title: defineMessage({id: 'admin.sidebar.siteStatistics', defaultMessage: 'Site Statistics'}), + searchableStrings: systemAnalyticsSearchableStrings, schema: { id: 'SystemAnalytics', component: SystemAnalytics, @@ -478,19 +444,8 @@ const AdminDefinition: AdminDefinitionType = { }, team_statistics: { url: 'reporting/team_statistics', - title: t('admin.sidebar.teamStatistics'), - title_default: 'Team Statistics', - searchableStrings: [ - ['analytics.team.title', {team: ''}], - 'analytics.system.info', - 'analytics.team.totalPosts', - 'analytics.team.activeUsers', - 'analytics.team.totalUsers', - 'analytics.team.publicChannels', - 'analytics.team.privateGroups', - 'analytics.team.recentUsers', - 'analytics.team.newlyCreated', - ], + title: defineMessage({id: 'admin.sidebar.teamStatistics', defaultMessage: 'Team Statistics'}), + searchableStrings: teamAnalyticsSearchableStrings, schema: { id: 'TeamAnalytics', component: TeamAnalytics, @@ -500,17 +455,13 @@ const AdminDefinition: AdminDefinitionType = { }, server_logs: { url: 'reporting/server_logs', - title: t('admin.sidebar.logs'), - title_default: 'Server Logs', + title: defineMessage({id: 'admin.sidebar.logs', defaultMessage: 'Server Logs'}), isHidden: it.any( it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.REPORTING.SERVER_LOGS)), ), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.REPORTING.SERVER_LOGS)), - searchableStrings: [ - 'admin.logs.bannerDesc', - 'admin.logs.title', - ], + searchableStrings: serverLogsSearchableStrings, schema: { id: 'ServerLogs', component: ServerLogs, @@ -525,17 +476,13 @@ const AdminDefinition: AdminDefinitionType = { color={'currentColor'} /> ), - sectionTitle: t('admin.sidebar.userManagement'), - sectionTitleDefault: 'User Management', + sectionTitle: defineMessage({id: 'admin.sidebar.userManagement', defaultMessage: 'User Management'}), isHidden: it.not(it.userHasReadPermissionOnSomeResources(RESOURCE_KEYS.USER_MANAGEMENT)), subsections: { system_users: { url: 'user_management/users', - title: t('admin.sidebar.users'), - title_default: 'Users', - searchableStrings: [ - ['admin.system_users.title', {siteName: ''}], - ], + title: defineMessage({id: 'admin.sidebar.users', defaultMessage: 'Users'}), + searchableStrings: systemUsersSearchableStrings, isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.USER_MANAGEMENT.USERS)), schema: { id: 'SystemUsers', @@ -561,8 +508,7 @@ const AdminDefinition: AdminDefinitionType = { }, groups: { url: 'user_management/groups', - title: t('admin.sidebar.groups'), - title_default: 'Groups', + title: defineMessage({id: 'admin.sidebar.groups', defaultMessage: 'Groups'}), isHidden: it.any( it.not(it.licensedForFeature('LDAPGroups')), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.USER_MANAGEMENT.GROUPS)), @@ -577,16 +523,14 @@ const AdminDefinition: AdminDefinitionType = { groups_feature_discovery: { url: 'user_management/groups', isDiscovery: true, - title: t('admin.sidebar.groups'), - title_default: 'Groups', + title: defineMessage({id: 'admin.sidebar.groups', defaultMessage: 'Groups'}), isHidden: it.any( it.licensedForFeature('LDAPGroups'), it.not(it.enterpriseReady), ), schema: { id: 'Groups', - name: t('admin.group_settings.groupsPageTitle'), - name_default: 'Groups', + name: defineMessage({id: 'admin.group_settings.groupsPageTitle', defaultMessage: 'Groups'}), settings: [ { type: 'custom', @@ -609,8 +553,7 @@ const AdminDefinition: AdminDefinitionType = { }, teams: { url: 'user_management/teams', - title: t('admin.sidebar.teams'), - title_default: 'Teams', + title: defineMessage({id: 'admin.sidebar.teams', defaultMessage: 'Teams'}), isHidden: it.any( it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.USER_MANAGEMENT.TEAMS)), ), @@ -630,8 +573,7 @@ const AdminDefinition: AdminDefinitionType = { }, channel: { url: 'user_management/channels', - title: t('admin.sidebar.channels'), - title_default: 'Channels', + title: defineMessage({id: 'admin.sidebar.channels', defaultMessage: 'Channels'}), isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.USER_MANAGEMENT.CHANNELS)), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.USER_MANAGEMENT.CHANNELS)), schema: { @@ -665,20 +607,8 @@ const AdminDefinition: AdminDefinitionType = { }, permissions: { url: 'user_management/permissions/', - title: t('admin.sidebar.permissions'), - title_default: 'Permissions', - searchableStrings: [ - 'admin.permissions.documentationLinkText', - 'admin.permissions.teamOverrideSchemesNoSchemes', - 'admin.permissions.loadMoreSchemes', - 'admin.permissions.introBanner', - 'admin.permissions.systemSchemeBannerTitle', - 'admin.permissions.systemSchemeBannerText', - 'admin.permissions.systemSchemeBannerButton', - 'admin.permissions.teamOverrideSchemesTitle', - 'admin.permissions.teamOverrideSchemesBannerText', - 'admin.permissions.teamOverrideSchemesNewButton', - ], + title: defineMessage({id: 'admin.sidebar.permissions', defaultMessage: 'Permissions'}), + searchableStrings: PermissionSchemeSearchableStrings, isHidden: it.any( it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.USER_MANAGEMENT.PERMISSIONS)), ), @@ -698,9 +628,7 @@ const AdminDefinition: AdminDefinitionType = { }, system_roles: { url: 'user_management/system_roles', - title: t('admin.sidebar.systemRoles'), - title_default: 'System Roles', - searchableStrings: [], + title: defineMessage({id: 'admin.sidebar.systemRoles', defaultMessage: 'System Roles'}), isHidden: it.any( it.not(it.licensedForFeature('LDAPGroups')), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.USER_MANAGEMENT.SYSTEM_ROLES)), @@ -715,16 +643,14 @@ const AdminDefinition: AdminDefinitionType = { system_roles_feature_discovery: { url: 'user_management/system_roles', isDiscovery: true, - title: t('admin.sidebar.systemRoles'), - title_default: 'System Roles', + title: defineMessage({id: 'admin.sidebar.systemRoles', defaultMessage: 'System Roles'}), isHidden: it.any( it.licensedForFeature('LDAPGroups'), it.not(it.enterpriseReady), ), schema: { id: 'SystemRoles', - name: t('admin.permissions.systemRoles'), - name_default: 'System Roles', + name: defineMessage({id: 'admin.permissions.systemRoles', defaultMessage: 'System Roles'}), settings: [ { type: 'custom', @@ -745,75 +671,58 @@ const AdminDefinition: AdminDefinitionType = { color={'currentColor'} /> ), - sectionTitle: t('admin.sidebar.environment'), - sectionTitleDefault: 'Environment', + sectionTitle: defineMessage({id: 'admin.sidebar.environment', defaultMessage: 'Environment'}), isHidden: it.not(it.userHasReadPermissionOnSomeResources(RESOURCE_KEYS.ENVIRONMENT)), subsections: { web_server: { url: 'environment/web_server', - title: t('admin.sidebar.webServer'), - title_default: 'Web Server', + title: defineMessage({id: 'admin.sidebar.webServer', defaultMessage: 'Web Server'}), isHidden: it.any( it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), ), schema: { id: 'ServiceSettings', - name: t('admin.environment.webServer'), - name_default: 'Web Server', + name: defineMessage({id: 'admin.environment.webServer', defaultMessage: 'Web Server'}), settings: [ { type: 'banner', - label: t('admin.rate.noteDescription'), - label_default: 'Changing properties in this section will require a server restart before taking effect.', + label: defineMessage({id: 'admin.rate.noteDescription', defaultMessage: 'Changing properties in this section will require a server restart before taking effect.'}), banner_type: 'info', }, { type: 'text', key: 'ServiceSettings.SiteURL', - label: t('admin.service.siteURL'), - label_default: 'Site URL:', - help_text: t('admin.service.siteURLDescription'), - help_text_default: 'The URL that users will use to access Mattermost. Standard ports, such as 80 and 443, can be omitted, but non-standard ports are required. For example: http://example.com:8065. This setting is required.\n \nMattermost may be hosted at a subpath. For example: http://example.com:8065/company/mattermost. A restart is required before the server will work correctly.', + label: defineMessage({id: 'admin.service.siteURL', defaultMessage: 'Site URL:'}), + help_text: defineMessage({id: 'admin.service.siteURLDescription', defaultMessage: 'The URL that users will use to access Mattermost. Standard ports, such as 80 and 443, can be omitted, but non-standard ports are required. For example: http://example.com:8065. This setting is required. Mattermost may be hosted at a subpath. For example: http://example.com:8065/company/mattermost. A restart is required before the server will work correctly.'}), help_text_markdown: true, - placeholder: t('admin.service.siteURLExample'), - placeholder_default: 'E.g.: "http://example.com:8065"', + placeholder: defineMessage({id: 'admin.service.siteURLExample', defaultMessage: 'E.g.: "http://example.com:8065"'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), }, { type: 'button', key: 'TestSiteURL', action: testSiteURL, - label: t('admin.service.testSiteURL'), - label_default: 'Test Live URL', - loading: t('admin.service.testSiteURLTesting'), - loading_default: 'Testing...', - error_message: t('admin.service.testSiteURLFail'), - error_message_default: 'Test unsuccessful: {error}', - success_message: t('admin.service.testSiteURLSuccess'), - success_message_default: 'Test successful. This is a valid URL.', + label: defineMessage({id: 'admin.service.testSiteURL', defaultMessage: 'Test Live URL'}), + loading: defineMessage({id: 'admin.service.testSiteURLTesting', defaultMessage: 'Testing...'}), + error_message: defineMessage({id: 'admin.service.testSiteURLFail', defaultMessage: 'Test unsuccessful: {error}'}), + success_message: defineMessage({id: 'admin.service.testSiteURLSuccess', defaultMessage: 'Test successful. This is a valid URL.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), }, { type: 'text', key: 'ServiceSettings.ListenAddress', - label: t('admin.service.listenAddress'), - label_default: 'Listen Address:', - placeholder: t('admin.service.listenExample'), - placeholder_default: 'E.g.: ":8065"', - help_text: t('admin.service.listenDescription'), - help_text_default: 'The address and port to which to bind and listen. Specifying ":8065" will bind to all network interfaces. Specifying "127.0.0.1:8065" will only bind to the network interface having that IP address. If you choose a port of a lower level (called "system ports" or "well-known ports", in the range of 0-1023), you must have permissions to bind to that port. On Linux you can use: "sudo setcap cap_net_bind_service=+ep ./bin/mattermost" to allow Mattermost to bind to well-known ports.', + label: defineMessage({id: 'admin.service.listenAddress', defaultMessage: 'Listen Address:'}), + placeholder: defineMessage({id: 'admin.service.listenExample', defaultMessage: 'E.g.: ":8065"'}), + help_text: defineMessage({id: 'admin.service.listenDescription', defaultMessage: 'The address and port to which to bind and listen. Specifying ":8065" will bind to all network interfaces. Specifying "127.0.0.1:8065" will only bind to the network interface having that IP address. If you choose a port of a lower level (called "system ports" or "well-known ports", in the range of 0-1023), you must have permissions to bind to that port. On Linux you can use: "sudo setcap cap_net_bind_service=+ep ./bin/mattermost" to allow Mattermost to bind to well-known ports.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), }, { type: 'bool', key: 'ServiceSettings.Forward80To443', - label: t('admin.service.forward80To443'), - label_default: 'Forward port 80 to 443:', - help_text: t('admin.service.forward80To443Description'), - help_text_default: 'Forwards all insecure traffic from port 80 to secure port 443. Not recommended when using a proxy server.', - disabled_help_text: t('admin.service.forward80To443Description.disabled'), - disabled_help_text_default: 'Forwards all insecure traffic from port 80 to secure port 443. Not recommended when using a proxy server.\n \nThis setting cannot be enabled until your server is [listening](#ServiceSettings.ListenAddress) on port 443.', + label: defineMessage({id: 'admin.service.forward80To443', defaultMessage: 'Forward port 80 to 443:'}), + help_text: defineMessage({id: 'admin.service.forward80To443Description', defaultMessage: 'Forwards all insecure traffic from port 80 to secure port 443. Not recommended when using a proxy server.'}), + disabled_help_text: defineMessage({id: 'admin.service.forward80To443Description.disabled', defaultMessage: 'Forwards all insecure traffic from port 80 to secure port 443. Not recommended when using a proxy server. This setting cannot be enabled until your server is [listening](#ServiceSettings.ListenAddress) on port 443.'}), disabled_help_text_markdown: true, isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), @@ -823,19 +732,16 @@ const AdminDefinition: AdminDefinitionType = { { type: 'dropdown', key: 'ServiceSettings.ConnectionSecurity', - label: t('admin.connectionSecurityTitle'), - label_default: 'Connection Security:', + label: defineMessage({id: 'admin.connectionSecurityTitle', defaultMessage: 'Connection Security:'}), help_text: DefinitionConstants.CONNECTION_SECURITY_HELP_TEXT_WEBSERVER, options: [ { value: '', - display_name: t('admin.connectionSecurityNone'), - display_name_default: 'None', + display_name: defineMessage({id: 'admin.connectionSecurityNone', defaultMessage: 'None'}), }, { value: 'TLS', - display_name: t('admin.connectionSecurityTls'), - display_name_default: 'TLS (Recommended)', + display_name: defineMessage({id: 'admin.connectionSecurityTls', defaultMessage: 'TLS (Recommended)'}), }, ], isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), @@ -843,10 +749,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'ServiceSettings.TLSCertFile', - label: t('admin.service.tlsCertFile'), - label_default: 'TLS Certificate File:', - help_text: t('admin.service.tlsCertFileDescription'), - help_text_default: 'The certificate file to use.', + label: defineMessage({id: 'admin.service.tlsCertFile', defaultMessage: 'TLS Certificate File:'}), + help_text: defineMessage({id: 'admin.service.tlsCertFileDescription', defaultMessage: 'The certificate file to use.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), it.stateIsTrue('ServiceSettings.UseLetsEncrypt'), @@ -855,10 +759,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'ServiceSettings.TLSKeyFile', - label: t('admin.service.tlsKeyFile'), - label_default: 'TLS Key File:', - help_text: t('admin.service.tlsKeyFileDescription'), - help_text_default: 'The private key file to use.', + label: defineMessage({id: 'admin.service.tlsKeyFile', defaultMessage: 'TLS Key File:'}), + help_text: defineMessage({id: 'admin.service.tlsKeyFileDescription', defaultMessage: 'The private key file to use.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), it.stateIsTrue('ServiceSettings.UseLetsEncrypt'), @@ -867,12 +769,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'ServiceSettings.UseLetsEncrypt', - label: t('admin.service.useLetsEncrypt'), - label_default: 'Use Let\'s Encrypt:', - help_text: t('admin.service.useLetsEncryptDescription'), - help_text_default: 'Enable the automatic retrieval of certificates from Let\'s Encrypt. The certificate will be retrieved when a client attempts to connect from a new domain. This will work with multiple domains.', - disabled_help_text: t('admin.service.useLetsEncryptDescription.disabled'), - disabled_help_text_default: 'Enable the automatic retrieval of certificates from Let\'s Encrypt. The certificate will be retrieved when a client attempts to connect from a new domain. This will work with multiple domains.\n \nThis setting cannot be enabled unless the [Forward port 80 to 443](#SystemSettings.Forward80To443) setting is set to true.', + label: defineMessage({id: 'admin.service.useLetsEncrypt', defaultMessage: 'Use Let\'s Encrypt:'}), + help_text: defineMessage({id: 'admin.service.useLetsEncryptDescription', defaultMessage: 'Enable the automatic retrieval of certificates from Let\'s Encrypt. The certificate will be retrieved when a client attempts to connect from a new domain. This will work with multiple domains.'}), + disabled_help_text: defineMessage({id: 'admin.service.useLetsEncryptDescription.disabled', defaultMessage: "Enable the automatic retrieval of certificates from Let's Encrypt. The certificate will be retrieved when a client attempts to connect from a new domain. This will work with multiple domains. This setting cannot be enabled unless the [Forward port 80 to 443](#SystemSettings.Forward80To443) setting is set to true."}), disabled_help_text_markdown: true, isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), @@ -882,10 +781,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'ServiceSettings.LetsEncryptCertificateCacheFile', - label: t('admin.service.letsEncryptCertificateCacheFile'), - label_default: 'Let\'s Encrypt Certificate Cache File:', - help_text: t('admin.service.letsEncryptCertificateCacheFileDescription'), - help_text_default: 'Certificates retrieved and other data about the Let\'s Encrypt service will be stored in this file.', + label: defineMessage({id: 'admin.service.letsEncryptCertificateCacheFile', defaultMessage: 'Let\'s Encrypt Certificate Cache File:'}), + help_text: defineMessage({id: 'admin.service.letsEncryptCertificateCacheFileDescription', defaultMessage: 'Certificates retrieved and other data about the Let\'s Encrypt service will be stored in this file.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), it.stateIsFalse('ServiceSettings.UseLetsEncrypt'), @@ -894,42 +791,34 @@ const AdminDefinition: AdminDefinitionType = { { type: 'number', key: 'ServiceSettings.ReadTimeout', - label: t('admin.service.readTimeout'), - label_default: 'Read Timeout:', - help_text: t('admin.service.readTimeoutDescription'), - help_text_default: 'Maximum time allowed from when the connection is accepted to when the request body is fully read.', + label: defineMessage({id: 'admin.service.readTimeout', defaultMessage: 'Read Timeout:'}), + help_text: defineMessage({id: 'admin.service.readTimeoutDescription', defaultMessage: 'Maximum time allowed from when the connection is accepted to when the request body is fully read.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), }, { type: 'number', key: 'ServiceSettings.WriteTimeout', - label: t('admin.service.writeTimeout'), - label_default: 'Write Timeout:', - help_text: t('admin.service.writeTimeoutDescription'), - help_text_default: 'If using HTTP (insecure), this is the maximum time allowed from the end of reading the request headers until the response is written. If using HTTPS, it is the total time from when the connection is accepted until the response is written.', + label: defineMessage({id: 'admin.service.writeTimeout', defaultMessage: 'Write Timeout:'}), + help_text: defineMessage({id: 'admin.service.writeTimeoutDescription', defaultMessage: 'If using HTTP (insecure), this is the maximum time allowed from the end of reading the request headers until the response is written. If using HTTPS, it is the total time from when the connection is accepted until the response is written.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), }, { type: 'dropdown', key: 'ServiceSettings.WebserverMode', - label: t('admin.webserverModeTitle'), - label_default: 'Webserver Mode:', + label: defineMessage({id: 'admin.webserverModeTitle', defaultMessage: 'Webserver Mode:'}), help_text: DefinitionConstants.WEBSERVER_MODE_HELP_TEXT, options: [ { value: 'gzip', - display_name: t('admin.webserverModeGzip'), - display_name_default: 'gzip', + display_name: defineMessage({id: 'admin.webserverModeGzip', defaultMessage: 'gzip'}), }, { value: 'uncompressed', - display_name: t('admin.webserverModeUncompressed'), - display_name_default: 'Uncompressed', + display_name: defineMessage({id: 'admin.webserverModeUncompressed', defaultMessage: 'Uncompressed'}), }, { value: 'disabled', - display_name: t('admin.webserverModeDisabled'), - display_name_default: 'Disabled', + display_name: defineMessage({id: 'admin.webserverModeDisabled', defaultMessage: 'Disabled'}), }, ], isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), @@ -937,19 +826,15 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'ServiceSettings.EnableInsecureOutgoingConnections', - label: t('admin.service.insecureTlsTitle'), - label_default: 'Enable Insecure Outgoing Connections: ', - help_text: t('admin.service.insecureTlsDesc'), - help_text_default: 'When true, any outgoing HTTPS requests will accept unverified, self-signed certificates. For example, outgoing webhooks to a server with a self-signed TLS certificate, using any domain, will be allowed. Note that this makes these connections susceptible to man-in-the-middle attacks.', + label: defineMessage({id: 'admin.service.insecureTlsTitle', defaultMessage: 'Enable Insecure Outgoing Connections: '}), + help_text: defineMessage({id: 'admin.service.insecureTlsDesc', defaultMessage: 'When true, any outgoing HTTPS requests will accept unverified, self-signed certificates. For example, outgoing webhooks to a server with a self-signed TLS certificate, using any domain, will be allowed. Note that this makes these connections susceptible to man-in-the-middle attacks.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), }, { type: 'text', key: 'ServiceSettings.ManagedResourcePaths', - label: t('admin.service.managedResourcePaths'), - label_default: 'Managed Resource Paths:', - help_text: t('admin.service.managedResourcePathsDescription'), - help_text_default: 'A comma-separated list of paths on the Mattermost server that are managed by another service. See here for more information.', + label: defineMessage({id: 'admin.service.managedResourcePaths', defaultMessage: 'Managed Resource Paths:'}), + help_text: defineMessage({id: 'admin.service.managedResourcePathsDescription', defaultMessage: 'A comma-separated list of paths on the Mattermost server that are managed by another service. See here for more information.'}), help_text_markdown: false, help_text_values: { link: (msg: string) => ( @@ -967,10 +852,8 @@ const AdminDefinition: AdminDefinitionType = { type: 'button', action: reloadConfig, key: 'ReloadConfigButton', - label: t('admin.reload.button'), - label_default: 'Reload Configuration From Disk', - help_text: t('admin.reload.reloadDescription'), - help_text_default: 'Deployments using multiple databases can switch from one master database to another without restarting the Mattermost server by updating "config.json" to the new desired configuration and using the {featureName} feature to load the new settings while the server is running. The administrator should then use the {recycleDatabaseConnections} feature to recycle the database connections based on the new settings.', + label: defineMessage({id: 'admin.reload.button', defaultMessage: 'Reload Configuration From Disk'}), + help_text: defineMessage({id: 'admin.reload.reloadDescription', defaultMessage: 'Deployments using multiple databases can switch from one master database to another without restarting the Mattermost server by updating "config.json" to the new desired configuration and using the {featureName} feature to load the new settings while the server is running. The administrator should then use the {recycleDatabaseConnections} feature to recycle the database connections based on the new settings.'}), help_text_values: { featureName: ( @@ -991,20 +874,16 @@ const AdminDefinition: AdminDefinitionType = { ), }, - error_message: t('admin.reload.reloadFail'), - error_message_default: 'Reload unsuccessful: {error}', + error_message: defineMessage({id: 'admin.reload.reloadFail', defaultMessage: 'Reload unsuccessful: {error}'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), }, { type: 'button', key: 'PurgeButton', action: invalidateAllCaches, - label: t('admin.purge.button'), - label_default: 'Purge All Caches', - help_text: t('admin.purge.purgeDescription'), - help_text_default: 'This will purge all the in-memory caches for things like sessions, accounts, channels, etc. Deployments using High Availability will attempt to purge all the servers in the cluster. Purging the caches may adversely impact performance.', - error_message: t('admin.purge.purgeFail'), - error_message_default: 'Purging unsuccessful: {error}', + label: defineMessage({id: 'admin.purge.button', defaultMessage: 'Purge All Caches'}), + help_text: defineMessage({id: 'admin.purge.purgeDescription', defaultMessage: 'This will purge all the in-memory caches for things like sessions, accounts, channels, etc. Deployments using High Availability will attempt to purge all the servers in the cluster. Purging the caches may adversely impact performance.'}), + error_message: defineMessage({id: 'admin.purge.purgeFail', defaultMessage: 'Purging unsuccessful: {error}'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.WEB_SERVER)), }, ], @@ -1012,34 +891,8 @@ const AdminDefinition: AdminDefinitionType = { }, database: { url: 'environment/database', - title: t('admin.sidebar.database'), - title_default: 'Database', - searchableStrings: [ - 'admin.database.title', - ['admin.recycle.recycleDescription', {featureName: '', reloadConfiguration: ''}], - 'admin.recycle.recycleDescription.featureName', - 'admin.recycle.recycleDescription.reloadConfiguration', - 'admin.recycle.button', - 'admin.sql.noteDescription', - 'admin.sql.disableDatabaseSearchTitle', - 'admin.sql.disableDatabaseSearchDescription', - 'admin.sql.driverName', - 'admin.sql.driverNameDescription', - 'admin.sql.dataSource', - 'admin.sql.dataSourceDescription', - 'admin.sql.maxConnectionsTitle', - 'admin.sql.maxConnectionsDescription', - 'admin.sql.maxOpenTitle', - 'admin.sql.maxOpenDescription', - 'admin.sql.queryTimeoutTitle', - 'admin.sql.queryTimeoutDescription', - 'admin.sql.connMaxLifetimeTitle', - 'admin.sql.connMaxLifetimeDescription', - 'admin.sql.connMaxIdleTimeTitle', - 'admin.sql.connMaxIdleTimeDescription', - 'admin.sql.traceTitle', - 'admin.sql.traceDescription', - ], + title: defineMessage({id: 'admin.sidebar.database', defaultMessage: 'Database'}), + searchableStrings: databaseSearchableStrings, isHidden: it.any( it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.DATABASE)), @@ -1052,39 +905,13 @@ const AdminDefinition: AdminDefinitionType = { }, elasticsearch: { url: 'environment/elasticsearch', - title: t('admin.sidebar.elasticsearch'), - title_default: 'Elasticsearch', + title: defineMessage({id: 'admin.sidebar.elasticsearch', defaultMessage: 'Elasticsearch'}), isHidden: it.any( it.not(it.licensedForFeature('Elasticsearch')), it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.ELASTICSEARCH)), ), - searchableStrings: [ - 'admin.elasticsearch.title', - 'admin.elasticsearch.enableIndexingTitle', - ['admin.elasticsearch.enableIndexingDescription', {documentationLink: ''}], - 'admin.elasticsearch.enableIndexingDescription.documentationLinkText', - 'admin.elasticsearch.connectionUrlTitle', - ['admin.elasticsearch.connectionUrlDescription', {documentationLink: ''}], - 'admin.elasticsearch.connectionUrlExample.documentationLinkText', - 'admin.elasticsearch.skipTLSVerificationTitle', - 'admin.elasticsearch.skipTLSVerificationDescription', - 'admin.elasticsearch.usernameTitle', - 'admin.elasticsearch.usernameDescription', - 'admin.elasticsearch.passwordTitle', - 'admin.elasticsearch.passwordDescription', - 'admin.elasticsearch.sniffTitle', - 'admin.elasticsearch.sniffDescription', - 'admin.elasticsearch.testHelpText', - 'admin.elasticsearch.elasticsearch_test_button', - 'admin.elasticsearch.bulkIndexingTitle', - 'admin.elasticsearch.createJob.help', - 'admin.elasticsearch.purgeIndexesHelpText', - 'admin.elasticsearch.purgeIndexesButton', - 'admin.elasticsearch.purgeIndexesButton.label', - 'admin.elasticsearch.enableSearchingTitle', - 'admin.elasticsearch.enableSearchingDescription', - ], + searchableStrings: elasticSearchSearchableStrings, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.ELASTICSEARCH)), schema: { id: 'ElasticSearchSettings', @@ -1093,35 +920,29 @@ const AdminDefinition: AdminDefinitionType = { }, storage: { url: 'environment/file_storage', - title: t('admin.sidebar.fileStorage'), - title_default: 'File Storage', + title: defineMessage({id: 'admin.sidebar.fileStorage', defaultMessage: 'File Storage'}), isHidden: it.any( it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), ), schema: { id: 'FileSettings', - name: t('admin.environment.fileStorage'), - name_default: 'File Storage', + name: defineMessage({id: 'admin.environment.fileStorage', defaultMessage: 'File Storage'}), settings: [ { type: 'dropdown', key: 'FileSettings.DriverName', - label: t('admin.image.storeTitle'), - label_default: 'File Storage System:', - help_text: t('admin.image.storeDescription'), - help_text_default: 'Storage system where files and image attachments are saved.\n \nSelecting "Amazon S3" enables fields to enter your Amazon credentials and bucket details.\n \nSelecting "Local File System" enables the field to specify a local file directory.', + label: defineMessage({id: 'admin.image.storeTitle', defaultMessage: 'File Storage System:'}), + help_text: defineMessage({id: 'admin.image.storeDescription', defaultMessage: 'Storage system where files and image attachments are saved. Selecting "Amazon S3" enables fields to enter your Amazon credentials and bucket details. Selecting "Local File System" enables the field to specify a local file directory.'}), help_text_markdown: true, options: [ { value: FILE_STORAGE_DRIVER_LOCAL, - display_name: t('admin.image.storeLocal'), - display_name_default: 'Local File System', + display_name: defineMessage({id: 'admin.image.storeLocal', defaultMessage: 'Local File System'}), }, { value: FILE_STORAGE_DRIVER_S3, - display_name: t('admin.image.storeAmazonS3'), - display_name_default: 'Amazon S3', + display_name: defineMessage({id: 'admin.image.storeAmazonS3', defaultMessage: 'Amazon S3'}), }, ], isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), @@ -1129,12 +950,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'FileSettings.Directory', - label: t('admin.image.localTitle'), - label_default: 'Local Storage Directory:', - help_text: t('admin.image.localDescription'), - help_text_default: 'Directory to which files and images are written. If blank, defaults to ./data/.', - placeholder: t('admin.image.localExample'), - placeholder_default: 'E.g.: "./data/"', + label: defineMessage({id: 'admin.image.localTitle', defaultMessage: 'Local Storage Directory:'}), + help_text: defineMessage({id: 'admin.image.localDescription', defaultMessage: 'Directory to which files and images are written. If blank, defaults to ./data/.'}), + placeholder: defineMessage({id: 'admin.image.localExample', defaultMessage: 'E.g.: "./data/"'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), it.not(it.stateEquals('FileSettings.DriverName', FILE_STORAGE_DRIVER_LOCAL)), @@ -1143,12 +961,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'number', key: 'FileSettings.MaxFileSize', - label: t('admin.image.maxFileSizeTitle'), - label_default: 'Maximum File Size:', - help_text: t('admin.image.maxFileSizeDescription'), - help_text_default: 'Maximum file size for message attachments in megabytes. Caution: Verify server memory can support your setting choice. Large file sizes increase the risk of server crashes and failed uploads due to network interruptions.', - placeholder: t('admin.image.maxFileSizeExample'), - placeholder_default: '50', + label: defineMessage({id: 'admin.image.maxFileSizeTitle', defaultMessage: 'Maximum File Size:'}), + help_text: defineMessage({id: 'admin.image.maxFileSizeDescription', defaultMessage: 'Maximum file size for message attachments in megabytes. Caution: Verify server memory can support your setting choice. Large file sizes increase the risk of server crashes and failed uploads due to network interruptions.'}), + placeholder: defineMessage({id: 'admin.image.maxFileSizeExample', defaultMessage: '50'}), onConfigLoad: (configVal) => configVal / MEBIBYTE, onConfigSave: (displayVal) => displayVal * MEBIBYTE, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), @@ -1156,11 +971,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'FileSettings.ExtractContent', - label: t('admin.image.extractContentTitle'), - label_default: 'Enable document search by content:', - help_text: t('admin.image.extractContentDescription'), + label: defineMessage({id: 'admin.image.extractContentTitle', defaultMessage: 'Enable document search by content:'}), + help_text: defineMessage({id: 'admin.image.extractContentDescription', defaultMessage: 'When enabled, supported document types are searchable by their content. Search results for existing documents may be incomplete until a data migration is executed.'}), help_text_markdown: false, - help_text_default: 'When enabled, supported document types are searchable by their content. Search results for existing documents may be incomplete until a data migration is executed.', help_text_values: { link: (msg: string) => ( IAM role. Enter the Access Key ID provided by your Amazon EC2 administrator.', + label: defineMessage({id: 'admin.image.amazonS3IdTitle', defaultMessage: 'Amazon S3 Access Key ID:'}), + help_text: defineMessage({id: 'admin.image.amazonS3IdDescription', defaultMessage: '(Optional) Only required if you do not want to authenticate to S3 using an IAM role. Enter the Access Key ID provided by your Amazon EC2 administrator.'}), help_text_values: { link: (msg: string) => ( documentation to learn more.', + label: defineMessage({id: 'admin.image.amazonS3SSETitle', defaultMessage: 'Enable Server-Side Encryption for Amazon S3:'}), + help_text: defineMessage({id: 'admin.image.amazonS3SSEDescription', defaultMessage: 'When true, encrypt files in Amazon S3 using server-side encryption with Amazon S3-managed keys. See documentation to learn more.'}), help_text_values: { link: (msg: string) => ( documentation to learn more.', + label: defineMessage({id: 'admin.image.proxyType', defaultMessage: 'Image Proxy Type:'}), + help_text: defineMessage({id: 'admin.image.proxyTypeDescription', defaultMessage: 'Configure an image proxy to load all Markdown images through a proxy. The image proxy prevents users from making insecure image requests, provides caching for increased performance, and automates image adjustments such as resizing. See documentation to learn more.'}), help_text_values: { link: (msg: string) => ( @@ -1851,10 +1527,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'LogSettings.EnableDiagnostics', - label: t('admin.log.enableDiagnostics'), - label_default: 'Enable Diagnostics and Error Reporting:', - help_text: t('admin.log.enableDiagnosticsDescription'), - help_text_default: 'Enable this feature to improve the quality and performance of Mattermost by sending error reporting and diagnostic information to Mattermost, Inc. Read our privacy policy to learn more.', + label: defineMessage({id: 'admin.log.enableDiagnostics', defaultMessage: 'Enable Diagnostics and Error Reporting:'}), + help_text: defineMessage({id: 'admin.log.enableDiagnosticsDescription', defaultMessage: 'Enable this feature to improve the quality and performance of Mattermost by sending error reporting and diagnostic information to Mattermost, Inc. Read our privacy policy to learn more.'}), help_text_markdown: false, help_text_values: { link: (msg: string) => ( @@ -1877,10 +1551,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'longtext', key: 'LogSettings.AdvancedLoggingJSON', - label: t('admin.log.AdvancedLoggingJSONTitle'), - label_default: 'Advanced Logging:', - help_text: t('admin.log.AdvancedLoggingJSONDescription'), - help_text_default: 'The JSON configuration for Advanced Logging. Please see documentation to learn more about Advanced Logging and the JSON format it uses.', + label: defineMessage({id: 'admin.log.AdvancedLoggingJSONTitle', defaultMessage: 'Advanced Logging:'}), + help_text: defineMessage({id: 'admin.log.AdvancedLoggingJSONDescription', defaultMessage: 'The JSON configuration for Advanced Logging. Please see documentation to learn more about Advanced Logging and the JSON format it uses.'}), help_text_markdown: false, help_text_values: { link: (msg: string) => ( @@ -1892,11 +1564,10 @@ const AdminDefinition: AdminDefinitionType = { ), }, - placeholder: t('admin.log.AdvancedLoggingJSONPlaceholder'), - placeholder_default: 'Enter your JSON configuration', + placeholder: defineMessage({id: 'admin.log.AdvancedLoggingJSONPlaceholder', defaultMessage: 'Enter your JSON configuration'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.LOGGING)), validate: (value) => { - const valid = new ValidationResult(true, '', ''); + const valid = new ValidationResult(true, ''); if (!value) { return valid; } @@ -1904,7 +1575,7 @@ const AdminDefinition: AdminDefinitionType = { JSON.parse(value); return valid; } catch (error) { - return new ValidationResult(false, '', error.message); + return new ValidationResult(false, error.message); } }, onConfigLoad: (configVal) => JSON.stringify(configVal, null, ' '), @@ -1922,31 +1593,12 @@ const AdminDefinition: AdminDefinitionType = { }, session_lengths: { url: 'environment/session_lengths', - title: t('admin.sidebar.sessionLengths'), - title_default: 'Session Lengths', + title: defineMessage({id: 'admin.sidebar.sessionLengths', defaultMessage: 'Session Lengths'}), isHidden: it.any( it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.SESSION_LENGTHS)), ), - searchableStrings: [ - 'admin.sessionLengths.title', - 'admin.service.webSessionHoursDesc.extendLength', - 'admin.service.mobileSessionHoursDesc.extendLength', - 'admin.service.ssoSessionHoursDesc.extendLength', - 'admin.service.webSessionHoursDesc', - 'admin.service.mobileSessionHoursDesc', - 'admin.service.ssoSessionHoursDesc', - 'admin.service.sessionIdleTimeout', - 'admin.service.sessionIdleTimeoutDesc', - 'admin.service.extendSessionLengthActivity.label', - 'admin.service.extendSessionLengthActivity.helpText', - 'admin.service.webSessionHours', - 'admin.service.sessionHoursEx', - 'admin.service.mobileSessionHours', - 'admin.service.ssoSessionHours', - 'admin.service.sessionCache', - 'admin.service.sessionCacheDesc', - ], + searchableStrings: sessionLengthSearchableStrings, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.SESSION_LENGTHS)), schema: { id: 'SessionLengths', @@ -1955,24 +1607,20 @@ const AdminDefinition: AdminDefinitionType = { }, metrics: { url: 'environment/performance_monitoring', - title: t('admin.sidebar.metrics'), - title_default: 'Performance Monitoring', + title: defineMessage({id: 'admin.sidebar.metrics', defaultMessage: 'Performance Monitoring'}), isHidden: it.any( it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.PERFORMANCE_MONITORING)), ), schema: { id: 'MetricsSettings', - name: t('admin.advance.metrics'), - name_default: 'Performance Monitoring', + name: defineMessage({id: 'admin.advance.metrics', defaultMessage: 'Performance Monitoring'}), settings: [ { type: 'bool', key: 'MetricsSettings.Enable', - label: t('admin.metrics.enableTitle'), - label_default: 'Enable Performance Monitoring:', - help_text: t('admin.metrics.enableDescription'), - help_text_default: 'When true, Mattermost will enable performance monitoring collection and profiling. Please see documentation to learn more about configuring performance monitoring for Mattermost.', + label: defineMessage({id: 'admin.metrics.enableTitle', defaultMessage: 'Enable Performance Monitoring:'}), + help_text: defineMessage({id: 'admin.metrics.enableDescription', defaultMessage: 'When true, Mattermost will enable performance monitoring collection and profiling. Please see documentation to learn more about configuring performance monitoring for Mattermost.'}), help_text_markdown: false, help_text_values: { link: (msg: string) => ( @@ -1989,12 +1637,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'MetricsSettings.ListenAddress', - label: t('admin.metrics.listenAddressTitle'), - label_default: 'Listen Address:', - placeholder: t('admin.metrics.listenAddressEx'), - placeholder_default: 'E.g.: ":8067"', - help_text: t('admin.metrics.listenAddressDesc'), - help_text_default: 'The address the server will listen on to expose performance metrics.', + label: defineMessage({id: 'admin.metrics.listenAddressTitle', defaultMessage: 'Listen Address:'}), + placeholder: defineMessage({id: 'admin.metrics.listenAddressEx', defaultMessage: 'E.g.: ":8067"'}), + help_text: defineMessage({id: 'admin.metrics.listenAddressDesc', defaultMessage: 'The address the server will listen on to expose performance metrics.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.PERFORMANCE_MONITORING)), }, ], @@ -2002,54 +1647,43 @@ const AdminDefinition: AdminDefinitionType = { }, developer: { url: 'environment/developer', - title: t('admin.sidebar.developer'), - title_default: 'Developer', + title: defineMessage({id: 'admin.sidebar.developer', defaultMessage: 'Developer'}), isHidden: it.any( it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.DEVELOPER)), ), schema: { id: 'ServiceSettings', - name: t('admin.developer.title'), - name_default: 'Developer Settings', + name: defineMessage({id: 'admin.developer.title', defaultMessage: 'Developer Settings'}), settings: [ { type: 'bool', key: 'ServiceSettings.EnableTesting', - label: t('admin.service.testingTitle'), - label_default: 'Enable Testing Commands:', - help_text: t('admin.service.testingDescription'), - help_text_default: 'When true, /test slash command is enabled to load test accounts, data and text formatting. Changing this requires a server restart before taking effect.', + label: defineMessage({id: 'admin.service.testingTitle', defaultMessage: 'Enable Testing Commands:'}), + help_text: defineMessage({id: 'admin.service.testingDescription', defaultMessage: 'When true, /test slash command is enabled to load test accounts, data and text formatting. Changing this requires a server restart before taking effect.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.DEVELOPER)), }, { type: 'bool', key: 'ServiceSettings.EnableDeveloper', - label: t('admin.service.developerTitle'), - label_default: 'Enable Developer Mode: ', - help_text: t('admin.service.developerDesc'), - help_text_default: 'When true, JavaScript errors are shown in a purple bar at the top of the user interface. Not recommended for use in production. Changing this requires a server restart before taking effect.', + label: defineMessage({id: 'admin.service.developerTitle', defaultMessage: 'Enable Developer Mode: '}), + help_text: defineMessage({id: 'admin.service.developerDesc', defaultMessage: 'When true, JavaScript errors are shown in a purple bar at the top of the user interface. Not recommended for use in production. Changing this requires a server restart before taking effect.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.DEVELOPER)), }, { type: 'bool', key: 'ServiceSettings.EnableClientPerformanceDebugging', - label: t('admin.service.performanceDebuggingTitle'), - label_default: 'Enable Client Performance Debugging: ', - help_text: t('admin.service.performanceDebuggingDescription'), - help_text_default: 'When true, users can access debugging settings for their account in **Settings > Advanced > Performance Debugging** to assist in diagnosing performance issues. Changing this requires a server restart before taking effect.', + label: defineMessage({id: 'admin.service.performanceDebuggingTitle', defaultMessage: 'Enable Client Performance Debugging: '}), + help_text: defineMessage({id: 'admin.service.performanceDebuggingDescription', defaultMessage: 'When true, users can access debugging settings for their account in **Settings > Advanced > Performance Debugging** to assist in diagnosing performance issues. Changing this requires a server restart before taking effect.'}), help_text_markdown: true, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.DEVELOPER)), }, { type: 'text', key: 'ServiceSettings.AllowedUntrustedInternalConnections', - label: t('admin.service.internalConnectionsTitle'), - label_default: 'Allow untrusted internal connections to: ', - placeholder: t('admin.service.internalConnectionsEx'), - placeholder_default: 'webhooks.internal.example.com 127.0.0.1 10.0.16.0/28', - help_text: t('admin.service.internalConnectionsDesc'), - help_text_default: 'A whitelist of local network addresses that can be requested by the Mattermost server on behalf of a client. Care should be used when configuring this setting to prevent unintended access to your local network. See documentation to learn more. Changing this requires a server restart before taking effect.', + label: defineMessage({id: 'admin.service.internalConnectionsTitle', defaultMessage: 'Allow untrusted internal connections to: '}), + placeholder: defineMessage({id: 'admin.service.internalConnectionsEx', defaultMessage: 'webhooks.internal.example.com 127.0.0.1 10.0.16.0/28'}), + help_text: defineMessage({id: 'admin.service.internalConnectionsDesc', defaultMessage: 'A whitelist of local network addresses that can be requested by the Mattermost server on behalf of a client. Care should be used when configuring this setting to prevent unintended access to your local network. See documentation to learn more. Changing this requires a server restart before taking effect.'}), help_text_values: { link: (msg: string) => ( ), - sectionTitle: t('admin.sidebar.site'), - sectionTitleDefault: 'Site Configuration', + sectionTitle: defineMessage({id: 'admin.sidebar.site', defaultMessage: 'Site Configuration'}), isHidden: it.not(it.userHasReadPermissionOnSomeResources(RESOURCE_KEYS.SITE)), subsections: { customization: { url: 'site_config/customization', - title: t('admin.sidebar.customization'), - title_default: 'Customization', + title: defineMessage({id: 'admin.sidebar.customization', defaultMessage: 'Customization'}), isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), schema: { id: 'Customization', - name: t('admin.site.customization'), - name_default: 'Customization', + name: defineMessage({id: 'admin.site.customization', defaultMessage: 'Customization'}), settings: [ { type: 'text', key: 'TeamSettings.SiteName', - label: t('admin.team.siteNameTitle'), - label_default: 'Site Name:', - help_text: t('admin.team.siteNameDescription'), - help_text_default: 'Name of service shown in login screens and UI. When not specified, it defaults to "Mattermost".', - placeholder: t('admin.team.siteNameExample'), - placeholder_default: 'E.g.: "Mattermost"', + label: defineMessage({id: 'admin.team.siteNameTitle', defaultMessage: 'Site Name:'}), + help_text: defineMessage({id: 'admin.team.siteNameDescription', defaultMessage: 'Name of service shown in login screens and UI. When not specified, it defaults to "Mattermost".'}), + placeholder: defineMessage({id: 'admin.team.siteNameExample', defaultMessage: 'E.g.: "Mattermost"'}), max_length: Constants.MAX_SITENAME_LENGTH, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), }, { type: 'text', key: 'TeamSettings.CustomDescriptionText', - label: t('admin.team.brandDescriptionTitle'), - label_default: 'Site Description: ', - help_text: t('admin.team.brandDescriptionHelp'), - help_text_default: 'Displays as a title above the login form. When not specified, the phrase "Log in" is displayed.', + label: defineMessage({id: 'admin.team.brandDescriptionTitle', defaultMessage: 'Site Description: '}), + help_text: defineMessage({id: 'admin.team.brandDescriptionHelp', defaultMessage: 'Displays as a title above the login form. When not specified, the phrase "Log in" is displayed.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), }, { type: 'bool', key: 'TeamSettings.EnableCustomBrand', - label: t('admin.team.brandTitle'), - label_default: 'Enable Custom Branding: ', - help_text: t('admin.team.brandDesc'), - help_text_default: 'Enable custom branding to show an image of your choice, uploaded below, and some help text, written below, on the login page.', + label: defineMessage({id: 'admin.team.brandTitle', defaultMessage: 'Enable Custom Branding: '}), + help_text: defineMessage({id: 'admin.team.brandDesc', defaultMessage: 'Enable custom branding to show an image of your choice, uploaded below, and some help text, written below, on the login page.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), }, { @@ -2131,10 +1755,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'longtext', key: 'TeamSettings.CustomBrandText', - label: t('admin.team.brandTextTitle'), - label_default: 'Custom Brand Text:', - help_text: t('admin.team.brandTextDescription'), - help_text_default: 'Text that will appear below your custom brand image on your login screen. Supports Markdown-formatted text. Maximum 500 characters allowed.', + label: defineMessage({id: 'admin.team.brandTextTitle', defaultMessage: 'Custom Brand Text:'}), + help_text: defineMessage({id: 'admin.team.brandTextDescription', defaultMessage: 'Text that will appear below your custom brand image on your login screen. Supports Markdown-formatted text. Maximum 500 characters allowed.'}), max_length: Constants.MAX_CUSTOM_BRAND_TEXT_LENGTH, isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), @@ -2144,98 +1766,78 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'SupportSettings.EnableAskCommunityLink', - label: t('admin.support.enableAskCommunityTitle'), - label_default: 'Enable Ask Community Link:', - help_text: t('admin.support.enableAskCommunityDesc'), - help_text_default: 'When true, "Ask the community" link appears on the Mattermost user interface and Help Menu, which allows users to join the Mattermost Community to ask questions and help others troubleshoot issues. When false, the link is hidden from users.', + label: defineMessage({id: 'admin.support.enableAskCommunityTitle', defaultMessage: 'Enable Ask Community Link:'}), + help_text: defineMessage({id: 'admin.support.enableAskCommunityDesc', defaultMessage: 'When true, "Ask the community" link appears on the Mattermost user interface and Help Menu, which allows users to join the Mattermost Community to ask questions and help others troubleshoot issues. When false, the link is hidden from users.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), }, { type: 'text', key: 'SupportSettings.HelpLink', - label: t('admin.support.helpTitle'), - label_default: 'Help Link:', - help_text: t('admin.support.helpDesc'), - help_text_default: 'The URL for the Help link on the Mattermost login page, sign-up pages, and Help Menu. If this field is empty, the Help link is hidden from users.', + label: defineMessage({id: 'admin.support.helpTitle', defaultMessage: 'Help Link:'}), + help_text: defineMessage({id: 'admin.support.helpDesc', defaultMessage: 'The URL for the Help link on the Mattermost login page, sign-up pages, and Help Menu. If this field is empty, the Help link is hidden from users.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), }, { type: 'text', key: 'SupportSettings.TermsOfServiceLink', - label: t('admin.support.termsTitle'), - label_default: 'Terms of Use Link:', - help_text: t('admin.support.termsDesc'), - help_text_default: 'Link to the terms under which users may use your online service. By default, this includes the "Mattermost Conditions of Use (End Users)" explaining the terms under which Mattermost software is provided to end users. If you change the default link to add your own terms for using the service you provide, your new terms must include a link to the default terms so end users are aware of the Mattermost Conditions of Use (End User) for Mattermost software.', + label: defineMessage({id: 'admin.support.termsTitle', defaultMessage: 'Terms of Use Link:'}), + help_text: defineMessage({id: 'admin.support.termsDesc', defaultMessage: 'Link to the terms under which users may use your online service. By default, this includes the "Mattermost Conditions of Use (End Users)" explaining the terms under which Mattermost software is provided to end users. If you change the default link to add your own terms for using the service you provide, your new terms must include a link to the default terms so end users are aware of the Mattermost Conditions of Use (End User) for Mattermost software.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), isHidden: it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), }, { type: 'text', key: 'SupportSettings.PrivacyPolicyLink', - label: t('admin.support.privacyTitle'), - label_default: 'Privacy Policy Link:', - help_text: t('admin.support.privacyDesc'), - help_text_default: 'The URL for the Privacy link on the login and sign-up pages. If this field is empty, the Privacy link is hidden from users.', + label: defineMessage({id: 'admin.support.privacyTitle', defaultMessage: 'Privacy Policy Link:'}), + help_text: defineMessage({id: 'admin.support.privacyDesc', defaultMessage: 'The URL for the Privacy link on the login and sign-up pages. If this field is empty, the Privacy link is hidden from users.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), isHidden: it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), }, { type: 'text', key: 'SupportSettings.AboutLink', - label: t('admin.support.aboutTitle'), - label_default: 'About Link:', - help_text: t('admin.support.aboutDesc'), - help_text_default: 'The URL for the About link on the Mattermost login and sign-up pages. If this field is empty, the About link is hidden from users.', + label: defineMessage({id: 'admin.support.aboutTitle', defaultMessage: 'About Link:'}), + help_text: defineMessage({id: 'admin.support.aboutDesc', defaultMessage: 'The URL for the About link on the Mattermost login and sign-up pages. If this field is empty, the About link is hidden from users.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), isHidden: it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), }, { type: 'text', key: 'SupportSettings.ForgotPasswordLink', - label: t('admin.support.forgotPasswordTitle'), - label_default: 'Forgot Password Custom Link:', - help_text: t('admin.support.forgotPasswordDesc'), - help_text_default: 'The URL for the Forgot Password link on the Mattermost login page. If this field is empty the Forgot Password link takes users to the Password Reset page.', + label: defineMessage({id: 'admin.support.forgotPasswordTitle', defaultMessage: 'Forgot Password Custom Link:'}), + help_text: defineMessage({id: 'admin.support.forgotPasswordDesc', defaultMessage: 'The URL for the Forgot Password link on the Mattermost login page. If this field is empty the Forgot Password link takes users to the Password Reset page.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), isHidden: it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), }, { type: 'text', key: 'SupportSettings.ReportAProblemLink', - label: t('admin.support.problemTitle'), - label_default: 'Report a Problem Link:', - help_text: t('admin.support.problemDesc'), - help_text_default: 'The URL for the Report a Problem link in the Help Menu. If this field is empty, the link is removed from the Help Menu.', + label: defineMessage({id: 'admin.support.problemTitle', defaultMessage: 'Report a Problem Link:'}), + help_text: defineMessage({id: 'admin.support.problemDesc', defaultMessage: 'The URL for the Report a Problem link in the Help Menu. If this field is empty, the link is removed from the Help Menu.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), isHidden: it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), }, { type: 'text', key: 'NativeAppSettings.AppDownloadLink', - label: t('admin.customization.appDownloadLinkTitle'), - label_default: 'Mattermost Apps Download Page Link:', - help_text: t('admin.customization.appDownloadLinkDesc'), - help_text_default: 'Add a link to a download page for the Mattermost apps. When a link is present, an option to "Download Mattermost Apps" will be added in the Product Menu so users can find the download page. Leave this field blank to hide the option from the Product Menu.', + label: defineMessage({id: 'admin.customization.appDownloadLinkTitle', defaultMessage: 'Mattermost Apps Download Page Link:'}), + help_text: defineMessage({id: 'admin.customization.appDownloadLinkDesc', defaultMessage: 'Add a link to a download page for the Mattermost apps. When a link is present, an option to "Download Mattermost Apps" will be added in the Product Menu so users can find the download page. Leave this field blank to hide the option from the Product Menu.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), isHidden: it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), }, { type: 'text', key: 'NativeAppSettings.AndroidAppDownloadLink', - label: t('admin.customization.androidAppDownloadLinkTitle'), - label_default: 'Android App Download Link:', - help_text: t('admin.customization.androidAppDownloadLinkDesc'), - help_text_default: 'Add a link to download the Android app. Users who access the site on a mobile web browser will be prompted with a page giving them the option to download the app. Leave this field blank to prevent the page from appearing.', + label: defineMessage({id: 'admin.customization.androidAppDownloadLinkTitle', defaultMessage: 'Android App Download Link:'}), + help_text: defineMessage({id: 'admin.customization.androidAppDownloadLinkDesc', defaultMessage: 'Add a link to download the Android app. Users who access the site on a mobile web browser will be prompted with a page giving them the option to download the app. Leave this field blank to prevent the page from appearing.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), isHidden: it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), }, { type: 'text', key: 'NativeAppSettings.IosAppDownloadLink', - label: t('admin.customization.iosAppDownloadLinkTitle'), - label_default: 'iOS App Download Link:', - help_text: t('admin.customization.iosAppDownloadLinkDesc'), - help_text_default: 'Add a link to download the iOS app. Users who access the site on a mobile web browser will be prompted with a page giving them the option to download the app. Leave this field blank to prevent the page from appearing.', + label: defineMessage({id: 'admin.customization.iosAppDownloadLinkTitle', defaultMessage: 'iOS App Download Link:'}), + help_text: defineMessage({id: 'admin.customization.iosAppDownloadLinkDesc', defaultMessage: 'Add a link to download the iOS app. Users who access the site on a mobile web browser will be prompted with a page giving them the option to download the app. Leave this field blank to prevent the page from appearing.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), isHidden: it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), }, @@ -2244,40 +1846,32 @@ const AdminDefinition: AdminDefinitionType = { }, localization: { url: 'site_config/localization', - title: t('admin.sidebar.localization'), - title_default: 'Localization', + title: defineMessage({id: 'admin.sidebar.localization', defaultMessage: 'Localization'}), isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.SITE.LOCALIZATION)), schema: { id: 'LocalizationSettings', - name: t('admin.site.localization'), - name_default: 'Localization', + name: defineMessage({id: 'admin.site.localization', defaultMessage: 'Localization'}), settings: [ { type: 'language', key: 'LocalizationSettings.DefaultServerLocale', - label: t('admin.general.localization.serverLocaleTitle'), - label_default: 'Default Server Language:', - help_text: t('admin.general.localization.serverLocaleDescription'), - help_text_default: 'Default language for system messages.', + label: defineMessage({id: 'admin.general.localization.serverLocaleTitle', defaultMessage: 'Default Server Language:'}), + help_text: defineMessage({id: 'admin.general.localization.serverLocaleDescription', defaultMessage: 'Default language for system messages.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.LOCALIZATION)), }, { type: 'language', key: 'LocalizationSettings.DefaultClientLocale', - label: t('admin.general.localization.clientLocaleTitle'), - label_default: 'Default Client Language:', - help_text: t('admin.general.localization.clientLocaleDescription'), - help_text_default: 'Default language for newly created users and pages where the user hasn\'t logged in.', + label: defineMessage({id: 'admin.general.localization.clientLocaleTitle', defaultMessage: 'Default Client Language:'}), + help_text: defineMessage({id: 'admin.general.localization.clientLocaleDescription', defaultMessage: 'Default language for newly created users and pages where the user hasn\'t logged in.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.LOCALIZATION)), }, { type: 'language', key: 'LocalizationSettings.AvailableLocales', - label: t('admin.general.localization.availableLocalesTitle'), - label_default: 'Available Languages:', - help_text: t('admin.general.localization.availableLocalesDescription'), + label: defineMessage({id: 'admin.general.localization.availableLocalesTitle', defaultMessage: 'Available Languages:'}), + help_text: defineMessage({id: 'admin.general.localization.availableLocalesDescription', defaultMessage: 'Set which languages are available for users in Settings > Display > Language (leave this field blank to have all supported languages available). If you\'re manually adding new languages, the Default Client Language must be added before saving this setting.\n \nWould like to help with translations? Join the Mattermost Translation Server to contribute.'}), help_text_markdown: false, - help_text_default: 'Set which languages are available for users in Settings > Display > Language (leave this field blank to have all supported languages available). If you\'re manually adding new languages, the Default Client Language must be added before saving this setting.\n \nWould like to help with translations? Join the Mattermost Translation Server to contribute.', help_text_values: { link: (msg: string) => ( {msg}, }, multiple: true, - no_result: t('admin.general.localization.availableLocalesNoResults'), - no_result_default: 'No results found', - not_present: t('admin.general.localization.availableLocalesNotPresent'), - not_present_default: 'The default client language must be included in the available list', + no_result: defineMessage({id: 'admin.general.localization.availableLocalesNoResults', defaultMessage: 'No results found'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.LOCALIZATION)), }, ], @@ -2301,62 +1892,48 @@ const AdminDefinition: AdminDefinitionType = { }, users_and_teams: { url: 'site_config/users_and_teams', - title: t('admin.sidebar.usersAndTeams'), - title_default: 'Users and Teams', + title: defineMessage({id: 'admin.sidebar.usersAndTeams', defaultMessage: 'Users and Teams'}), isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)), schema: { id: 'UserAndTeamsSettings', - name: t('admin.site.usersAndTeams'), - name_default: 'Users and Teams', + name: defineMessage({id: 'admin.site.usersAndTeams', defaultMessage: 'Users and Teams'}), settings: [ { type: 'number', key: 'TeamSettings.MaxUsersPerTeam', - label: t('admin.team.maxUsersTitle'), - label_default: 'Max Users Per Team:', - help_text: t('admin.team.maxUsersDescription'), - help_text_default: 'Maximum total number of users per team, including both active and inactive users.', - placeholder: t('admin.team.maxUsersExample'), - placeholder_default: 'E.g.: "25"', + label: defineMessage({id: 'admin.team.maxUsersTitle', defaultMessage: 'Max Users Per Team:'}), + help_text: defineMessage({id: 'admin.team.maxUsersDescription', defaultMessage: 'Maximum total number of users per team, including both active and inactive users.'}), + placeholder: defineMessage({id: 'admin.team.maxUsersExample', defaultMessage: 'E.g.: "25"'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)), }, { type: 'number', key: 'TeamSettings.MaxChannelsPerTeam', - label: t('admin.team.maxChannelsTitle'), - label_default: 'Max Channels Per Team:', - help_text: t('admin.team.maxChannelsDescription'), - help_text_default: 'Maximum total number of channels per team, including both active and archived channels.', - placeholder: t('admin.team.maxChannelsExample'), - placeholder_default: 'E.g.: "100"', + label: defineMessage({id: 'admin.team.maxChannelsTitle', defaultMessage: 'Max Channels Per Team:'}), + help_text: defineMessage({id: 'admin.team.maxChannelsDescription', defaultMessage: 'Maximum total number of channels per team, including both active and archived channels.'}), + placeholder: defineMessage({id: 'admin.team.maxChannelsExample', defaultMessage: 'E.g.: "100"'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)), }, { type: 'bool', key: 'TeamSettings.EnableJoinLeaveMessageByDefault', - label: t('admin.team.enableJoinLeaveMessageTitle'), - label_default: 'Enable join/leave messages by default:', - help_text: t('admin.team.enableJoinLeaveMessageDescription'), - help_text_default: 'Choose the default configuration of system messages displayed when users join or leave channels. Users can override this default by configuring Join/Leave messages in Account Settings > Advanced.', + label: defineMessage({id: 'admin.team.enableJoinLeaveMessageTitle', defaultMessage: 'Enable join/leave messages by default:'}), + help_text: defineMessage({id: 'admin.team.enableJoinLeaveMessageDescription', defaultMessage: 'Choose the default configuration of system messages displayed when users join or leave channels. Users can override this default by configuring Join/Leave messages in Account Settings > Advanced.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)), }, { type: 'dropdown', key: 'TeamSettings.RestrictDirectMessage', - label: t('admin.team.restrictDirectMessage'), - label_default: 'Enable users to open Direct Message channels with:', - help_text: t('admin.team.restrictDirectMessageDesc'), - help_text_default: '"Any user on the Mattermost server" enables users to open a Direct Message channel with any user on the server, even if they are not on any teams together. "Any member of the team" limits the ability in the Direct Messages "More" menu to only open Direct Message channels with users who are in the same team.\n \nNote: This setting only affects the UI, not permissions on the server.', + label: defineMessage({id: 'admin.team.restrictDirectMessage', defaultMessage: 'Enable users to open Direct Message channels with:'}), + help_text: defineMessage({id: 'admin.team.restrictDirectMessageDesc', defaultMessage: '"Any user on the Mattermost server" enables users to open a Direct Message channel with any user on the server, even if they are not on any teams together. "Any member of the team" limits the ability in the Direct Messages "More" menu to only open Direct Message channels with users who are in the same team. Note: This setting only affects the UI, not permissions on the server.'}), options: [ { value: 'any', - display_name: t('admin.team.restrict_direct_message_any'), - display_name_default: 'Any user on the Mattermost server', + display_name: defineMessage({id: 'admin.team.restrict_direct_message_any', defaultMessage: 'Any user on the Mattermost server'}), }, { value: 'team', - display_name: t('admin.team.restrict_direct_message_team'), - display_name_default: 'Any member of the team', + display_name: defineMessage({id: 'admin.team.restrict_direct_message_team', defaultMessage: 'Any member of the team'}), }, ], isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)), @@ -2364,25 +1941,20 @@ const AdminDefinition: AdminDefinitionType = { { type: 'dropdown', key: 'TeamSettings.TeammateNameDisplay', - label: t('admin.team.teammateNameDisplay'), - label_default: 'Teammate Name Display:', - help_text: t('admin.team.teammateNameDisplayDesc'), - help_text_default: 'Set how to display users\' names in posts and the Direct Messages list.', + label: defineMessage({id: 'admin.team.teammateNameDisplay', defaultMessage: 'Teammate Name Display:'}), + help_text: defineMessage({id: 'admin.team.teammateNameDisplayDesc', defaultMessage: 'Set how to display users\' names in posts and the Direct Messages list.'}), options: [ { value: Constants.TEAMMATE_NAME_DISPLAY.SHOW_USERNAME, - display_name: t('admin.team.showUsername'), - display_name_default: 'Show username (default)', + display_name: defineMessage({id: 'admin.team.showUsername', defaultMessage: 'Show username (default)'}), }, { value: Constants.TEAMMATE_NAME_DISPLAY.SHOW_NICKNAME_FULLNAME, - display_name: t('admin.team.showNickname'), - display_name_default: 'Show nickname if one exists, otherwise show first and last name', + display_name: defineMessage({id: 'admin.team.showNickname', defaultMessage: 'Show nickname if one exists, otherwise show first and last name'}), }, { value: Constants.TEAMMATE_NAME_DISPLAY.SHOW_FULLNAME, - display_name: t('admin.team.showFullname'), - display_name_default: 'Show first and last name', + display_name: defineMessage({id: 'admin.team.showFullname', defaultMessage: 'Show first and last name'}), }, ], isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)), @@ -2390,10 +1962,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'TeamSettings.LockTeammateNameDisplay', - label: t('admin.lockTeammateNameDisplay'), - label_default: 'Lock Teammate Name Display for all users: ', - help_text: t('admin.lockTeammateNameDisplayHelpText'), - help_text_default: 'When true, disables users\' ability to change settings under Account Menu > Account Settings > Display > Teammate Name Display.', + label: defineMessage({id: 'admin.lockTeammateNameDisplay', defaultMessage: 'Lock Teammate Name Display for all users: '}), + help_text: defineMessage({id: 'admin.lockTeammateNameDisplayHelpText', defaultMessage: 'When true, disables users\' ability to change settings under Account Menu > Account Settings > Display > Teammate Name Display.'}), help_text_values: { strong: (msg: string) => {msg}, }, @@ -2403,56 +1973,44 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'TeamSettings.ExperimentalViewArchivedChannels', - label: t('admin.viewArchivedChannelsTitle'), - label_default: 'Allow users to view archived channels: ', - help_text: t('admin.viewArchivedChannelsHelpText'), - help_text_default: 'When true, allows users to view, share and search for content of channels that have been archived. Users can only view the content in channels of which they were a member before the channel was archived.', + label: defineMessage({id: 'admin.viewArchivedChannelsTitle', defaultMessage: 'Allow users to view archived channels: '}), + help_text: defineMessage({id: 'admin.viewArchivedChannelsHelpText', defaultMessage: 'When true, allows users to view, share and search for content of channels that have been archived. Users can only view the content in channels of which they were a member before the channel was archived.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)), isHidden: it.licensedForFeature('Cloud'), }, { type: 'bool', key: 'PrivacySettings.ShowEmailAddress', - label: t('admin.privacy.showEmailTitle'), - label_default: 'Show Email Address:', - help_text: t('admin.privacy.showEmailDescription'), - help_text_default: 'When false, hides the email address of members from everyone except System Administrators and the System Roles with read/write access to Compliance, Billing, or User Management.', + label: defineMessage({id: 'admin.privacy.showEmailTitle', defaultMessage: 'Show Email Address:'}), + help_text: defineMessage({id: 'admin.privacy.showEmailDescription', defaultMessage: 'When false, hides the email address of members from everyone except System Administrators and the System Roles with read/write access to Compliance, Billing, or User Management.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)), }, { type: 'bool', key: 'PrivacySettings.ShowFullName', - label: t('admin.privacy.showFullNameTitle'), - label_default: 'Show Full Name:', - help_text: t('admin.privacy.showFullNameDescription'), - help_text_default: 'When false, hides the full name of members from everyone except System Administrators. Username is shown in place of full name.', + label: defineMessage({id: 'admin.privacy.showFullNameTitle', defaultMessage: 'Show Full Name:'}), + help_text: defineMessage({id: 'admin.privacy.showFullNameDescription', defaultMessage: 'When false, hides the full name of members from everyone except System Administrators. Username is shown in place of full name.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)), }, { type: 'bool', key: 'TeamSettings.EnableCustomUserStatuses', - label: t('admin.team.customUserStatusesTitle'), - label_default: 'Enable Custom Statuses: ', - help_text: t('admin.team.customUserStatusesDescription'), - help_text_default: 'When true, users can set a descriptive status message and status emoji visible to all users.', + label: defineMessage({id: 'admin.team.customUserStatusesTitle', defaultMessage: 'Enable Custom Statuses: '}), + help_text: defineMessage({id: 'admin.team.customUserStatusesDescription', defaultMessage: 'When true, users can set a descriptive status message and status emoji visible to all users.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)), }, { type: 'bool', key: 'TeamSettings.EnableLastActiveTime', - label: t('admin.team.lastActiveTimeTitle'), - label_default: 'Enable last active time: ', - help_text: t('admin.team.lastActiveTimeDescription'), - help_text_default: 'When enabled, last active time allows users to see when someone was last online.', + label: defineMessage({id: 'admin.team.lastActiveTimeTitle', defaultMessage: 'Enable last active time: '}), + help_text: defineMessage({id: 'admin.team.lastActiveTimeDescription', defaultMessage: 'When enabled, last active time allows users to see when someone was last online.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)), }, { type: 'bool', key: 'ServiceSettings.EnableCustomGroups', - label: t('admin.team.customUserGroupsTitle'), - label_default: 'Enable Custom User Groups (Beta): ', - help_text: t('admin.team.customUserGroupsDescription'), - help_text_default: 'When true, users with appropriate permissions can create custom user groups and enables at-mentions for those groups.', + label: defineMessage({id: 'admin.team.customUserGroupsTitle', defaultMessage: 'Enable Custom User Groups (Beta): '}), + help_text: defineMessage({id: 'admin.team.customUserGroupsDescription', defaultMessage: 'When true, users with appropriate permissions can create custom user groups and enables at-mentions for those groups.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)), isHidden: it.not(it.any( it.licensedForSku(LicenseSkus.Enterprise), @@ -2462,12 +2020,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'ServiceSettings.RefreshPostStatsRunTime', - label: t('admin.team.refreshPostStatsRunTimeTitle'), - label_default: 'User Statistics Update Time:', - help_text: t('admin.team.refreshPostStatsRunTimeDescription'), - help_text_default: "Set the server time for updating the user post statistics, including each user's total post count and the timestamp of their most recent post. Must be a 24-hour time stamp in the form HH:MM based on the local time of the server.", - placeholder: t('admin.team.refreshPostStatsRunTimeExample'), - placeholder_default: 'E.g.: "00:00"', + label: defineMessage({id: 'admin.team.refreshPostStatsRunTimeTitle', defaultMessage: 'User Statistics Update Time:'}), + help_text: defineMessage({id: 'admin.team.refreshPostStatsRunTimeDescription', defaultMessage: "Set the server time for updating the user post statistics, including each user's total post count and the timestamp of their most recent post. Must be a 24-hour time stamp in the form HH:MM based on the local time of the server."}), + placeholder: defineMessage({id: 'admin.team.refreshPostStatsRunTimeExample', defaultMessage: 'E.g.: "00:00"'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)), }, ], @@ -2475,40 +2030,32 @@ const AdminDefinition: AdminDefinitionType = { }, notifications: { url: 'environment/notifications', - title: t('admin.sidebar.notifications'), - title_default: 'Notifications', + title: defineMessage({id: 'admin.sidebar.notifications', defaultMessage: 'Notifications'}), isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.SITE.NOTIFICATIONS)), schema: { id: 'notifications', - name: t('admin.environment.notifications'), - name_default: 'Notifications', + name: defineMessage({id: 'admin.environment.notifications', defaultMessage: 'Notifications'}), settings: [ { type: 'bool', key: 'TeamSettings.EnableConfirmNotificationsToChannel', - label: t('admin.environment.notifications.enableConfirmNotificationsToChannel.label'), - label_default: 'Show @channel, @all, @here and group mention confirmation dialog:', - help_text: t('admin.environment.notifications.enableConfirmNotificationsToChannel.help'), - help_text_default: 'When true, users will be prompted to confirm when posting @channel, @all, @here and group mentions in channels with over five members. When false, no confirmation is required.', + label: defineMessage({id: 'admin.environment.notifications.enableConfirmNotificationsToChannel.label', defaultMessage: 'Show @channel, @all, @here and group mention confirmation dialog:'}), + help_text: defineMessage({id: 'admin.environment.notifications.enableConfirmNotificationsToChannel.help', defaultMessage: 'When true, users will be prompted to confirm when posting @channel, @all, @here and group mentions in channels with over five members. When false, no confirmation is required.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.NOTIFICATIONS)), }, { type: 'bool', key: 'EmailSettings.SendEmailNotifications', - label: t('admin.environment.notifications.enable.label'), - label_default: 'Enable Email Notifications:', - help_text: t('admin.environment.notifications.enable.help'), - help_text_default: 'Typically set to true in production. When true, Mattermost attempts to send email notifications. When false, email invitations and user account setting change emails are still sent as long as the SMTP server is configured. Developers may set this field to false to skip email setup for faster development.', + label: defineMessage({id: 'admin.environment.notifications.enable.label', defaultMessage: 'Enable Email Notifications:'}), + help_text: defineMessage({id: 'admin.environment.notifications.enable.help', defaultMessage: 'Typically set to true in production. When true, Mattermost attempts to send email notifications. When false, email invitations and user account setting change emails are still sent as long as the SMTP server is configured. Developers may set this field to false to skip email setup for faster development.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.NOTIFICATIONS)), isHidden: it.licensedForFeature('Cloud'), }, { type: 'bool', key: 'EmailSettings.EnablePreviewModeBanner', - label: t('admin.environment.notifications.enablePreviewModeBanner.label'), - label_default: 'Enable Preview Mode Banner:', - help_text: t('admin.environment.notifications.enablePreviewModeBanner.help'), - help_text_default: 'When true, the Preview Mode banner is displayed so users are aware that email notifications are disabled. When false, the Preview Mode banner is not displayed to users.', + label: defineMessage({id: 'admin.environment.notifications.enablePreviewModeBanner.label', defaultMessage: 'Enable Preview Mode Banner:'}), + help_text: defineMessage({id: 'admin.environment.notifications.enablePreviewModeBanner.help', defaultMessage: 'When true, the Preview Mode banner is displayed so users are aware that email notifications are disabled. When false, the Preview Mode banner is not displayed to users.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.NOTIFICATIONS)), it.stateIsTrue('EmailSettings.SendEmailNotifications'), @@ -2518,10 +2065,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'EmailSettings.EnableEmailBatching', - label: t('admin.environment.notifications.enableEmailBatching.label'), - label_default: 'Enable Email Batching:', - help_text: t('admin.environment.notifications.enableEmailBatching.help'), - help_text_default: 'When true, users will have email notifications for multiple direct messages and mentions combined into a single email. Batching will occur at a default interval of 15 minutes, configurable in Settings > Notifications.', + label: defineMessage({id: 'admin.environment.notifications.enableEmailBatching.label', defaultMessage: 'Enable Email Batching:'}), + help_text: defineMessage({id: 'admin.environment.notifications.enableEmailBatching.help', defaultMessage: 'When true, users will have email notifications for multiple direct messages and mentions combined into a single email. Batching will occur at a default interval of 15 minutes, configurable in Settings > Notifications.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.NOTIFICATIONS)), it.stateIsFalse('EmailSettings.SendEmailNotifications'), @@ -2533,21 +2078,17 @@ const AdminDefinition: AdminDefinitionType = { { type: 'dropdown', key: 'EmailSettings.EmailNotificationContentsType', - label: t('admin.environment.notifications.contents.label'), - label_default: 'Email Notification Contents:', - help_text: t('admin.environment.notifications.contents.help'), - help_text_default: '**Send full message contents** - Sender name and channel are included in email notifications.\n **Send generic description with only sender name** - Only the name of the person who sent the message, with no information about channel name or message contents are included in email notifications. Typically used for compliance reasons if Mattermost contains confidential information and policy dictates it cannot be stored in email.', + label: defineMessage({id: 'admin.environment.notifications.contents.label', defaultMessage: 'Email Notification Contents:'}), + help_text: defineMessage({id: 'admin.environment.notifications.contents.help', defaultMessage: '**Send full message contents** - Sender name and channel are included in email notifications. **Send generic description with only sender name** - Only the name of the person who sent the message, with no information about channel name or message contents are included in email notifications. Typically used for compliance reasons if Mattermost contains confidential information and policy dictates it cannot be stored in email.'}), help_text_markdown: true, options: [ { value: 'full', - display_name: t('admin.environment.notifications.contents.full'), - display_name_default: 'Send full message contents', + display_name: defineMessage({id: 'admin.environment.notifications.contents.full', defaultMessage: 'Send full message contents'}), }, { value: 'generic', - display_name: t('admin.environment.notifications.contents.generic'), - display_name_default: 'Send generic description with only sender name', + display_name: defineMessage({id: 'admin.environment.notifications.contents.generic', defaultMessage: 'Send generic description with only sender name'}), }, ], isHidden: it.not(it.licensedForFeature('EmailNotificationContents')), @@ -2556,55 +2097,43 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'EmailSettings.FeedbackName', - label: t('admin.environment.notifications.notificationDisplay.label'), - label_default: 'Notification Display Name:', - placeholder: t('admin.environment.notifications.notificationDisplay.placeholder'), - placeholder_default: 'Ex: "Mattermost Notification", "System", "No-Reply"', - help_text: t('admin.environment.notifications.notificationDisplay.help'), - help_text_default: 'Display name on email account used when sending notification emails from Mattermost.', + label: defineMessage({id: 'admin.environment.notifications.notificationDisplay.label', defaultMessage: 'Notification Display Name:'}), + placeholder: defineMessage({id: 'admin.environment.notifications.notificationDisplay.placeholder', defaultMessage: 'Ex: "Mattermost Notification", "System", "No-Reply"'}), + help_text: defineMessage({id: 'admin.environment.notifications.notificationDisplay.help', defaultMessage: 'Display name on email account used when sending notification emails from Mattermost.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.NOTIFICATIONS)), it.stateIsFalse('EmailSettings.SendEmailNotifications'), ), - validate: validators.isRequired(t('admin.environment.notifications.notificationDisplay.required'), '"Notification Display Name" is required'), + validate: validators.isRequired(defineMessage({id: 'admin.environment.notifications.notificationDisplay.required', defaultMessage: '"Notification Display Name" is required'})), }, { type: 'text', key: 'EmailSettings.FeedbackEmail', - label: t('admin.environment.notifications.feedbackEmail.label'), - label_default: 'Notification From Address:', - placeholder: t('admin.environment.notifications.feedbackEmail.placeholder'), - placeholder_default: 'Ex: "mattermost@yourcompany.com", "admin@yourcompany.com"', - help_text: t('admin.environment.notifications.feedbackEmail.help'), - help_text_default: 'Email address displayed on email account used when sending notification emails from Mattermost.', + label: defineMessage({id: 'admin.environment.notifications.feedbackEmail.label', defaultMessage: 'Notification From Address:'}), + placeholder: defineMessage({id: 'admin.environment.notifications.feedbackEmail.placeholder', defaultMessage: 'Ex: "mattermost@yourcompany.com", "admin@yourcompany.com"'}), + help_text: defineMessage({id: 'admin.environment.notifications.feedbackEmail.help', defaultMessage: 'Email address displayed on email account used when sending notification emails from Mattermost.'}), isHidden: it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.NOTIFICATIONS)), it.stateIsFalse('EmailSettings.SendEmailNotifications'), ), - validate: validators.isRequired(t('admin.environment.notifications.feedbackEmail.required'), '"Notification From Address" is required'), + validate: validators.isRequired(defineMessage({id: 'admin.environment.notifications.feedbackEmail.required', defaultMessage: '"Notification From Address" is required'})), }, { type: 'text', key: 'SupportSettings.SupportEmail', - label: t('admin.environment.notifications.supportEmail.label'), - label_default: 'Support Email Address:', - placeholder: t('admin.environment.notifications.supportAddress.placeholder'), - placeholder_default: 'Ex: "support@yourcompany.com", "admin@yourcompany.com"', - help_text: t('admin.environment.notifications.supportEmail.help'), - help_text_default: 'Email address displayed on support emails.', + label: defineMessage({id: 'admin.environment.notifications.supportEmail.label', defaultMessage: 'Support Email Address:'}), + placeholder: defineMessage({id: 'admin.environment.notifications.supportAddress.placeholder', defaultMessage: 'Ex: "support@yourcompany.com", "admin@yourcompany.com"'}), + help_text: defineMessage({id: 'admin.environment.notifications.supportEmail.help', defaultMessage: 'Email address displayed on support emails.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.CUSTOMIZATION)), - validate: validators.isRequired(t('admin.environment.notifications.supportEmail.required'), '"Support Email Address" is required'), + validate: validators.isRequired(defineMessage({id: 'admin.environment.notifications.supportEmail.required', defaultMessage: '"Support Email Address" is required'})), }, { type: 'text', key: 'EmailSettings.ReplyToAddress', - label: t('admin.environment.notifications.replyToAddress.label'), - label_default: 'Notification Reply-To Address:', - placeholder: t('admin.environment.notifications.replyToAddress.placeholder'), - placeholder_default: 'Ex: "mattermost@yourcompany.com", "admin@yourcompany.com"', - help_text: t('admin.environment.notifications.replyToAddress.help'), - help_text_default: 'Email address used in the Reply-To header when sending notification emails from Mattermost.', + label: defineMessage({id: 'admin.environment.notifications.replyToAddress.label', defaultMessage: 'Notification Reply-To Address:'}), + placeholder: defineMessage({id: 'admin.environment.notifications.replyToAddress.placeholder', defaultMessage: 'Ex: "mattermost@yourcompany.com", "admin@yourcompany.com"'}), + help_text: defineMessage({id: 'admin.environment.notifications.replyToAddress.help', defaultMessage: 'Email address used in the Reply-To header when sending notification emails from Mattermost.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.NOTIFICATIONS)), it.stateIsFalse('EmailSettings.SendEmailNotifications'), @@ -2613,12 +2142,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'EmailSettings.FeedbackOrganization', - label: t('admin.environment.notifications.feedbackOrganization.label'), - label_default: 'Notification Footer Mailing Address:', - placeholder: t('admin.environment.notifications.feedbackOrganization.placeholder'), - placeholder_default: 'Ex: "© ABC Corporation, 565 Knight Way, Palo Alto, California, 94305, USA"', - help_text: t('admin.environment.notifications.feedbackOrganization.help'), - help_text_default: 'Organization name and address displayed on email notifications from Mattermost, such as "© ABC Corporation, 565 Knight Way, Palo Alto, California, 94305, USA". If the field is left empty, the organization name and address will not be displayed.', + label: defineMessage({id: 'admin.environment.notifications.feedbackOrganization.label', defaultMessage: 'Notification Footer Mailing Address:'}), + placeholder: defineMessage({id: 'admin.environment.notifications.feedbackOrganization.placeholder', defaultMessage: 'Ex: "© ABC Corporation, 565 Knight Way, Palo Alto, California, 94305, USA"'}), + help_text: defineMessage({id: 'admin.environment.notifications.feedbackOrganization.help', defaultMessage: 'Organization name and address displayed on email notifications from Mattermost, such as "© ABC Corporation, 565 Knight Way, Palo Alto, California, 94305, USA". If the field is left empty, the organization name and address will not be displayed.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.NOTIFICATIONS)), it.stateIsFalse('EmailSettings.SendEmailNotifications'), @@ -2627,26 +2153,21 @@ const AdminDefinition: AdminDefinitionType = { { type: 'dropdown', key: 'EmailSettings.PushNotificationContents', - label: t('admin.environment.notifications.pushContents.label'), - label_default: 'Push Notification Contents:', - help_text: t('admin.environment.notifications.pushContents.help'), - help_text_default: '**Generic description with only sender name** - Includes only the name of the person who sent the message in push notifications, with no information about channel name or message contents.\n **Generic description with sender and channel names** - Includes the name of the person who sent the message and the channel it was sent in, but not the message contents.\n **Full message content sent in the notification payload** - Includes the message contents in the push notification payload that is relayed through Apple\'s Push Notification Service (APNS) or Google\'s Firebase Cloud Messaging (FCM). It is **highly recommended** this option only be used with an "https" protocol to encrypt the connection and protect confidential information sent in messages.', + label: defineMessage({id: 'admin.environment.notifications.pushContents.label', defaultMessage: 'Push Notification Contents:'}), + help_text: defineMessage({id: 'admin.environment.notifications.pushContents.help', defaultMessage: "**Generic description with only sender name** - Includes only the name of the person who sent the message in push notifications, with no information about channel name or message contents. **Generic description with sender and channel names** - Includes the name of the person who sent the message and the channel it was sent in, but not the message contents. **Full message content sent in the notification payload** - Includes the message contents in the push notification payload that is relayed through Apple's Push Notification Service (APNS) or Google's Firebase Cloud Messaging (FCM). It is **highly recommended** this option only be used with an \"https\" protocol to encrypt the connection and protect confidential information sent in messages."}), help_text_markdown: true, options: [ { value: 'generic_no_channel', - display_name: t('admin.environment.notifications.pushContents.genericNoChannel'), - display_name_default: 'Generic description with only sender name', + display_name: defineMessage({id: 'admin.environment.notifications.pushContents.genericNoChannel', defaultMessage: 'Generic description with only sender name'}), }, { value: 'generic', - display_name: t('admin.environment.notifications.pushContents.generic'), - display_name_default: 'Generic description with sender and channel names', + display_name: defineMessage({id: 'admin.environment.notifications.pushContents.generic', defaultMessage: 'Generic description with sender and channel names'}), }, { value: 'full', - display_name: t('admin.environment.notifications.pushContents.full'), - display_name_default: 'Full message content sent in the notification payload', + display_name: defineMessage({id: 'admin.environment.notifications.pushContents.full', defaultMessage: 'Full message content sent in the notification payload'}), }, ], isHidden: it.licensedForFeature('IDLoadedPushNotifications'), @@ -2655,31 +2176,25 @@ const AdminDefinition: AdminDefinitionType = { { type: 'dropdown', key: 'EmailSettings.PushNotificationContents', - label: t('admin.environment.notifications.pushContents.label'), - label_default: 'Push Notification Contents:', - help_text: t('admin.environment.notifications.pushContents.withIdLoaded.help'), - help_text_default: '**Generic description with only sender name** - Includes only the name of the person who sent the message in push notifications, with no information about channel name or message contents.\n **Generic description with sender and channel names** - Includes the name of the person who sent the message and the channel it was sent in, but not the message contents.\n **Full message content sent in the notification payload** - Includes the message contents in the push notification payload that is relayed through Apple\'s Push Notification Service (APNS) or Google\'s Firebase Cloud Messaging (FCM). It is **highly recommended** this option only be used with an "https" protocol to encrypt the connection and protect confidential information sent in messages.\n **Full message content fetched from the server on receipt** - The notification payload relayed through APNS or FCM contains no message content, instead it contains a unique message ID used to fetch message content from the server when a push notification is received by a device. If the server cannot be reached, a generic notification will be displayed.', + label: defineMessage({id: 'admin.environment.notifications.pushContents.label', defaultMessage: 'Push Notification Contents:'}), + help_text: defineMessage({id: 'admin.environment.notifications.pushContents.withIdLoaded.help', defaultMessage: "**Generic description with only sender name** - Includes only the name of the person who sent the message in push notifications, with no information about channel name or message contents. **Generic description with sender and channel names** - Includes the name of the person who sent the message and the channel it was sent in, but not the message contents. **Full message content sent in the notification payload** - Includes the message contents in the push notification payload that is relayed through Apple's Push Notification Service (APNS) or Google's Firebase Cloud Messaging (FCM). It is **highly recommended** this option only be used with an \"https\" protocol to encrypt the connection and protect confidential information sent in messages. **Full message content fetched from the server on receipt** - The notification payload relayed through APNS or FCM contains no message content, instead it contains a unique message ID used to fetch message content from the server when a push notification is received by a device. If the server cannot be reached, a generic notification will be displayed."}), help_text_markdown: true, options: [ { value: 'generic_no_channel', - display_name: t('admin.environment.notifications.pushContents.genericNoChannel'), - display_name_default: 'Generic description with only sender name', + display_name: defineMessage({id: 'admin.environment.notifications.pushContents.genericNoChannel', defaultMessage: 'Generic description with only sender name'}), }, { value: 'generic', - display_name: t('admin.environment.notifications.pushContents.generic'), - display_name_default: 'Generic description with sender and channel names', + display_name: defineMessage({id: 'admin.environment.notifications.pushContents.generic', defaultMessage: 'Generic description with sender and channel names'}), }, { value: 'full', - display_name: t('admin.environment.notifications.pushContents.full'), - display_name_default: 'Full message content sent in the notification payload', + display_name: defineMessage({id: 'admin.environment.notifications.pushContents.full', defaultMessage: 'Full message content sent in the notification payload'}), }, { value: 'id_loaded', - display_name: t('admin.environment.notifications.pushContents.idLoaded'), - display_name_default: 'Full message content fetched from the server on receipt', + display_name: defineMessage({id: 'admin.environment.notifications.pushContents.idLoaded', defaultMessage: 'Full message content fetched from the server on receipt'}), }, ], isHidden: it.not(it.licensedForFeature('IDLoadedPushNotifications')), @@ -2690,33 +2205,27 @@ const AdminDefinition: AdminDefinitionType = { }, announcement_banner: { url: 'site_config/announcement_banner', - title: t('admin.sidebar.announcement'), - title_default: 'Announcement Banner', + title: defineMessage({id: 'admin.sidebar.announcement', defaultMessage: 'Announcement Banner'}), isHidden: it.any( it.not(it.licensedForFeature('Announcement')), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.SITE.ANNOUNCEMENT_BANNER)), ), schema: { id: 'AnnouncementSettings', - name: t('admin.site.announcementBanner'), - name_default: 'Announcement Banner', + name: defineMessage({id: 'admin.site.announcementBanner', defaultMessage: 'Announcement Banner'}), settings: [ { type: 'bool', key: 'AnnouncementSettings.EnableBanner', - label: t('admin.customization.announcement.enableBannerTitle'), - label_default: 'Enable Announcement Banner:', - help_text: t('admin.customization.announcement.enableBannerDesc'), - help_text_default: 'Enable an announcement banner across all teams.', + label: defineMessage({id: 'admin.customization.announcement.enableBannerTitle', defaultMessage: 'Enable Announcement Banner:'}), + help_text: defineMessage({id: 'admin.customization.announcement.enableBannerDesc', defaultMessage: 'Enable an announcement banner across all teams.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.ANNOUNCEMENT_BANNER)), }, { type: 'text', key: 'AnnouncementSettings.BannerText', - label: t('admin.customization.announcement.bannerTextTitle'), - label_default: 'Banner Text:', - help_text: t('admin.customization.announcement.bannerTextDesc'), - help_text_default: 'Text that will appear in the announcement banner.', + label: defineMessage({id: 'admin.customization.announcement.bannerTextTitle', defaultMessage: 'Banner Text:'}), + help_text: defineMessage({id: 'admin.customization.announcement.bannerTextDesc', defaultMessage: 'Text that will appear in the announcement banner.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.ANNOUNCEMENT_BANNER)), it.stateIsFalse('AnnouncementSettings.EnableBanner'), @@ -2725,8 +2234,7 @@ const AdminDefinition: AdminDefinitionType = { { type: 'color', key: 'AnnouncementSettings.BannerColor', - label: t('admin.customization.announcement.bannerColorTitle'), - label_default: 'Banner Color:', + label: defineMessage({id: 'admin.customization.announcement.bannerColorTitle', defaultMessage: 'Banner Color:'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.ANNOUNCEMENT_BANNER)), it.stateIsFalse('AnnouncementSettings.EnableBanner'), @@ -2735,8 +2243,7 @@ const AdminDefinition: AdminDefinitionType = { { type: 'color', key: 'AnnouncementSettings.BannerTextColor', - label: t('admin.customization.announcement.bannerTextColorTitle'), - label_default: 'Banner Text Color:', + label: defineMessage({id: 'admin.customization.announcement.bannerTextColorTitle', defaultMessage: 'Banner Text Color:'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.ANNOUNCEMENT_BANNER)), it.stateIsFalse('AnnouncementSettings.EnableBanner'), @@ -2745,10 +2252,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'AnnouncementSettings.AllowBannerDismissal', - label: t('admin.customization.announcement.allowBannerDismissalTitle'), - label_default: 'Allow Banner Dismissal:', - help_text: t('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.', + label: defineMessage({id: 'admin.customization.announcement.allowBannerDismissalTitle', defaultMessage: 'Allow Banner Dismissal:'}), + help_text: defineMessage({id: 'admin.customization.announcement.allowBannerDismissalDesc', defaultMessage: '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.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.ANNOUNCEMENT_BANNER)), it.stateIsFalse('AnnouncementSettings.EnableBanner'), @@ -2761,16 +2266,14 @@ const AdminDefinition: AdminDefinitionType = { announcement_banner_feature_discovery: { url: 'site_config/announcement_banner', isDiscovery: true, - title: t('admin.sidebar.announcement'), - title_default: 'Announcement Banner', + title: defineMessage({id: 'admin.sidebar.announcement', defaultMessage: 'Announcement Banner'}), isHidden: it.any( it.licensedForFeature('Announcement'), it.not(it.enterpriseReady), ), schema: { id: 'AnnouncementSettings', - name: t('admin.site.announcementBanner'), - name_default: 'Announcement Banner', + name: defineMessage({id: 'admin.site.announcementBanner', defaultMessage: 'Announcement Banner'}), settings: [ { type: 'custom', @@ -2784,30 +2287,24 @@ const AdminDefinition: AdminDefinitionType = { }, emoji: { url: 'site_config/emoji', - title: t('admin.sidebar.emoji'), - title_default: 'Emoji', + title: defineMessage({id: 'admin.sidebar.emoji', defaultMessage: 'Emoji'}), isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.SITE.EMOJI)), schema: { id: 'EmojiSettings', - name: t('admin.site.emoji'), - name_default: 'Emoji', + name: defineMessage({id: 'admin.site.emoji', defaultMessage: 'Emoji'}), settings: [ { type: 'bool', key: 'ServiceSettings.EnableEmojiPicker', - label: t('admin.customization.enableEmojiPickerTitle'), - label_default: 'Enable Emoji Picker:', - help_text: t('admin.customization.enableEmojiPickerDesc'), - help_text_default: 'The emoji picker allows users to select emoji to add as reactions or use in messages. Enabling the emoji picker with a large number of custom emoji may slow down performance.', + label: defineMessage({id: 'admin.customization.enableEmojiPickerTitle', defaultMessage: 'Enable Emoji Picker:'}), + help_text: defineMessage({id: 'admin.customization.enableEmojiPickerDesc', defaultMessage: 'The emoji picker allows users to select emoji to add as reactions or use in messages. Enabling the emoji picker with a large number of custom emoji may slow down performance.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.EMOJI)), }, { type: 'bool', key: 'ServiceSettings.EnableCustomEmoji', - label: t('admin.customization.enableCustomEmojiTitle'), - label_default: 'Enable Custom Emoji:', - help_text: t('admin.customization.enableCustomEmojiDesc'), - help_text_default: 'Enable users to create custom emoji for use in messages. When enabled, custom emoji settings can be accessed in Channels through the emoji picker.', + label: defineMessage({id: 'admin.customization.enableCustomEmojiTitle', defaultMessage: 'Enable Custom Emoji:'}), + help_text: defineMessage({id: 'admin.customization.enableCustomEmojiDesc', defaultMessage: 'Enable users to create custom emoji for use in messages. When enabled, custom emoji settings can be accessed in Channels through the emoji picker.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.EMOJI)), }, ], @@ -2815,21 +2312,17 @@ const AdminDefinition: AdminDefinitionType = { }, posts: { url: 'site_config/posts', - title: t('admin.sidebar.posts'), - title_default: 'Posts', + title: defineMessage({id: 'admin.sidebar.posts', defaultMessage: 'Posts'}), isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.SITE.POSTS)), schema: { id: 'PostSettings', - name: t('admin.site.posts'), - name_default: 'Posts', + name: defineMessage({id: 'admin.site.posts', defaultMessage: 'Posts'}), settings: [ { type: 'bool', key: 'ServiceSettings.ThreadAutoFollow', - label: t('admin.experimental.threadAutoFollow.title'), - label_default: 'Automatically Follow Threads', - help_text: t('admin.experimental.threadAutoFollow.desc'), - help_text_default: 'This setting must be enabled in order to enable Collapsed Reply Threads. When enabled, threads a user starts, participates in, or is mentioned in are automatically followed. A new `Threads` table is added in the database that tracks threads and thread participants, and a `ThreadMembership` table tracks followed threads for each user and the read or unread state of each followed thread. When false, all backend operations to support Collapsed Reply Threads are disabled.', + label: defineMessage({id: 'admin.experimental.threadAutoFollow.title', defaultMessage: 'Automatically Follow Threads'}), + help_text: defineMessage({id: 'admin.experimental.threadAutoFollow.desc', defaultMessage: 'This setting must be enabled in order to enable Collapsed Reply Threads. When enabled, threads a user starts, participates in, or is mentioned in are automatically followed. A new `Threads` table is added in the database that tracks threads and thread participants, and a `ThreadMembership` table tracks followed threads for each user and the read or unread state of each followed thread. When false, all backend operations to support Collapsed Reply Threads are disabled.'}), help_text_markdown: true, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), isHidden: it.licensedForFeature('Cloud'), @@ -2837,10 +2330,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'dropdown', key: 'ServiceSettings.CollapsedThreads', - label: t('admin.experimental.collapsedThreads.title'), - label_default: 'Collapsed Reply Threads', - help_text: t('admin.experimental.collapsedThreads.desc'), - help_text_default: 'When enabled (default off), users must enable collapsed reply threads in Settings. When disabled, users cannot access Collapsed Reply Threads. Please review our documentation for known issues and help provide feedback in our Community Channel.', + label: defineMessage({id: 'admin.experimental.collapsedThreads.title', defaultMessage: 'Collapsed Reply Threads'}), + help_text: defineMessage({id: 'admin.experimental.collapsedThreads.desc', defaultMessage: 'When enabled (default off), users must enable collapsed reply threads in Settings. When disabled, users cannot access Collapsed Reply Threads. Please review our documentation for known issues and help provide feedback in our Community Channel.'}), help_text_values: { linkKnownIssues: (msg: string) => ( documentation.', + label: defineMessage({id: 'admin.posts.postPriority.title', defaultMessage: 'Message Priority'}), + help_text: defineMessage({id: 'admin.posts.postPriority.desc', defaultMessage: 'When enabled, users can configure a visual indicator to communicate messages that are important or urgent. Learn more about message priority in our documentation.'}), help_text_values: { link: (msg: string) => ( documentation.', + label: defineMessage({id: 'admin.posts.persistentNotifications.title', defaultMessage: 'Persistent Notifications'}), + help_text: defineMessage({id: 'admin.posts.persistentNotifications.desc', defaultMessage: 'When enabled, users can trigger repeating notifications for the recipients of urgent messages. Learn more about message priority and persistent notifications in our documentation.'}), help_text_values: { link: (msg: string) => ( documentation.', + label: defineMessage({id: 'admin.posts.persistentNotificationsMaxRecipients.title', defaultMessage: 'Maximum number of recipients for persistent notifications'}), + help_text: defineMessage({id: 'admin.posts.persistentNotificationsMaxRecipients.desc', defaultMessage: 'Configure the maximum number of recipients to which users may send persistent notifications. Learn more about message priority and persistent notifications in our documentation.'}), help_text_values: { link: (msg: string) => ( documentation.', + label: defineMessage({id: 'admin.posts.persistentNotificationsInterval.title', defaultMessage: 'Frequency of persistent notifications'}), + help_text: defineMessage({id: 'admin.posts.persistentNotificationsInterval.desc', defaultMessage: 'Configure the number of minutes between repeated notifications for urgent messages send with persistent notifications. Learn more about message priority and persistent notifications in our documentation.'}), help_text_values: { link: (msg: string) => ( documentation.', + label: defineMessage({id: 'admin.posts.persistentNotificationsMaxCount.title', defaultMessage: 'Total number of persistent notification per post'}), + help_text: defineMessage({id: 'admin.posts.persistentNotificationsMaxCount.desc', defaultMessage: 'Configure the maximum number of times users may receive persistent notifications. Learn more about message priority and persistent notifications in our documentation.'}), help_text_values: { link: (msg: string) => ( documentation.', + label: defineMessage({id: 'admin.posts.persistentNotificationsGuests.title', defaultMessage: 'Allow guests to send persistent notifications'}), + help_text: defineMessage({id: 'admin.posts.persistentNotificationsGuests.desc', defaultMessage: 'Whether a guest is able to require persistent notifications. Learn more about message priority and persistent notifications in our documentation.'}), help_text_values: { link: (msg: string) => ( Display > Website Link Previews.', + label: defineMessage({id: 'admin.customization.enableLinkPreviewsTitle', defaultMessage: 'Enable website link previews:'}), + help_text: defineMessage({id: 'admin.customization.enableLinkPreviewsDesc', defaultMessage: 'Display a preview of website content, image links and YouTube links below the message when available. The server must be connected to the internet and have access through the firewall (if applicable) to the websites from which previews are expected. Users can disable these previews from Settings > Display > Website Link Previews.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.POSTS)), }, { type: 'text', key: 'ServiceSettings.RestrictLinkPreviews', - label: t('admin.customization.restrictLinkPreviewsTitle'), - label_default: 'Disable website link previews from these domains:', - help_text: t('admin.customization.restrictLinkPreviewsDesc'), - help_text_default: 'Link previews and image link previews will not be shown for the above list of comma-separated domains.', - placeholder: t('admin.customization.restrictLinkPreviewsExample'), - placeholder_default: 'E.g.: "internal.mycompany.com, images.example.com"', + label: defineMessage({id: 'admin.customization.restrictLinkPreviewsTitle', defaultMessage: 'Disable website link previews from these domains:'}), + help_text: defineMessage({id: 'admin.customization.restrictLinkPreviewsDesc', defaultMessage: 'Link previews and image link previews will not be shown for the above list of comma-separated domains.'}), + placeholder: defineMessage({id: 'admin.customization.restrictLinkPreviewsExample', defaultMessage: 'E.g.: "internal.mycompany.com, images.example.com"'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.POSTS)), it.configIsFalse('ServiceSettings', 'EnableLinkPreviews'), @@ -3057,10 +2527,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'ServiceSettings.EnablePermalinkPreviews', - label: t('admin.customization.enablePermalinkPreviewsTitle'), - label_default: 'Enable message link previews:', - help_text: t('admin.customization.enablePermalinkPreviewsDesc'), - help_text_default: 'When enabled, links to Mattermost messages will generate a preview for any users that have access to the original message. Please review our documentation for details.', + label: defineMessage({id: 'admin.customization.enablePermalinkPreviewsTitle', defaultMessage: 'Enable message link previews:'}), + help_text: defineMessage({id: 'admin.customization.enablePermalinkPreviewsDesc', defaultMessage: 'When enabled, links to Mattermost messages will generate a preview for any users that have access to the original message. Please review our documentation for details.'}), help_text_values: { link: (msg: string) => ( documentation for details about text formatting.', + label: defineMessage({id: 'admin.customization.enableInlineLatexTitle', defaultMessage: 'Enable Inline Latex Rendering:'}), + help_text: defineMessage({id: 'admin.customization.enableInlineLatexDesc', defaultMessage: 'Enable rendering of inline Latex code. If false, Latex can only be rendered in a code block using syntax highlighting. Please review our documentation for details about text formatting.'}), help_text_values: { link: (msg: string) => ( Google Developers Tutorial for instructions on how to obtain a key and add YouTube Data API v3 as a service to your key.', + label: defineMessage({id: 'admin.service.googleTitle', defaultMessage: 'Google API Key:'}), + placeholder: defineMessage({id: 'admin.service.googleExample', defaultMessage: 'E.g.: "7rAh6iwQCkV4cA1Gsg3fgGOXJAQ43QV"'}), + help_text: defineMessage({id: 'admin.service.googleDescription', defaultMessage: 'Set this key to enable the display of titles for embedded YouTube video previews. Without the key, YouTube previews will still be created based on hyperlinks appearing in messages or comments but they will not show the video title. View a Google Developers Tutorial for instructions on how to obtain a key and add YouTube Data API v3 as a service to your key.'}), help_text_values: { link: (msg: string) => ( { const maxResult = validators.maxValue( 500, - t('admin.customization.uniqueEmojiReactionLimitPerPost.maxValue'), - 'Cannot increase the limit to a value above 500.', + defineMessage({id: 'admin.customization.uniqueEmojiReactionLimitPerPost.maxValue', defaultMessage: 'Cannot increase the limit to a value above 500.'}), )(value); if (!maxResult.isValid()) { return maxResult; } - const minResult = validators.minValue(0, - t('admin.customization.uniqueEmojiReactionLimitPerPost.minValue'), - 'Cannot decrease the limit below 0.', + const minResult = validators.minValue( + 0, + defineMessage({id: 'admin.customization.uniqueEmojiReactionLimitPerPost.minValue', defaultMessage: 'Cannot decrease the limit below 0.'}), )(value); if (!minResult.isValid()) { return minResult; } - return new ValidationResult(true, '', ''); + return new ValidationResult(true, ''); }, }, ], @@ -3197,22 +2648,18 @@ const AdminDefinition: AdminDefinitionType = { }, wrangler: { url: 'site_config/wrangler', - title: t('admin.sidebar.move_thread'), - title_default: 'Move Thread (Beta)', + title: defineMessage({id: 'admin.sidebar.move_thread', defaultMessage: 'Move Thread (Beta)'}), isHidden: it.any(it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.SITE.POSTS)), it.configIsFalse('FeatureFlags', 'MoveThreadsEnabled'), it.not(it.licensed)), schema: { id: 'WranglerSettings', - name: t('admin.site.move_thread'), - name_default: 'Move Thread (Beta)', + name: defineMessage({id: 'admin.site.move_thread', defaultMessage: 'Move Thread (Beta)'}), settings: [ { type: 'roles', multiple: true, key: 'WranglerSettings.PermittedWranglerRoles', - label: t('admin.experimental.PermittedMoveThreadRoles.title'), - label_default: 'Permitted Roles', - help_text: t('admin.experimental.PermittedMoveThreadRoles.desc'), - help_text_default: 'Choose who is allowed to move threads to other channels based on roles. (Other permissions below still apply).', + label: defineMessage({id: 'admin.experimental.PermittedMoveThreadRoles.title', defaultMessage: 'Permitted Roles'}), + help_text: defineMessage({id: 'admin.experimental.PermittedMoveThreadRoles.desc', defaultMessage: 'Choose who is allowed to move threads to other channels based on roles. (Other permissions below still apply).'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, @@ -3220,60 +2667,48 @@ const AdminDefinition: AdminDefinitionType = { type: 'text', key: 'WranglerSettings.AllowedEmailDomain', multiple: true, - label: t('admin.experimental.allowedEmailDomain.title'), - label_default: 'Allowed Email Domain', - help_text: t('admin.experimental.allowedEmailDomain.desc'), - help_text_default: '(Optional) When set, users must have an email ending in this domain to move threads. Multiple domains can be specified by separating them with commas.', + label: defineMessage({id: 'admin.experimental.allowedEmailDomain.title', defaultMessage: 'Allowed Email Domain'}), + help_text: defineMessage({id: 'admin.experimental.allowedEmailDomain.desc', defaultMessage: '(Optional) When set, users must have an email ending in this domain to move threads. Multiple domains can be specified by separating them with commas.'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'number', key: 'WranglerSettings.MoveThreadMaxCount', - label: t('admin.experimental.moveThreadMaxCount.title'), - label_default: 'Max Thread Count Move Size', - help_text: t('admin.experimental.moveThreadMaxCount.desc'), - help_text_default: 'The maximum number of messages in a thread that the plugin is allowed to move. Leave empty for unlimited messages.', + label: defineMessage({id: 'admin.experimental.moveThreadMaxCount.title', defaultMessage: 'Max Thread Count Move Size'}), + help_text: defineMessage({id: 'admin.experimental.moveThreadMaxCount.desc', defaultMessage: 'The maximum number of messages in a thread that the plugin is allowed to move. Leave empty for unlimited messages.'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'bool', key: 'WranglerSettings.MoveThreadToAnotherTeamEnable', - label: t('admin.experimental.moveThreadToAnotherTeamEnable.title'), - label_default: 'Enable Moving Threads To Different Teams', - help_text: t('admin.experimental.moveThreadToAnotherTeamEnable.desc'), - help_text_default: 'Control whether Wrangler is permitted to move message threads from one team to another or not.', + label: defineMessage({id: 'admin.experimental.moveThreadToAnotherTeamEnable.title', defaultMessage: 'Enable Moving Threads To Different Teams'}), + help_text: defineMessage({id: 'admin.experimental.moveThreadToAnotherTeamEnable.desc', defaultMessage: 'Control whether Wrangler is permitted to move message threads from one team to another or not.'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'bool', key: 'WranglerSettings.MoveThreadFromPrivateChannelEnable', - label: t('admin.experimental.moveThreadFromPrivateChannelEnable.title'), - label_default: 'Enable Moving Threads From Private Channels', - help_text: t('admin.experimental.moveThreadFromPrivateChannelEnable.desc'), - help_text_default: 'Control whether Wrangler is permitted to move message threads from private channels or not.', + label: defineMessage({id: 'admin.experimental.moveThreadFromPrivateChannelEnable.title', defaultMessage: 'Enable Moving Threads From Private Channels'}), + help_text: defineMessage({id: 'admin.experimental.moveThreadFromPrivateChannelEnable.desc', defaultMessage: 'Control whether Wrangler is permitted to move message threads from private channels or not.'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'bool', key: 'WranglerSettings.MoveThreadFromDirectMessageChannelEnable', - label: t('admin.experimental.moveThreadFromDirectMessageChannelEnable.title'), - label_default: 'Enable Moving Threads From Direct Message Channels', - help_text: t('admin.experimental.moveThreadFromDirectMessageChannelEnable.desc'), - help_text_default: 'Control whether Wrangler is permitted to move message threads from direct message channels or not.', + label: defineMessage({id: 'admin.experimental.moveThreadFromDirectMessageChannelEnable.title', defaultMessage: 'Enable Moving Threads From Direct Message Channels'}), + help_text: defineMessage({id: 'admin.experimental.moveThreadFromDirectMessageChannelEnable.desc', defaultMessage: 'Control whether Wrangler is permitted to move message threads from direct message channels or not.'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'bool', key: 'WranglerSettings.MoveThreadFromGroupMessageChannelEnable', - label: t('admin.experimental.moveThreadFromGroupMessageChannelEnable.title'), - label_default: 'Enable Moving Threads From Group Message Channels', - help_text: t('admin.experimental.moveThreadFromGroupMessageChannelEnable.desc'), - help_text_default: 'Control whether Wrangler is permitted to move message threads from group message channels or not.', + label: defineMessage({id: 'admin.experimental.moveThreadFromGroupMessageChannelEnable.title', defaultMessage: 'Enable Moving Threads From Group Message Channels'}), + help_text: defineMessage({id: 'admin.experimental.moveThreadFromGroupMessageChannelEnable.desc', defaultMessage: 'Control whether Wrangler is permitted to move message threads from group message channels or not.'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, @@ -3282,42 +2717,34 @@ const AdminDefinition: AdminDefinitionType = { }, file_sharing_downloads: { url: 'site_config/file_sharing_downloads', - title: t('admin.sidebar.fileSharingDownloads'), - title_default: 'File Sharing and Downloads', + title: defineMessage({id: 'admin.sidebar.fileSharingDownloads', defaultMessage: 'File Sharing and Downloads'}), isHidden: it.any( it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.SITE.FILE_SHARING_AND_DOWNLOADS)), ), schema: { id: 'FileSharingDownloads', - name: t('admin.site.fileSharingDownloads'), - name_default: 'File Sharing and Downloads', + name: defineMessage({id: 'admin.site.fileSharingDownloads', defaultMessage: 'File Sharing and Downloads'}), settings: [ { type: 'bool', key: 'FileSettings.EnableFileAttachments', - label: t('admin.file.enableFileAttachments'), - label_default: 'Allow File Sharing:', - help_text: t('admin.file.enableFileAttachmentsDesc'), - help_text_default: 'When false, disables file sharing on the server. All file and image uploads on messages are forbidden across clients and devices, including mobile.', + label: defineMessage({id: 'admin.file.enableFileAttachments', defaultMessage: 'Allow File Sharing:'}), + help_text: defineMessage({id: 'admin.file.enableFileAttachmentsDesc', defaultMessage: 'When false, disables file sharing on the server. All file and image uploads on messages are forbidden across clients and devices, including mobile.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.FILE_SHARING_AND_DOWNLOADS)), }, { type: 'bool', key: 'FileSettings.EnableMobileUpload', - label: t('admin.file.enableMobileUploadTitle'), - label_default: 'Allow File Uploads on Mobile:', - help_text: t('admin.file.enableMobileUploadDesc'), - help_text_default: 'When false, disables file uploads on mobile apps. If Allow File Sharing is set to true, users can still upload files from a mobile web browser.', + label: defineMessage({id: 'admin.file.enableMobileUploadTitle', defaultMessage: 'Allow File Uploads on Mobile:'}), + help_text: defineMessage({id: 'admin.file.enableMobileUploadDesc', defaultMessage: 'When false, disables file uploads on mobile apps. If Allow File Sharing is set to true, users can still upload files from a mobile web browser.'}), isHidden: it.not(it.licensedForFeature('Compliance')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.FILE_SHARING_AND_DOWNLOADS)), }, { type: 'bool', key: 'FileSettings.EnableMobileDownload', - label: t('admin.file.enableMobileDownloadTitle'), - label_default: 'Allow File Downloads on Mobile:', - help_text: t('admin.file.enableMobileDownloadDesc'), - help_text_default: 'When false, disables file downloads on mobile apps. Users can still download files from a mobile web browser.', + label: defineMessage({id: 'admin.file.enableMobileDownloadTitle', defaultMessage: 'Allow File Downloads on Mobile:'}), + help_text: defineMessage({id: 'admin.file.enableMobileDownloadDesc', defaultMessage: 'When false, disables file downloads on mobile apps. Users can still download files from a mobile web browser.'}), isHidden: it.not(it.licensedForFeature('Compliance')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.FILE_SHARING_AND_DOWNLOADS)), }, @@ -3326,33 +2753,27 @@ const AdminDefinition: AdminDefinitionType = { }, public_links: { url: 'site_config/public_links', - title: t('admin.sidebar.publicLinks'), - title_default: 'Public Links', + title: defineMessage({id: 'admin.sidebar.publicLinks', defaultMessage: 'Public Links'}), isHidden: it.any( it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.SITE.PUBLIC_LINKS)), ), schema: { id: 'PublicLinkSettings', - name: t('admin.site.public_links'), - name_default: 'Public Links', + name: defineMessage({id: 'admin.site.public_links', defaultMessage: 'Public Links'}), settings: [ { type: 'bool', key: 'FileSettings.EnablePublicLink', - label: t('admin.image.shareTitle'), - label_default: 'Enable Public File Links: ', - help_text: t('admin.image.shareDescription'), - help_text_default: 'Allow users to share public links to files and images.', + label: defineMessage({id: 'admin.image.shareTitle', defaultMessage: 'Enable Public File Links: '}), + help_text: defineMessage({id: 'admin.image.shareDescription', defaultMessage: 'Allow users to share public links to files and images.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.PUBLIC_LINKS)), }, { type: 'generated', key: 'FileSettings.PublicLinkSalt', - label: t('admin.image.publicLinkTitle'), - label_default: 'Public Link Salt:', - help_text: t('admin.image.publicLinkDescription'), - help_text_default: '32-character salt added to signing of public links. Randomly generated on install. Select "Regenerate" to create new salt.', + label: defineMessage({id: 'admin.image.publicLinkTitle', defaultMessage: 'Public Link Salt:'}), + help_text: defineMessage({id: 'admin.image.publicLinkDescription', defaultMessage: '32-character salt added to signing of public links. Randomly generated on install. Select "Regenerate" to create new salt.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.PUBLIC_LINKS)), }, ], @@ -3360,21 +2781,17 @@ const AdminDefinition: AdminDefinitionType = { }, notices: { url: 'site_config/notices', - title: t('admin.sidebar.notices'), - title_default: 'Notices', + title: defineMessage({id: 'admin.sidebar.notices', defaultMessage: 'Notices'}), isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.SITE.NOTICES)), schema: { id: 'NoticesSettings', - name: t('admin.site.notices'), - name_default: 'Notices', + name: defineMessage({id: 'admin.site.notices', defaultMessage: 'Notices'}), settings: [ { type: 'bool', key: 'AnnouncementSettings.AdminNoticesEnabled', - label: t('admin.notices.enableAdminNoticesTitle'), - label_default: 'Enable Admin Notices: ', - help_text: t('admin.notices.enableAdminNoticesDescription'), - help_text_default: 'When enabled, System Admins will receive notices about available server upgrades and relevant system administration features. Learn more about notices in our documentation.', + label: defineMessage({id: 'admin.notices.enableAdminNoticesTitle', defaultMessage: 'Enable Admin Notices: '}), + help_text: defineMessage({id: 'admin.notices.enableAdminNoticesDescription', defaultMessage: 'When enabled, System Admins will receive notices about available server upgrades and relevant system administration features. Learn more about notices in our documentation.'}), help_text_values: { link: (msg: string) => ( Learn more about notices in our documentation.', + label: defineMessage({id: 'admin.notices.enableEndUserNoticesTitle', defaultMessage: 'Enable End User Notices: '}), + help_text: defineMessage({id: 'admin.notices.enableEndUserNoticesDescription', defaultMessage: 'When enabled, all users will receive notices about available client upgrades and relevant end user features to improve user experience. Learn more about notices in our documentation.'}), help_text_values: { link: (msg: string) => ( ), - sectionTitle: t('admin.sidebar.authentication'), - sectionTitleDefault: 'Authentication', + sectionTitle: defineMessage({id: 'admin.sidebar.authentication', defaultMessage: 'Authentication'}), isHidden: it.not(it.userHasReadPermissionOnSomeResources(RESOURCE_KEYS.AUTHENTICATION)), subsections: { signup: { url: 'authentication/signup', - title: t('admin.sidebar.signup'), - title_default: 'Signup', + title: defineMessage({id: 'admin.sidebar.signup', defaultMessage: 'Signup'}), isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SIGNUP)), schema: { id: 'SignupSettings', - name: t('admin.authentication.signup'), - name_default: 'Signup', + name: defineMessage({id: 'admin.authentication.signup', defaultMessage: 'Signup'}), settings: [ { type: 'bool', key: 'TeamSettings.EnableUserCreation', - label: t('admin.team.userCreationTitle'), - label_default: 'Enable Account Creation: ', - help_text: t('admin.team.userCreationDescription'), - help_text_default: 'When false, the ability to create accounts is disabled. The create account button displays error when pressed.', + label: defineMessage({id: 'admin.team.userCreationTitle', defaultMessage: 'Enable Account Creation: '}), + help_text: defineMessage({id: 'admin.team.userCreationDescription', defaultMessage: 'When false, the ability to create accounts is disabled. The create account button displays error when pressed.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SIGNUP)), }, { type: 'text', key: 'TeamSettings.RestrictCreationToDomains', - label: t('admin.team.restrictTitle'), - label_default: 'Restrict new system and team members to specified email domains:', - help_text: t('admin.team.restrictDescription'), - help_text_default: 'New user accounts are restricted to the above specified email domain (e.g. "mattermost.com") or list of comma-separated domains (e.g. "corp.mattermost.com, mattermost.com"). New teams can only be created by users from the above domain(s). This setting only affects email login for users.', - placeholder: t('admin.team.restrictExample'), - placeholder_default: 'E.g.: "corp.mattermost.com, mattermost.com"', + label: defineMessage({id: 'admin.team.restrictTitle', defaultMessage: 'Restrict new system and team members to specified email domains:'}), + help_text: defineMessage({id: 'admin.team.restrictDescription', defaultMessage: 'New user accounts are restricted to the above specified email domain (e.g. "mattermost.com") or list of comma-separated domains (e.g. "corp.mattermost.com, mattermost.com"). New teams can only be created by users from the above domain(s). This setting only affects email login for users.'}), + placeholder: defineMessage({id: 'admin.team.restrictExample', defaultMessage: 'E.g.: "corp.mattermost.com, mattermost.com"'}), isHidden: it.all( it.licensed, it.not(it.licensedForSku('starter')), @@ -3475,12 +2879,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'TeamSettings.RestrictCreationToDomains', - label: t('admin.team.restrictTitle'), - label_default: 'Restrict new system and team members to specified email domains:', - help_text: t('admin.team.restrictGuestDescription'), - help_text_default: 'New user accounts are restricted to the above specified email domain (e.g. "mattermost.com") or list of comma-separated domains (e.g. "corp.mattermost.com, mattermost.com"). New teams can only be created by users from the above domain(s). This setting affects email login for users. For Guest users, please add domains under Signup > Guest Access.', - placeholder: t('admin.team.restrictExample'), - placeholder_default: 'E.g.: "corp.mattermost.com, mattermost.com"', + label: defineMessage({id: 'admin.team.restrictTitle', defaultMessage: 'Restrict new system and team members to specified email domains:'}), + help_text: defineMessage({id: 'admin.team.restrictGuestDescription', defaultMessage: 'New user accounts are restricted to the above specified email domain (e.g. "mattermost.com") or list of comma-separated domains (e.g. "corp.mattermost.com, mattermost.com"). New teams can only be created by users from the above domain(s). This setting affects email login for users. For Guest users, please add domains under Signup > Guest Access.'}), + placeholder: defineMessage({id: 'admin.team.restrictExample', defaultMessage: 'E.g.: "corp.mattermost.com, mattermost.com"'}), isHidden: it.any( it.not(it.licensed), it.licensedForSku('starter'), @@ -3490,19 +2891,15 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'TeamSettings.EnableOpenServer', - label: t('admin.team.openServerTitle'), - label_default: 'Enable Open Server: ', - help_text: t('admin.team.openServerDescription'), - help_text_default: 'When true, anyone can signup for a user account on this server without the need to be invited.', + label: defineMessage({id: 'admin.team.openServerTitle', defaultMessage: 'Enable Open Server: '}), + help_text: defineMessage({id: 'admin.team.openServerDescription', defaultMessage: 'When true, anyone can signup for a user account on this server without the need to be invited.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SIGNUP)), }, { type: 'bool', key: 'ServiceSettings.EnableEmailInvitations', - label: t('admin.team.emailInvitationsTitle'), - label_default: 'Enable Email Invitations: ', - help_text: t('admin.team.emailInvitationsDescription'), - help_text_default: 'When true users can invite others to the system using email.', + label: defineMessage({id: 'admin.team.emailInvitationsTitle', defaultMessage: 'Enable Email Invitations: '}), + help_text: defineMessage({id: 'admin.team.emailInvitationsDescription', defaultMessage: 'When true users can invite others to the system using email.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SIGNUP)), isHidden: it.licensedForFeature('Cloud'), }, @@ -3510,14 +2907,10 @@ const AdminDefinition: AdminDefinitionType = { type: 'button', key: 'InvalidateEmailInvitesButton', action: invalidateAllEmailInvites, - label: t('admin.team.invalidateEmailInvitesTitle'), - label_default: 'Invalidate pending email invites', - help_text: t('admin.team.invalidateEmailInvitesDescription'), - help_text_default: 'This will invalidate active email invitations that have not been accepted by the user. By default email invitations expire after 48 hours.', - error_message: t('admin.team.invalidateEmailInvitesFail'), - error_message_default: 'Unable to invalidate pending email invites: {error}', - success_message: t('admin.team.invalidateEmailInvitesSuccess'), - success_message_default: 'Pending email invitations invalidated successfully', + label: defineMessage({id: 'admin.team.invalidateEmailInvitesTitle', defaultMessage: 'Invalidate pending email invites'}), + help_text: defineMessage({id: 'admin.team.invalidateEmailInvitesDescription', defaultMessage: 'This will invalidate active email invitations that have not been accepted by the user. By default email invitations expire after 48 hours.'}), + error_message: defineMessage({id: 'admin.team.invalidateEmailInvitesFail', defaultMessage: 'Unable to invalidate pending email invites: {error}'}), + success_message: defineMessage({id: 'admin.team.invalidateEmailInvitesSuccess', defaultMessage: 'Pending email invitations invalidated successfully'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SIGNUP)), }, ], @@ -3525,49 +2918,39 @@ const AdminDefinition: AdminDefinitionType = { }, email: { url: 'authentication/email', - title: t('admin.sidebar.email'), - title_default: 'Email', + title: defineMessage({id: 'admin.sidebar.email', defaultMessage: 'Email'}), isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.EMAIL)), schema: { id: 'EmailSettings', - name: t('admin.authentication.email'), - name_default: 'Email', + name: defineMessage({id: 'admin.authentication.email', defaultMessage: 'Email'}), settings: [ { type: 'bool', key: 'EmailSettings.EnableSignUpWithEmail', - label: t('admin.email.allowSignupTitle'), - label_default: 'Enable account creation with email:', - help_text: t('admin.email.allowSignupDescription'), - help_text_default: 'When true, Mattermost allows account creation using email and password. This value should be false only when you want to limit sign up to a single sign-on service like AD/LDAP, SAML or GitLab.', + label: defineMessage({id: 'admin.email.allowSignupTitle', defaultMessage: 'Enable account creation with email:'}), + help_text: defineMessage({id: 'admin.email.allowSignupDescription', defaultMessage: 'When true, Mattermost allows account creation using email and password. This value should be false only when you want to limit sign up to a single sign-on service like AD/LDAP, SAML or GitLab.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.EMAIL)), }, { type: 'bool', key: 'EmailSettings.RequireEmailVerification', - label: t('admin.email.requireVerificationTitle'), - label_default: 'Require Email Verification: ', - help_text: t('admin.email.requireVerificationDescription'), - help_text_default: 'Typically set to true in production. When true, Mattermost requires email verification after account creation prior to allowing login. Developers may set this field to false to skip sending verification emails for faster development.', + label: defineMessage({id: 'admin.email.requireVerificationTitle', defaultMessage: 'Require Email Verification: '}), + help_text: defineMessage({id: 'admin.email.requireVerificationDescription', defaultMessage: 'Typically set to true in production. When true, Mattermost requires email verification after account creation prior to allowing login. Developers may set this field to false to skip sending verification emails for faster development.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.EMAIL)), isHidden: it.licensedForFeature('Cloud'), }, { type: 'bool', key: 'EmailSettings.EnableSignInWithEmail', - label: t('admin.email.allowEmailSignInTitle'), - label_default: 'Enable sign-in with email:', - help_text: t('admin.email.allowEmailSignInDescription'), - help_text_default: 'When true, Mattermost allows users to sign in using their email and password.', + label: defineMessage({id: 'admin.email.allowEmailSignInTitle', defaultMessage: 'Enable sign-in with email:'}), + help_text: defineMessage({id: 'admin.email.allowEmailSignInDescription', defaultMessage: 'When true, Mattermost allows users to sign in using their email and password.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.EMAIL)), }, { type: 'bool', key: 'EmailSettings.EnableSignInWithUsername', - label: t('admin.email.allowUsernameSignInTitle'), - label_default: 'Enable sign-in with username:', - help_text: t('admin.email.allowUsernameSignInDescription'), - help_text_default: 'When true, users with email login can sign in using their username and password. This setting does not affect AD/LDAP login.', + label: defineMessage({id: 'admin.email.allowUsernameSignInTitle', defaultMessage: 'Enable sign-in with username:'}), + help_text: defineMessage({id: 'admin.email.allowUsernameSignInDescription', defaultMessage: 'When true, users with email login can sign in using their username and password. This setting does not affect AD/LDAP login.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.EMAIL)), }, ], @@ -3575,22 +2958,8 @@ const AdminDefinition: AdminDefinitionType = { }, password: { url: 'authentication/password', - title: t('admin.sidebar.password'), - title_default: 'Password', - searchableStrings: [ - 'user.settings.security.passwordMinLength', - 'admin.security.password', - ['admin.password.minimumLength', {max: '', min: ''}], - ['admin.password.minimumLengthDescription', {max: '', min: ''}], - 'passwordRequirements', - 'admin.password.lowercase', - 'admin.password.uppercase', - 'admin.password.number', - 'admin.password.symbol', - 'admin.password.preview', - 'admin.service.attemptTitle', - 'admin.service.attemptDescription', - ], + title: defineMessage({id: 'admin.sidebar.password', defaultMessage: 'Password'}), + searchableStrings: passwordSearchableStrings, isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.PASSWORD)), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.PASSWORD)), schema: { @@ -3600,18 +2969,15 @@ const AdminDefinition: AdminDefinitionType = { }, mfa: { url: 'authentication/mfa', - title: t('admin.sidebar.mfa'), - title_default: 'MFA', + title: defineMessage({id: 'admin.sidebar.mfa', defaultMessage: 'MFA'}), isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.MFA)), schema: { id: 'ServiceSettings', - name: t('admin.authentication.mfa'), - name_default: 'Multi-factor Authentication', + name: defineMessage({id: 'admin.authentication.mfa', defaultMessage: 'Multi-factor Authentication'}), settings: [ { type: 'banner', - label: t('admin.mfa.bannerDesc'), - label_default: 'Multi-factor authentication is available for accounts with AD/LDAP or email login. If other login methods are used, MFA should be configured with the authentication provider.', + label: defineMessage({id: 'admin.mfa.bannerDesc', defaultMessage: 'Multi-factor authentication is available for accounts with AD/LDAP or email login. If other login methods are used, MFA should be configured with the authentication provider.'}), label_markdown: false, label_values: { link: (msg: string) => ( @@ -3628,20 +2994,16 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'ServiceSettings.EnableMultifactorAuthentication', - label: t('admin.service.mfaTitle'), - label_default: 'Enable Multi-factor Authentication:', - help_text: t('admin.service.mfaDesc'), - help_text_default: 'When true, users with AD/LDAP or email login can add multi-factor authentication to their account using Google Authenticator.', + label: defineMessage({id: 'admin.service.mfaTitle', defaultMessage: 'Enable Multi-factor Authentication:'}), + help_text: defineMessage({id: 'admin.service.mfaDesc', defaultMessage: 'When true, users with AD/LDAP or email login can add multi-factor authentication to their account using Google Authenticator.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.MFA)), }, { type: 'bool', key: 'ServiceSettings.EnforceMultifactorAuthentication', - label: t('admin.service.enforceMfaTitle'), - label_default: 'Enforce Multi-factor Authentication:', - help_text: t('admin.service.enforceMfaDesc'), + label: defineMessage({id: 'admin.service.enforceMfaTitle', defaultMessage: 'Enforce Multi-factor Authentication:'}), + help_text: defineMessage({id: 'admin.service.enforceMfaDesc', defaultMessage: 'When true, multi-factor authentication is required for login. New users will be required to configure MFA on signup. Logged in users without MFA configured are redirected to the MFA setup page until configuration is complete.\n \nIf your system has users with login methods other than AD/LDAP and email, MFA must be enforced with the authentication provider outside of Mattermost.'}), help_text_markdown: false, - help_text_default: 'When true, multi-factor authentication is required for login. New users will be required to configure MFA on signup. Logged in users without MFA configured are redirected to the MFA setup page until configuration is complete.\n \nIf your system has users with login methods other than AD/LDAP and email, MFA must be enforced with the authentication provider outside of Mattermost.', help_text_values: { link: (msg: string) => ( Groups]({siteURL}/admin_console/user_management/groups), select which AD/LDAP groups should be linked and configured.'}), help_text_markdown: true, - help_text_default: '(Optional) Enter an AD/LDAP filter to use when searching for group objects. Only the groups selected by the query will be available to Mattermost. From [User Management > Groups]({siteURL}/admin_console/user_management/groups), select which AD/LDAP groups should be linked and configured.', help_text_values: {siteURL: getSiteURL()}, - placeholder: t('admin.ldap.groupFilterEx'), - placeholder_default: 'E.g.: "(objectClass=group)"', + placeholder: defineMessage({id: 'admin.ldap.groupFilterEx', defaultMessage: 'E.g.: "(objectClass=group)"'}), isHidden: it.not(it.licensedForFeature('LDAPGroups')), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.LDAP)), @@ -3931,8 +3247,7 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'LdapSettings.EnableAdminFilter', - label: t('admin.ldap.enableAdminFilterTitle'), - label_default: 'Enable Admin Filter:', + label: defineMessage({id: 'admin.ldap.enableAdminFilterTitle', defaultMessage: 'Enable Admin Filter:'}), isDisabled: it.any( it.not(it.isSystemAdmin), it.all( @@ -3944,13 +3259,10 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'LdapSettings.AdminFilter', - label: t('admin.ldap.adminFilterTitle'), - label_default: 'Admin Filter:', - help_text: t('admin.ldap.adminFilterFilterDesc'), - help_text_default: '(Optional) Enter an AD/LDAP filter to use for designating System Admins. The users selected by the query will have access to your Mattermost server as System Admins. By default, System Admins have complete access to the Mattermost System Console.\n \nExisting members that are identified by this attribute will be promoted from member to System Admin upon next login. The next login is based upon Session lengths set in **System Console > Session Lengths**. It is highly recommend to manually demote users to members in **System Console > User Management** to ensure access is restricted immediately.\n \nNote: If this filter is removed/changed, System Admins that were promoted via this filter will be demoted to members and will not retain access to the System Console. When this filter is not in use, System Admins can be manually promoted/demoted in **System Console > User Management**.', + label: defineMessage({id: 'admin.ldap.adminFilterTitle', defaultMessage: 'Admin Filter:'}), + help_text: defineMessage({id: 'admin.ldap.adminFilterFilterDesc', defaultMessage: '(Optional) Enter an AD/LDAP filter to use for designating System Admins. The users selected by the query will have access to your Mattermost server as System Admins. By default, System Admins have complete access to the Mattermost System Console. Existing members that are identified by this attribute will be promoted from member to System Admin upon next login. The next login is based upon Session lengths set in **System Console > Session Lengths**. It is highly recommend to manually demote users to members in **System Console > User Management** to ensure access is restricted immediately. Note: If this filter is removed/changed, System Admins that were promoted via this filter will be demoted to members and will not retain access to the System Console. When this filter is not in use, System Admins can be manually promoted/demoted in **System Console > User Management**.'}), help_text_markdown: true, - placeholder: t('admin.ldap.adminFilterEx'), - placeholder_default: 'E.g.: "(objectClass=user)"', + placeholder: defineMessage({id: 'admin.ldap.adminFilterEx', defaultMessage: 'E.g.: "(objectClass=user)"'}), isDisabled: it.any( it.not(it.isSystemAdmin), it.stateIsFalse('LdapSettings.EnableAdminFilter'), @@ -3963,13 +3275,10 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'LdapSettings.GuestFilter', - label: t('admin.ldap.guestFilterTitle'), - label_default: 'Guest Filter:', - help_text: t('admin.ldap.guestFilterFilterDesc'), - help_text_default: '(Optional) Requires Guest Access to be enabled before being applied. Enter an AD/LDAP filter to use when searching for guest objects. Only the users selected by the query will be able to access Mattermost as Guests. Guests are prevented from accessing teams or channels upon logging in until they are assigned a team and at least one channel.\n \nNote: If this filter is removed/changed, active guests will not be promoted to a member and will retain their Guest role. Guests can be promoted in **System Console > User Management**.\n \n \nExisting members that are identified by this attribute as a guest will be demoted from a member to a guest when they are asked to login next. The next login is based upon Session lengths set in **System Console > Session Lengths**. It is highly recommend to manually demote users to guests in **System Console > User Management ** to ensure access is restricted immediately.', + label: defineMessage({id: 'admin.ldap.guestFilterTitle', defaultMessage: 'Guest Filter:'}), + help_text: defineMessage({id: 'admin.ldap.guestFilterFilterDesc', defaultMessage: '(Optional) Requires Guest Access to be enabled before being applied. Enter an AD/LDAP filter to use when searching for guest objects. Only the users selected by the query will be able to access Mattermost as Guests. Guests are prevented from accessing teams or channels upon logging in until they are assigned a team and at least one channel. Note: If this filter is removed/changed, active guests will not be promoted to a member and will retain their Guest role. Guests can be promoted in **System Console > User Management**. Existing members that are identified by this attribute as a guest will be demoted from a member to a guest when they are asked to login next. The next login is based upon Session lengths set in **System Console > Session Lengths**. It is highly recommend to manually demote users to guests in **System Console > User Management ** to ensure access is restricted immediately.'}), help_text_markdown: true, - placeholder: t('admin.ldap.guestFilterEx'), - placeholder_default: 'E.g.: "(objectClass=user)"', + placeholder: defineMessage({id: 'admin.ldap.guestFilterEx', defaultMessage: 'E.g.: "(objectClass=user)"'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.LDAP)), it.configIsFalse('GuestAccountsSettings', 'Enable'), @@ -3987,13 +3296,10 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'LdapSettings.IdAttribute', - label: t('admin.ldap.idAttrTitle'), - label_default: 'ID Attribute: ', - placeholder: t('admin.ldap.idAttrEx'), - placeholder_default: 'E.g.: "objectGUID" or "uid"', - help_text: t('admin.ldap.idAttrDesc'), + label: defineMessage({id: 'admin.ldap.idAttrTitle', defaultMessage: 'ID Attribute: '}), + placeholder: defineMessage({id: 'admin.ldap.idAttrEx', defaultMessage: 'E.g.: "objectGUID" or "uid"'}), + help_text: defineMessage({id: 'admin.ldap.idAttrDesc', defaultMessage: "The attribute in the AD/LDAP server used as a unique identifier in Mattermost. It should be an AD/LDAP attribute with a value that does not change such as `uid` for LDAP or `objectGUID` for Active Directory. If a user's ID Attribute changes, it will create a new Mattermost account unassociated with their old one. If you need to change this field after users have already logged in, use the mattermost ldap idmigrate CLI tool."}), help_text_markdown: false, - help_text_default: 'The attribute in the AD/LDAP server used as a unique identifier in Mattermost. It should be an AD/LDAP attribute with a value that does not change such as `uid` for LDAP or `objectGUID` for Active Directory. If a user\'s ID Attribute changes, it will create a new Mattermost account unassociated with their old one.\n \nIf you need to change this field after users have already logged in, use the mattermost ldap idmigrate CLI tool.', help_text_values: { link: (msg: string) => ( Account Menu > Account Settings > Profile.', + label: defineMessage({id: 'admin.ldap.firstnameAttrTitle', defaultMessage: 'First Name Attribute:'}), + placeholder: defineMessage({id: 'admin.ldap.firstnameAttrEx', defaultMessage: 'E.g.: "givenName"'}), + help_text: defineMessage({id: 'admin.ldap.firstnameAttrDesc', defaultMessage: '(Optional) The attribute in the AD/LDAP server used to populate the first name of users in Mattermost. When set, users cannot edit their first name, since it is synchronized with the LDAP server. When left blank, users can set their first name in Account Menu > Account Settings > Profile.'}), help_text_values: { strong: (msg: string) => {msg}, }, @@ -4087,12 +3381,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'LdapSettings.LastNameAttribute', - label: t('admin.ldap.lastnameAttrTitle'), - label_default: 'Last Name Attribute:', - placeholder: t('admin.ldap.lastnameAttrEx'), - placeholder_default: 'E.g.: "sn"', - help_text: t('admin.ldap.lastnameAttrDesc'), - help_text_default: '(Optional) The attribute in the AD/LDAP server used to populate the last name of users in Mattermost. When set, users cannot edit their last name, since it is synchronized with the LDAP server. When left blank, users can set their last name in Account Menu > Account Settings > Profile.', + label: defineMessage({id: 'admin.ldap.lastnameAttrTitle', defaultMessage: 'Last Name Attribute:'}), + placeholder: defineMessage({id: 'admin.ldap.lastnameAttrEx', defaultMessage: 'E.g.: "sn"'}), + help_text: defineMessage({id: 'admin.ldap.lastnameAttrDesc', defaultMessage: '(Optional) The attribute in the AD/LDAP server used to populate the last name of users in Mattermost. When set, users cannot edit their last name, since it is synchronized with the LDAP server. When left blank, users can set their last name in Account Menu > Account Settings > Profile.'}), help_text_values: { strong: (msg: string) => {msg}, }, @@ -4107,12 +3398,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'LdapSettings.NicknameAttribute', - label: t('admin.ldap.nicknameAttrTitle'), - label_default: 'Nickname Attribute:', - placeholder: t('admin.ldap.nicknameAttrEx'), - placeholder_default: 'E.g.: "nickname"', - help_text: t('admin.ldap.nicknameAttrDesc'), - help_text_default: '(Optional) The attribute in the AD/LDAP server used to populate the nickname of users in Mattermost. When set, users cannot edit their nickname, since it is synchronized with the LDAP server. When left blank, users can set their nickname in Account Menu > Account Settings > Profile.', + label: defineMessage({id: 'admin.ldap.nicknameAttrTitle', defaultMessage: 'Nickname Attribute:'}), + placeholder: defineMessage({id: 'admin.ldap.nicknameAttrEx', defaultMessage: 'E.g.: "nickname"'}), + help_text: defineMessage({id: 'admin.ldap.nicknameAttrDesc', defaultMessage: '(Optional) The attribute in the AD/LDAP server used to populate the nickname of users in Mattermost. When set, users cannot edit their nickname, since it is synchronized with the LDAP server. When left blank, users can set their nickname in Account Menu > Account Settings > Profile.'}), help_text_values: { strong: (msg: string) => {msg}, }, @@ -4127,12 +3415,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'LdapSettings.PositionAttribute', - label: t('admin.ldap.positionAttrTitle'), - label_default: 'Position Attribute:', - placeholder: t('admin.ldap.positionAttrEx'), - placeholder_default: 'E.g.: "title"', - help_text: t('admin.ldap.positionAttrDesc'), - help_text_default: '(Optional) The attribute in the AD/LDAP server used to populate the position field in Mattermost. When set, users cannot edit their position, since it is synchronized with the LDAP server. When left blank, users can set their position in Account Menu > Account Settings > Profile.', + label: defineMessage({id: 'admin.ldap.positionAttrTitle', defaultMessage: 'Position Attribute:'}), + placeholder: defineMessage({id: 'admin.ldap.positionAttrEx', defaultMessage: 'E.g.: "title"'}), + help_text: defineMessage({id: 'admin.ldap.positionAttrDesc', defaultMessage: '(Optional) The attribute in the AD/LDAP server used to populate the position field in Mattermost. When set, users cannot edit their position, since it is synchronized with the LDAP server. When left blank, users can set their position in Account Menu > Account Settings > Profile.'}), help_text_values: { strong: (msg: string) => {msg}, }, @@ -4147,12 +3432,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'LdapSettings.PictureAttribute', - label: t('admin.ldap.pictureAttrTitle'), - label_default: 'Profile Picture Attribute:', - placeholder: t('admin.ldap.pictureAttrEx'), - placeholder_default: 'E.g.: "thumbnailPhoto" or "jpegPhoto"', - help_text: t('admin.ldap.pictureAttrDesc'), - help_text_default: 'The attribute in the AD/LDAP server used to populate the profile picture in Mattermost.', + label: defineMessage({id: 'admin.ldap.pictureAttrTitle', defaultMessage: 'Profile Picture Attribute:'}), + placeholder: defineMessage({id: 'admin.ldap.pictureAttrEx', defaultMessage: 'E.g.: "thumbnailPhoto" or "jpegPhoto"'}), + help_text: defineMessage({id: 'admin.ldap.pictureAttrDesc', defaultMessage: 'The attribute in the AD/LDAP server used to populate the profile picture in Mattermost.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.LDAP)), it.all( @@ -4169,12 +3451,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'LdapSettings.GroupDisplayNameAttribute', - label: t('admin.ldap.groupDisplayNameAttributeTitle'), - label_default: 'Group Display Name Attribute:', - help_text: t('admin.ldap.groupDisplayNameAttributeDesc'), - help_text_default: 'The attribute in the AD/LDAP server used to populate the group display names.', - placeholder: t('admin.ldap.groupDisplayNameAttributeEx'), - placeholder_default: 'E.g.: "cn"', + label: defineMessage({id: 'admin.ldap.groupDisplayNameAttributeTitle', defaultMessage: 'Group Display Name Attribute:'}), + help_text: defineMessage({id: 'admin.ldap.groupDisplayNameAttributeDesc', defaultMessage: 'The attribute in the AD/LDAP server used to populate the group display names.'}), + placeholder: defineMessage({id: 'admin.ldap.groupDisplayNameAttributeEx', defaultMessage: 'E.g.: "cn"'}), isHidden: it.not(it.licensedForFeature('LDAPGroups')), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.LDAP)), @@ -4184,13 +3463,10 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'LdapSettings.GroupIdAttribute', - label: t('admin.ldap.groupIdAttributeTitle'), - label_default: 'Group ID Attribute:', - help_text: t('admin.ldap.groupIdAttributeDesc'), - help_text_default: 'The attribute in the AD/LDAP server used as a unique identifier for Groups. This should be a AD/LDAP attribute with a value that does not change such as `entryUUID` for LDAP or `objectGUID` for Active Directory.', + label: defineMessage({id: 'admin.ldap.groupIdAttributeTitle', defaultMessage: 'Group ID Attribute:'}), + help_text: defineMessage({id: 'admin.ldap.groupIdAttributeDesc', defaultMessage: 'The attribute in the AD/LDAP server used as a unique identifier for Groups. This should be a AD/LDAP attribute with a value that does not change such as `entryUUID` for LDAP or `objectGUID` for Active Directory.'}), help_text_markdown: true, - placeholder: t('admin.ldap.groupIdAttributeEx'), - placeholder_default: 'E.g.: "objectGUID" or "entryUUID"', + placeholder: defineMessage({id: 'admin.ldap.groupIdAttributeEx', defaultMessage: 'E.g.: "objectGUID" or "entryUUID"'}), isHidden: it.not(it.licensedForFeature('LDAPGroups')), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.LDAP)), @@ -4205,10 +3481,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'number', key: 'LdapSettings.SyncIntervalMinutes', - label: t('admin.ldap.syncIntervalTitle'), - label_default: 'Synchronization Interval (minutes):', - help_text: t('admin.ldap.syncIntervalHelpText'), - help_text_default: 'AD/LDAP Synchronization updates Mattermost user information to reflect updates on the AD/LDAP server. For example, when a user\'s name changes on the AD/LDAP server, the change updates in Mattermost when synchronization is performed. Accounts removed from or disabled in the AD/LDAP server have their Mattermost accounts set to "Inactive" and have their account sessions revoked. Mattermost performs synchronization on the interval entered. For example, if 60 is entered, Mattermost synchronizes every 60 minutes.', + label: defineMessage({id: 'admin.ldap.syncIntervalTitle', defaultMessage: 'Synchronization Interval (minutes):'}), + help_text: defineMessage({id: 'admin.ldap.syncIntervalHelpText', defaultMessage: 'AD/LDAP Synchronization updates Mattermost user information to reflect updates on the AD/LDAP server. For example, when a user\'s name changes on the AD/LDAP server, the change updates in Mattermost when synchronization is performed. Accounts removed from or disabled in the AD/LDAP server have their Mattermost accounts set to "Inactive" and have their account sessions revoked. Mattermost performs synchronization on the interval entered. For example, if 60 is entered, Mattermost synchronizes every 60 minutes.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.LDAP)), it.all( @@ -4220,12 +3494,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'number', key: 'LdapSettings.MaxPageSize', - label: t('admin.ldap.maxPageSizeTitle'), - label_default: 'Maximum Page Size:', - placeholder: t('admin.ldap.maxPageSizeEx'), - placeholder_default: 'E.g.: "2000"', - help_text: t('admin.ldap.maxPageSizeHelpText'), - help_text_default: 'The maximum number of users the Mattermost server will request from the AD/LDAP server at one time. 0 is unlimited.', + label: defineMessage({id: 'admin.ldap.maxPageSizeTitle', defaultMessage: 'Maximum Page Size:'}), + placeholder: defineMessage({id: 'admin.ldap.maxPageSizeEx', defaultMessage: 'E.g.: "2000"'}), + help_text: defineMessage({id: 'admin.ldap.maxPageSizeHelpText', defaultMessage: 'The maximum number of users the Mattermost server will request from the AD/LDAP server at one time. 0 is unlimited.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.LDAP)), it.all( @@ -4237,12 +3508,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'number', key: 'LdapSettings.QueryTimeout', - label: t('admin.ldap.queryTitle'), - label_default: 'Query Timeout (seconds):', - placeholder: t('admin.ldap.queryEx'), - placeholder_default: 'E.g.: "60"', - help_text: t('admin.ldap.queryDesc'), - help_text_default: 'The timeout value for queries to the AD/LDAP server. Increase if you are getting timeout errors caused by a slow AD/LDAP server.', + label: defineMessage({id: 'admin.ldap.queryTitle', defaultMessage: 'Query Timeout (seconds):'}), + placeholder: defineMessage({id: 'admin.ldap.queryEx', defaultMessage: 'E.g.: "60"'}), + help_text: defineMessage({id: 'admin.ldap.queryDesc', defaultMessage: 'The timeout value for queries to the AD/LDAP server. Increase if you are getting timeout errors caused by a slow AD/LDAP server.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.LDAP)), it.all( @@ -4255,10 +3523,8 @@ const AdminDefinition: AdminDefinitionType = { type: 'button', action: ldapTest, key: 'LdapSettings.LdapTest', - label: t('admin.ldap.ldap_test_button'), - label_default: 'AD/LDAP Test', - help_text: t('admin.ldap.testHelpText'), - help_text_default: 'Tests if the Mattermost server can connect to the AD/LDAP server specified. Please review "System Console > Logs" and documentation to troubleshoot errors.', + label: defineMessage({id: 'admin.ldap.ldap_test_button', defaultMessage: 'AD/LDAP Test'}), + help_text: defineMessage({id: 'admin.ldap.testHelpText', defaultMessage: 'Tests if the Mattermost server can connect to the AD/LDAP server specified. Please review "System Console > Logs" and documentation to troubleshoot errors.'}), help_text_values: { link: (msg: string) => ( Logs" and documentation to troubleshoot errors.'}), help_text_markdown: false, - help_text_default: 'Initiates an AD/LDAP synchronization immediately. See the table below for status of each synchronization. Please review "System Console > Logs" and documentation to troubleshoot errors.', help_text_values: { link: (msg: string) => ( documentation to learn more about configuring SAML for Mattermost.', + label: defineMessage({id: 'admin.saml.enableTitle', defaultMessage: 'Enable Login With SAML 2.0:'}), + help_text: defineMessage({id: 'admin.saml.enableDescription', defaultMessage: 'When true, Mattermost allows login using SAML 2.0. Please see documentation to learn more about configuring SAML for Mattermost.'}), help_text_markdown: false, help_text_values: { link: (msg: string) => ( @@ -4494,10 +3750,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'SamlSettings.EnableSyncWithLdap', - label: t('admin.saml.enableSyncWithLdapTitle'), - label_default: 'Enable Synchronizing SAML Accounts With AD/LDAP:', - help_text: t('admin.saml.enableSyncWithLdapDescription'), - help_text_default: 'When true, Mattermost periodically synchronizes SAML user attributes, including user deactivation and removal, from AD/LDAP. Enable and configure synchronization settings at Authentication > AD/LDAP. When false, user attributes are updated from SAML during user login. See documentation to learn more.', + label: defineMessage({id: 'admin.saml.enableSyncWithLdapTitle', defaultMessage: 'Enable Synchronizing SAML Accounts With AD/LDAP:'}), + help_text: defineMessage({id: 'admin.saml.enableSyncWithLdapDescription', defaultMessage: 'When true, Mattermost periodically synchronizes SAML user attributes, including user deactivation and removal, from AD/LDAP. Enable and configure synchronization settings at Authentication > AD/LDAP. When false, user attributes are updated from SAML during user login. See documentation to learn more.'}), help_text_values: { link: (msg: string) => ( Users.', + label: defineMessage({id: 'admin.saml.ignoreGuestsLdapSyncTitle', defaultMessage: 'Ignore Guest Users when Synchronizing with AD/LDAP'}), + help_text: defineMessage({id: 'admin.saml.ignoreGuestsLdapSyncDesc', defaultMessage: 'When true, Mattermost will ignore Guest Users who are identified by the Guest Attribute, when synchronizing with AD/LDAP for user deactivation and removal and Guest deactivation will need to be managed manually via System Console > Users.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SAML)), it.configIsFalse('GuestAccountsSettings', 'Enable'), @@ -4532,10 +3784,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'SamlSettings.EnableSyncWithLdapIncludeAuth', - label: t('admin.saml.enableSyncWithLdapIncludeAuthTitle'), - label_default: 'Override SAML bind data with AD/LDAP information:', - help_text: t('admin.saml.enableSyncWithLdapIncludeAuthDescription'), - help_text_default: 'When true, Mattermost will override the SAML ID attribute with the AD/LDAP ID attribute if configured or override the SAML Email attribute with the AD/LDAP Email attribute if SAML ID attribute is not present. This will allow you automatically migrate users from Email binding to ID binding to prevent creation of new users when an email address changes for a user. Moving from true to false, will remove the override from happening.\n \nNote: SAML IDs must match the LDAP IDs to prevent disabling of user accounts. Please review documentation for more information.', + label: defineMessage({id: 'admin.saml.enableSyncWithLdapIncludeAuthTitle', defaultMessage: 'Override SAML bind data with AD/LDAP information:'}), + help_text: defineMessage({id: 'admin.saml.enableSyncWithLdapIncludeAuthDescription', defaultMessage: 'When true, Mattermost will override the SAML ID attribute with the AD/LDAP ID attribute if configured or override the SAML Email attribute with the AD/LDAP Email attribute if SAML ID attribute is not present. This will allow you automatically migrate users from Email binding to ID binding to prevent creation of new users when an email address changes for a user. Moving from true to false, will remove the override from happening. Note: SAML IDs must match the LDAP IDs to prevent disabling of user accounts. Please review documentation for more information.'}), help_text_values: { link: (msg: string) => ( /login/sso/saml"', + label: defineMessage({id: 'admin.saml.assertionConsumerServiceURLTitle', defaultMessage: 'Service Provider Login URL:'}), + help_text: defineMessage({id: 'admin.saml.assertionConsumerServiceURLPopulatedDesc', defaultMessage: 'This field is also known as the Assertion Consumer Service URL.'}), + placeholder: defineMessage({id: 'admin.saml.assertionConsumerServiceURLEx', defaultMessage: 'E.g.: "your-mattermost-url"'}), + placeholder_values: { + urlChunk: (chunk: string) => `https://'<${chunk}>'/login/sso/saml`, + }, onConfigLoad: (value, config) => { - const siteUrl = config.ServiceSettings.SiteURL; + const siteUrl = config.ServiceSettings?.SiteURL || ''; if (siteUrl.length > 0 && value.length === 0) { const addSlashIfNeeded = siteUrl[siteUrl.length - 1] === '/' ? '' : '/'; return `${siteUrl}${addSlashIfNeeded}login/sso/saml`; @@ -4679,12 +3908,12 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'SamlSettings.ServiceProviderIdentifier', - label: t('admin.saml.serviceProviderIdentifierTitle'), - label_default: 'Service Provider Identifier:', - help_text: t('admin.saml.serviceProviderIdentifierDesc'), - help_text_default: 'The unique identifier for the Service Provider, usually the same as Service Provider Login URL. In ADFS, this MUST match the Relying Party Identifier.', - placeholder: t('admin.saml.serviceProviderIdentifierEx'), - placeholder_default: "E.g.: \"https://''/login/sso/saml\"", + label: defineMessage({id: 'admin.saml.serviceProviderIdentifierTitle', defaultMessage: 'Service Provider Identifier:'}), + help_text: defineMessage({id: 'admin.saml.serviceProviderIdentifierDesc', defaultMessage: 'The unique identifier for the Service Provider, usually the same as Service Provider Login URL. In ADFS, this MUST match the Relying Party Identifier.'}), + placeholder: defineMessage({id: 'admin.saml.serviceProviderIdentifierEx', defaultMessage: 'E.g.: "your-mattermost-url"'}), + placeholder_values: { + urlChunk: (chunk: string) => `https://'<${chunk}>'/login/sso/saml`, + }, isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SAML)), it.stateIsFalse('SamlSettings.Enable'), @@ -4693,10 +3922,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'SamlSettings.Encrypt', - label: t('admin.saml.encryptTitle'), - label_default: 'Enable Encryption:', - help_text: t('admin.saml.encryptDescription'), - help_text_default: 'When false, Mattermost will not decrypt SAML Assertions encrypted with your Service Provider Public Certificate. Disabling encryption is not recommended for production environments.', + label: defineMessage({id: 'admin.saml.encryptTitle', defaultMessage: 'Enable Encryption:'}), + help_text: defineMessage({id: 'admin.saml.encryptDescription', defaultMessage: 'When false, Mattermost will not decrypt SAML Assertions encrypted with your Service Provider Public Certificate. Disabling encryption is not recommended for production environments.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SAML)), it.stateIsFalse('SamlSettings.Enable'), @@ -4705,18 +3932,12 @@ const AdminDefinition: AdminDefinitionType = { { type: 'fileupload', key: 'SamlSettings.PrivateKeyFile', - label: t('admin.saml.privateKeyFileTitle'), - label_default: 'Service Provider Private Key:', - help_text: t('admin.saml.privateKeyFileFileDesc'), - help_text_default: 'The private key used to decrypt SAML Assertions from the Identity Provider.', - remove_help_text: t('admin.saml.privateKeyFileFileRemoveDesc'), - remove_help_text_default: 'Remove the private key used to decrypt SAML Assertions from the Identity Provider.', - remove_button_text: t('admin.saml.remove.privKey'), - remove_button_text_default: 'Remove Service Provider Private Key', - removing_text: t('admin.saml.removing.privKey'), - removing_text_default: 'Removing Private Key...', - uploading_text: t('admin.saml.uploading.privateKey'), - uploading_text_default: 'Uploading Private Key...', + label: defineMessage({id: 'admin.saml.privateKeyFileTitle', defaultMessage: 'Service Provider Private Key:'}), + help_text: defineMessage({id: 'admin.saml.privateKeyFileFileDesc', defaultMessage: 'The private key used to decrypt SAML Assertions from the Identity Provider.'}), + remove_help_text: defineMessage({id: 'admin.saml.privateKeyFileFileRemoveDesc', defaultMessage: 'Remove the private key used to decrypt SAML Assertions from the Identity Provider.'}), + remove_button_text: defineMessage({id: 'admin.saml.remove.privKey', defaultMessage: 'Remove Service Provider Private Key'}), + removing_text: defineMessage({id: 'admin.saml.removing.privKey', defaultMessage: 'Removing Private Key...'}), + uploading_text: defineMessage({id: 'admin.saml.uploading.privateKey', defaultMessage: 'Uploading Private Key...'}), fileType: '.key', upload_action: uploadPrivateSamlCertificate, remove_action: removePrivateSamlCertificate, @@ -4729,18 +3950,12 @@ const AdminDefinition: AdminDefinitionType = { { type: 'fileupload', key: 'SamlSettings.PublicCertificateFile', - label: t('admin.saml.publicCertificateFileTitle'), - label_default: 'Service Provider Public Certificate:', - help_text: t('admin.saml.publicCertificateFileDesc'), - help_text_default: 'The certificate used to generate the signature on a SAML request to the Identity Provider for a service provider initiated SAML login, when Mattermost is the Service Provider.', - remove_help_text: t('admin.saml.publicCertificateFileRemoveDesc'), - remove_help_text_default: 'Remove the certificate used to generate the signature on a SAML request to the Identity Provider for a service provider initiated SAML login, when Mattermost is the Service Provider.', - remove_button_text: t('admin.saml.remove.sp_certificate'), - remove_button_text_default: 'Remove Service Provider Certificate', - removing_text: t('admin.saml.removing.certificate'), - removing_text_default: 'Removing Certificate...', - uploading_text: t('admin.saml.uploading.certificate'), - uploading_text_default: 'Uploading Certificate...', + label: defineMessage({id: 'admin.saml.publicCertificateFileTitle', defaultMessage: 'Service Provider Public Certificate:'}), + help_text: defineMessage({id: 'admin.saml.publicCertificateFileDesc', defaultMessage: 'The certificate used to generate the signature on a SAML request to the Identity Provider for a service provider initiated SAML login, when Mattermost is the Service Provider.'}), + remove_help_text: defineMessage({id: 'admin.saml.publicCertificateFileRemoveDesc', defaultMessage: 'Remove the certificate used to generate the signature on a SAML request to the Identity Provider for a service provider initiated SAML login, when Mattermost is the Service Provider.'}), + remove_button_text: defineMessage({id: 'admin.saml.remove.sp_certificate', defaultMessage: 'Remove Service Provider Certificate'}), + removing_text: defineMessage({id: 'admin.saml.removing.certificate', defaultMessage: 'Removing Certificate...'}), + uploading_text: defineMessage({id: 'admin.saml.uploading.certificate', defaultMessage: 'Uploading Certificate...'}), fileType: '.crt,.cer', upload_action: uploadPublicSamlCertificate, remove_action: removePublicSamlCertificate, @@ -4753,10 +3968,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'SamlSettings.SignRequest', - label: t('admin.saml.signRequestTitle'), - label_default: 'Sign Request:', - help_text: t('admin.saml.signRequestDescription'), - help_text_default: 'When true, Mattermost will sign the SAML request using your private key. When false, Mattermost will not sign the SAML request.', + label: defineMessage({id: 'admin.saml.signRequestTitle', defaultMessage: 'Sign Request:'}), + help_text: defineMessage({id: 'admin.saml.signRequestDescription', defaultMessage: 'When true, Mattermost will sign the SAML request using your private key. When false, Mattermost will not sign the SAML request.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SAML)), it.stateIsFalse('SamlSettings.Encrypt'), @@ -4767,8 +3980,7 @@ const AdminDefinition: AdminDefinitionType = { { type: 'dropdown', key: 'SamlSettings.SignatureAlgorithm', - label: t('admin.saml.signatureAlgorithmTitle'), - label_default: 'Signature Algorithm', + label: defineMessage({id: 'admin.saml.signatureAlgorithmTitle', defaultMessage: 'Signature Algorithm'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SAML)), it.stateIsFalse('SamlSettings.Encrypt'), @@ -4777,46 +3989,35 @@ const AdminDefinition: AdminDefinitionType = { options: [ { value: SAML_SETTINGS_SIGNATURE_ALGORITHM_SHA1, - display_name: t('admin.saml.signatureAlgorithmDisplay.sha1'), - display_name_default: SAML_SETTINGS_SIGNATURE_ALGORITHM_SHA1, - help_text: t('admin.saml.signatureAlgorithmDescription.sha1'), - help_text_default: 'Specify the Signature algorithm used to sign the request (RSAwithSHA1). Please see more information provided at http://www.w3.org/2000/09/xmldsig#rsa-sha1', + display_name: defineMessage({id: 'admin.saml.signatureAlgorithmDisplay.sha1', defaultMessage: 'RSAwithSHA1'}), + help_text: defineMessage({id: 'admin.saml.signatureAlgorithmDescription.sha1', defaultMessage: 'Specify the Signature algorithm used to sign the request (RSAwithSHA1). Please see more information provided at http://www.w3.org/2000/09/xmldsig#rsa-sha1'}), }, { value: SAML_SETTINGS_SIGNATURE_ALGORITHM_SHA256, - display_name: t('admin.saml.signatureAlgorithmDisplay.sha256'), - display_name_default: SAML_SETTINGS_SIGNATURE_ALGORITHM_SHA256, - help_text: t('admin.saml.signatureAlgorithmDescription.sha256'), - help_text_default: 'Specify the Signature algorithm used to sign the request (RSAwithSHA256). Please see more information provided at http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 [section 6.4.2 RSA (PKCS#1 v1.5)]', + display_name: defineMessage({id: 'admin.saml.signatureAlgorithmDisplay.sha256', defaultMessage: 'RSAwithSHA256'}), + help_text: defineMessage({id: 'admin.saml.signatureAlgorithmDescription.sha256', defaultMessage: 'Specify the Signature algorithm used to sign the request (RSAwithSHA256). Please see more information provided at http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 [section 6.4.2 RSA (PKCS#1 v1.5)]'}), }, { value: SAML_SETTINGS_SIGNATURE_ALGORITHM_SHA512, - display_name: t('admin.saml.signatureAlgorithmDisplay.sha512'), - display_name_default: SAML_SETTINGS_SIGNATURE_ALGORITHM_SHA512, - help_text: t('admin.saml.signatureAlgorithmDescription.sha512'), - help_text_default: 'Specify the Signature algorithm used to sign the request (RSAwithSHA512). Please see more information provided at http://www.w3.org/2001/04/xmldsig-more#rsa-sha512', + display_name: defineMessage({id: 'admin.saml.signatureAlgorithmDisplay.sha512', defaultMessage: 'RSAwithSHA512'}), + help_text: defineMessage({id: 'admin.saml.signatureAlgorithmDescription.sha512', defaultMessage: 'Specify the Signature algorithm used to sign the request (RSAwithSHA512). Please see more information provided at http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'}), }, ], }, { type: 'dropdown', key: 'SamlSettings.CanonicalAlgorithm', - label: t('admin.saml.canonicalAlgorithmTitle'), - label_default: 'Canonicalization Algorithm', + label: defineMessage({id: 'admin.saml.canonicalAlgorithmTitle', defaultMessage: 'Canonicalization Algorithm'}), options: [ { value: SAML_SETTINGS_CANONICAL_ALGORITHM_C14N, - display_name: t('admin.saml.canonicalAlgorithmDisplay.n10'), - display_name_default: 'Exclusive XML Canonicalization 1.0 (omit comments)', - help_text: t('admin.saml.canonicalAlgorithmDescription.exc'), - help_text_default: 'Specify the Canonicalization algorithm (Exclusive XML Canonicalization 1.0). Please see more information provided at http://www.w3.org/2001/10/xml-exc-c14n#', + display_name: defineMessage({id: 'admin.saml.canonicalAlgorithmDisplay.n10', defaultMessage: 'Exclusive XML Canonicalization 1.0 (omit comments)'}), + help_text: defineMessage({id: 'admin.saml.canonicalAlgorithmDescription.exc', defaultMessage: 'Specify the Canonicalization algorithm (Exclusive XML Canonicalization 1.0). Please see more information provided at http://www.w3.org/2001/10/xml-exc-c14n#'}), }, { value: SAML_SETTINGS_CANONICAL_ALGORITHM_C14N11, - display_name: t('admin.saml.canonicalAlgorithmDisplay.n11'), - display_name_default: 'Canonical XML 1.1 (omit comments)', - help_text: t('admin.saml.canonicalAlgorithmDescription.c14'), - help_text_default: 'Specify the Canonicalization algorithm (Canonical XML 1.1). Please see more information provided at http://www.w3.org/2006/12/xml-c14n11', + display_name: defineMessage({id: 'admin.saml.canonicalAlgorithmDisplay.n11', defaultMessage: 'Canonical XML 1.1 (omit comments)'}), + help_text: defineMessage({id: 'admin.saml.canonicalAlgorithmDescription.c14', defaultMessage: 'Specify the Canonicalization algorithm (Canonical XML 1.1). Please see more information provided at http://www.w3.org/2006/12/xml-c14n11'}), }, ], isDisabled: it.any( @@ -4828,12 +4029,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'SamlSettings.EmailAttribute', - label: t('admin.saml.emailAttrTitle'), - label_default: 'Email Attribute:', - placeholder: t('admin.saml.emailAttrEx'), - placeholder_default: 'E.g.: "Email" or "PrimaryEmail"', - help_text: t('admin.saml.emailAttrDesc'), - help_text_default: 'The attribute in the SAML Assertion that will be used to populate the email addresses of users in Mattermost.', + label: defineMessage({id: 'admin.saml.emailAttrTitle', defaultMessage: 'Email Attribute:'}), + placeholder: defineMessage({id: 'admin.saml.emailAttrEx', defaultMessage: 'E.g.: "Email" or "PrimaryEmail"'}), + help_text: defineMessage({id: 'admin.saml.emailAttrDesc', defaultMessage: 'The attribute in the SAML Assertion that will be used to populate the email addresses of users in Mattermost.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SAML)), it.stateIsFalse('SamlSettings.Enable'), @@ -4842,12 +4040,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'SamlSettings.UsernameAttribute', - label: t('admin.saml.usernameAttrTitle'), - label_default: 'Username Attribute:', - placeholder: t('admin.saml.usernameAttrEx'), - placeholder_default: 'E.g.: "Username"', - help_text: t('admin.saml.usernameAttrDesc'), - help_text_default: 'The attribute in the SAML Assertion that will be used to populate the username field in Mattermost.', + label: defineMessage({id: 'admin.saml.usernameAttrTitle', defaultMessage: 'Username Attribute:'}), + placeholder: defineMessage({id: 'admin.saml.usernameAttrEx', defaultMessage: 'E.g.: "Username"'}), + help_text: defineMessage({id: 'admin.saml.usernameAttrDesc', defaultMessage: 'The attribute in the SAML Assertion that will be used to populate the username field in Mattermost.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SAML)), it.stateIsFalse('SamlSettings.Enable'), @@ -4856,12 +4051,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'SamlSettings.IdAttribute', - label: t('admin.saml.idAttrTitle'), - label_default: 'Id Attribute:', - placeholder: t('admin.saml.idAttrEx'), - placeholder_default: 'E.g.: "Id"', - help_text: t('admin.saml.idAttrDesc'), - help_text_default: '(Optional) The attribute in the SAML Assertion that will be used to bind users from SAML to users in Mattermost.', + label: defineMessage({id: 'admin.saml.idAttrTitle', defaultMessage: 'Id Attribute:'}), + placeholder: defineMessage({id: 'admin.saml.idAttrEx', defaultMessage: 'E.g.: "Id"'}), + help_text: defineMessage({id: 'admin.saml.idAttrDesc', defaultMessage: '(Optional) The attribute in the SAML Assertion that will be used to bind users from SAML to users in Mattermost.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SAML)), it.stateIsFalse('SamlSettings.Enable'), @@ -4870,12 +4062,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'SamlSettings.GuestAttribute', - label: t('admin.saml.guestAttrTitle'), - label_default: 'Guest Attribute:', - placeholder: t('admin.saml.guestAttrEx'), - placeholder_default: 'E.g.: "usertype=Guest" or "isGuest=true"', - help_text: t('admin.saml.guestAttrDesc'), - help_text_default: '(Optional) Requires Guest Access to be enabled before being applied. The attribute in the SAML Assertion that will be used to apply a guest role to users in Mattermost. Guests are prevented from accessing teams or channels upon logging in until they are assigned a team and at least one channel.\n \nNote: If this attribute is removed/changed from your guest user in SAML and the user is still active, they will not be promoted to a member and will retain their Guest role. Guests can be promoted in **System Console > User Management**.\n \n \nExisting members that are identified by this attribute as a guest will be demoted from a member to a guest when they are asked to login next. The next login is based upon Session lengths set in **System Console > Session Lengths**. It is highly recommend to manually demote users to guests in **System Console > User Management ** to ensure access is restricted immediately.', + label: defineMessage({id: 'admin.saml.guestAttrTitle', defaultMessage: 'Guest Attribute:'}), + placeholder: defineMessage({id: 'admin.saml.guestAttrEx', defaultMessage: 'E.g.: "usertype=Guest" or "isGuest=true"'}), + help_text: defineMessage({id: 'admin.saml.guestAttrDesc', defaultMessage: '(Optional) Requires Guest Access to be enabled before being applied. The attribute in the SAML Assertion that will be used to apply a guest role to users in Mattermost. Guests are prevented from accessing teams or channels upon logging in until they are assigned a team and at least one channel. Note: If this attribute is removed/changed from your guest user in SAML and the user is still active, they will not be promoted to a member and will retain their Guest role. Guests can be promoted in **System Console > User Management**. Existing members that are identified by this attribute as a guest will be demoted from a member to a guest when they are asked to login next. The next login is based upon Session lengths set in **System Console > Session Lengths**. It is highly recommend to manually demote users to guests in **System Console > User Management ** to ensure access is restricted immediately.'}), help_text_markdown: true, isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SAML)), @@ -4886,8 +4075,7 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'SamlSettings.EnableAdminAttribute', - label: t('admin.saml.enableAdminAttrTitle'), - label_default: 'Enable Admin Attribute:', + label: defineMessage({id: 'admin.saml.enableAdminAttrTitle', defaultMessage: 'Enable Admin Attribute:'}), isDisabled: it.any( it.not(it.isSystemAdmin), it.stateIsFalse('SamlSettings.Enable'), @@ -4896,12 +4084,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'SamlSettings.AdminAttribute', - label: t('admin.saml.adminAttrTitle'), - label_default: 'Admin Attribute:', - placeholder: t('admin.saml.adminAttrEx'), - placeholder_default: 'E.g.: "usertype=Admin" or "isAdmin=true"', - help_text: t('admin.saml.adminAttrDesc'), - help_text_default: '(Optional) The attribute in the SAML Assertion for designating System Admins. The users selected by the query will have access to your Mattermost server as System Admins. By default, System Admins have complete access to the Mattermost System Console.\n \nExisting members that are identified by this attribute will be promoted from member to System Admin upon next login. The next login is based upon Session lengths set in **System Console > Session Lengths.** It is highly recommend to manually demote users to members in **System Console > User Management** to ensure access is restricted immediately.\n \nNote: If this filter is removed/changed, System Admins that were promoted via this filter will be demoted to members and will not retain access to the System Console. When this filter is not in use, System Admins can be manually promoted/demoted in **System Console > User Management**.', + label: defineMessage({id: 'admin.saml.adminAttrTitle', defaultMessage: 'Admin Attribute:'}), + placeholder: defineMessage({id: 'admin.saml.adminAttrEx', defaultMessage: 'E.g.: "usertype=Admin" or "isAdmin=true"'}), + help_text: defineMessage({id: 'admin.saml.adminAttrDesc', defaultMessage: '(Optional) The attribute in the SAML Assertion for designating System Admins. The users selected by the query will have access to your Mattermost server as System Admins. By default, System Admins have complete access to the Mattermost System Console. Existing members that are identified by this attribute will be promoted from member to System Admin upon next login. The next login is based upon Session lengths set in **System Console > Session Lengths.** It is highly recommend to manually demote users to members in **System Console > User Management** to ensure access is restricted immediately. Note: If this filter is removed/changed, System Admins that were promoted via this filter will be demoted to members and will not retain access to the System Console. When this filter is not in use, System Admins can be manually promoted/demoted in **System Console > User Management**.'}), help_text_markdown: true, isDisabled: it.any( it.not(it.isSystemAdmin), @@ -4912,12 +4097,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'SamlSettings.FirstNameAttribute', - label: t('admin.saml.firstnameAttrTitle'), - label_default: 'First Name Attribute:', - placeholder: t('admin.saml.firstnameAttrEx'), - placeholder_default: 'E.g.: "FirstName"', - help_text: t('admin.saml.firstnameAttrDesc'), - help_text_default: '(Optional) The attribute in the SAML Assertion that will be used to populate the first name of users in Mattermost.', + label: defineMessage({id: 'admin.saml.firstnameAttrTitle', defaultMessage: 'First Name Attribute:'}), + placeholder: defineMessage({id: 'admin.saml.firstnameAttrEx', defaultMessage: 'E.g.: "FirstName"'}), + help_text: defineMessage({id: 'admin.saml.firstnameAttrDesc', defaultMessage: '(Optional) The attribute in the SAML Assertion that will be used to populate the first name of users in Mattermost.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SAML)), it.stateIsFalse('SamlSettings.Enable'), @@ -4926,12 +4108,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'SamlSettings.LastNameAttribute', - label: t('admin.saml.lastnameAttrTitle'), - label_default: 'Last Name Attribute:', - placeholder: t('admin.saml.lastnameAttrEx'), - placeholder_default: 'E.g.: "LastName"', - help_text: t('admin.saml.lastnameAttrDesc'), - help_text_default: '(Optional) The attribute in the SAML Assertion that will be used to populate the last name of users in Mattermost.', + label: defineMessage({id: 'admin.saml.lastnameAttrTitle', defaultMessage: 'Last Name Attribute:'}), + placeholder: defineMessage({id: 'admin.saml.lastnameAttrEx', defaultMessage: 'E.g.: "LastName"'}), + help_text: defineMessage({id: 'admin.saml.lastnameAttrDesc', defaultMessage: '(Optional) The attribute in the SAML Assertion that will be used to populate the last name of users in Mattermost.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SAML)), it.stateIsFalse('SamlSettings.Enable'), @@ -4940,12 +4119,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'SamlSettings.NicknameAttribute', - label: t('admin.saml.nicknameAttrTitle'), - label_default: 'Nickname Attribute:', - placeholder: t('admin.saml.nicknameAttrEx'), - placeholder_default: 'E.g.: "Nickname"', - help_text: t('admin.saml.nicknameAttrDesc'), - help_text_default: '(Optional) The attribute in the SAML Assertion that will be used to populate the nickname of users in Mattermost.', + label: defineMessage({id: 'admin.saml.nicknameAttrTitle', defaultMessage: 'Nickname Attribute:'}), + placeholder: defineMessage({id: 'admin.saml.nicknameAttrEx', defaultMessage: 'E.g.: "Nickname"'}), + help_text: defineMessage({id: 'admin.saml.nicknameAttrDesc', defaultMessage: '(Optional) The attribute in the SAML Assertion that will be used to populate the nickname of users in Mattermost.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SAML)), it.stateIsFalse('SamlSettings.Enable'), @@ -4954,12 +4130,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'SamlSettings.PositionAttribute', - label: t('admin.saml.positionAttrTitle'), - label_default: 'Position Attribute:', - placeholder: t('admin.saml.positionAttrEx'), - placeholder_default: 'E.g.: "Role"', - help_text: t('admin.saml.positionAttrDesc'), - help_text_default: '(Optional) The attribute in the SAML Assertion that will be used to populate the position of users in Mattermost.', + label: defineMessage({id: 'admin.saml.positionAttrTitle', defaultMessage: 'Position Attribute:'}), + placeholder: defineMessage({id: 'admin.saml.positionAttrEx', defaultMessage: 'E.g.: "Role"'}), + help_text: defineMessage({id: 'admin.saml.positionAttrDesc', defaultMessage: '(Optional) The attribute in the SAML Assertion that will be used to populate the position of users in Mattermost.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SAML)), it.stateIsFalse('SamlSettings.Enable'), @@ -4968,12 +4141,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'SamlSettings.LocaleAttribute', - label: t('admin.saml.localeAttrTitle'), - label_default: 'Preferred Language Attribute:', - placeholder: t('admin.saml.localeAttrEx'), - placeholder_default: 'E.g.: "Locale" or "PrimaryLanguage"', - help_text: t('admin.saml.localeAttrDesc'), - help_text_default: '(Optional) The attribute in the SAML Assertion that will be used to populate the language of users in Mattermost.', + label: defineMessage({id: 'admin.saml.localeAttrTitle', defaultMessage: 'Preferred Language Attribute:'}), + placeholder: defineMessage({id: 'admin.saml.localeAttrEx', defaultMessage: 'E.g.: "Locale" or "PrimaryLanguage"'}), + help_text: defineMessage({id: 'admin.saml.localeAttrDesc', defaultMessage: '(Optional) The attribute in the SAML Assertion that will be used to populate the language of users in Mattermost.'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SAML)), it.stateIsFalse('SamlSettings.Enable'), @@ -4982,12 +4152,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'SamlSettings.LoginButtonText', - label: t('admin.saml.loginButtonTextTitle'), - label_default: 'Login Button Text:', - placeholder: t('admin.saml.loginButtonTextEx'), - placeholder_default: 'E.g.: "OKTA"', - help_text: t('admin.saml.loginButtonTextDesc'), - help_text_default: '(Optional) The text that appears in the login button on the login page. Defaults to "SAML".', + label: defineMessage({id: 'admin.saml.loginButtonTextTitle', defaultMessage: 'Login Button Text:'}), + placeholder: defineMessage({id: 'admin.saml.loginButtonTextEx', defaultMessage: 'E.g.: "OKTA"'}), + help_text: defineMessage({id: 'admin.saml.loginButtonTextDesc', defaultMessage: '(Optional) The text that appears in the login button on the login page. Defaults to "SAML".'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.SAML)), it.stateIsFalse('SamlSettings.Enable'), @@ -5000,16 +4167,14 @@ const AdminDefinition: AdminDefinitionType = { saml_feature_discovery: { url: 'authentication/saml', isDiscovery: true, - title: t('admin.sidebar.saml'), - title_default: 'SAML 2.0', + title: defineMessage({id: 'admin.sidebar.saml', defaultMessage: 'SAML 2.0'}), isHidden: it.any( it.licensedForFeature('SAML'), it.not(it.enterpriseReady), ), schema: { id: 'SamlSettings', - name: t('admin.authentication.saml'), - name_default: 'SAML 2.0', + name: defineMessage({id: 'admin.authentication.saml', defaultMessage: 'SAML 2.0'}), settings: [ { type: 'custom', @@ -5023,19 +4188,17 @@ const AdminDefinition: AdminDefinitionType = { }, gitlab: { url: 'authentication/gitlab', - title: t('admin.sidebar.gitlab'), - title_default: 'GitLab', + title: defineMessage({id: 'admin.sidebar.gitlab', defaultMessage: 'GitLab'}), isHidden: it.any( it.licensed, it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), ), schema: { id: 'GitLabSettings', - name: t('admin.authentication.gitlab'), - name_default: 'GitLab', - onConfigLoad: (config: any) => { - const newState: any = {}; - newState['GitLabSettings.Url'] = config.GitLabSettings.UserAPIEndpoint.replace('/api/v4/user', ''); + name: defineMessage({id: 'admin.authentication.gitlab', defaultMessage: 'GitLab'}), + onConfigLoad: (config) => { + const newState: {'GitLabSettings.Url'?: string} = {}; + newState['GitLabSettings.Url'] = config.GitLabSettings?.UserAPIEndpoint?.replace('/api/v4/user', ''); return newState; }, onConfigSave: (config) => { @@ -5047,22 +4210,22 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'GitLabSettings.Enable', - label: t('admin.gitlab.enableTitle'), - label_default: 'Enable authentication with GitLab: ', - help_text: t('admin.gitlab.enableDescription'), - help_text_default: "When true, Mattermost allows team creation and account signup using GitLab OAuth.\n \n1. Log in to your GitLab account and go to Profile Settings -> Applications.\n2. Enter Redirect URIs \"''/login/gitlab/complete\" (example: http://localhost:8065/login/gitlab/complete) and \"/signup/gitlab/complete\".\n3. Then use \"Application Secret Key\" and \"Application ID\" fields from GitLab to complete the options below.\n4. Complete the Endpoint URLs below.", + label: defineMessage({id: 'admin.gitlab.enableTitle', defaultMessage: 'Enable authentication with GitLab: '}), + help_text: defineMessage({id: 'admin.gitlab.enableDescription', defaultMessage: 'When true, Mattermost allows team creation and account signup using GitLab OAuth.{lineBreak} {lineBreak}1. Log in to your GitLab account and go to Profile Settings -> Applications.{lineBreak}2. Enter Redirect URIs "your-mattermost-url" (example: http://localhost:8065/login/gitlab/complete) and "your-mattermost-url".\n3. Then use "Application Secret Key" and "Application ID" fields from GitLab to complete the options below.\n4. Complete the Endpoint URLs below.'}), + help_text_values: { + lineBreak: '\n', + loginUrlChunk: (chunk: string) => `<${chunk}>/login/gitlab/complete"`, + signupUrlChunk: (chunk: string) => `<${chunk}>/signup/gitlab/complete"`, + }, help_text_markdown: true, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'GitLabSettings.Id', - label: t('admin.gitlab.clientIdTitle'), - label_default: 'Application ID:', - help_text: t('admin.gitlab.clientIdDescription'), - help_text_default: 'Obtain this value via the instructions above for logging into GitLab.', - placeholder: t('admin.gitlab.clientIdExample'), - placeholder_default: 'E.g.: "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"', + label: defineMessage({id: 'admin.gitlab.clientIdTitle', defaultMessage: 'Application ID:'}), + help_text: defineMessage({id: 'admin.gitlab.clientIdDescription', defaultMessage: 'Obtain this value via the instructions above for logging into GitLab.'}), + placeholder: defineMessage({id: 'admin.gitlab.clientIdExample', defaultMessage: 'E.g.: "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), it.stateIsFalse('GitLabSettings.Enable'), @@ -5071,12 +4234,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'GitLabSettings.Secret', - label: t('admin.gitlab.clientSecretTitle'), - label_default: 'Application Secret Key:', - help_text: t('admin.gitlab.clientSecretDescription'), - help_text_default: 'Obtain this value via the instructions above for logging into GitLab.', - placeholder: t('admin.gitlab.clientSecretExample'), - placeholder_default: 'E.g.: "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"', + label: defineMessage({id: 'admin.gitlab.clientSecretTitle', defaultMessage: 'Application Secret Key:'}), + help_text: defineMessage({id: 'admin.gitlab.clientSecretDescription', defaultMessage: 'Obtain this value via the instructions above for logging into GitLab.'}), + placeholder: defineMessage({id: 'admin.gitlab.clientSecretExample', defaultMessage: 'E.g.: "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), it.stateIsFalse('GitLabSettings.Enable'), @@ -5085,12 +4245,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'GitLabSettings.Url', - label: t('admin.gitlab.siteUrl'), - label_default: 'GitLab Site URL:', - help_text: t('admin.gitlab.siteUrlDescription'), - help_text_default: 'Enter the URL of your GitLab instance, e.g. https://example.com:3000. If your GitLab instance is not set up with SSL, start the URL with http:// instead of https://.', - placeholder: t('admin.gitlab.siteUrlExample'), - placeholder_default: 'E.g.: https://', + label: defineMessage({id: 'admin.gitlab.siteUrl', defaultMessage: 'GitLab Site URL:'}), + help_text: defineMessage({id: 'admin.gitlab.siteUrlDescription', defaultMessage: 'Enter the URL of your GitLab instance, e.g. https://example.com:3000. If your GitLab instance is not set up with SSL, start the URL with http:// instead of https://.'}), + placeholder: defineMessage({id: 'admin.gitlab.siteUrlExample', defaultMessage: 'E.g.: https://'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), it.stateIsFalse('GitLabSettings.Enable'), @@ -5099,8 +4256,7 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'GitLabSettings.UserAPIEndpoint', - label: t('admin.gitlab.userTitle'), - label_default: 'User API Endpoint:', + label: defineMessage({id: 'admin.gitlab.userTitle', defaultMessage: 'User API Endpoint:'}), dynamic_value: (value, config, state) => { if (state['GitLabSettings.Url']) { return state['GitLabSettings.Url'].replace(/\/$/, '') + '/api/v4/user'; @@ -5112,8 +4268,7 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'GitLabSettings.AuthEndpoint', - label: t('admin.gitlab.authTitle'), - label_default: 'Auth Endpoint:', + label: defineMessage({id: 'admin.gitlab.authTitle', defaultMessage: 'Auth Endpoint:'}), dynamic_value: (value, config, state) => { if (state['GitLabSettings.Url']) { return state['GitLabSettings.Url'].replace(/\/$/, '') + '/oauth/authorize'; @@ -5125,8 +4280,7 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'GitLabSettings.TokenEndpoint', - label: t('admin.gitlab.tokenTitle'), - label_default: 'Token Endpoint:', + label: defineMessage({id: 'admin.gitlab.tokenTitle', defaultMessage: 'Token Endpoint:'}), dynamic_value: (value, config, state) => { if (state['GitLabSettings.Url']) { return state['GitLabSettings.Url'].replace(/\/$/, '') + '/oauth/token'; @@ -5140,8 +4294,7 @@ const AdminDefinition: AdminDefinitionType = { }, oauth: { url: 'authentication/oauth', - title: t('admin.sidebar.oauth'), - title_default: 'OAuth 2.0', + title: defineMessage({id: 'admin.sidebar.oauth', defaultMessage: 'OAuth 2.0'}), isHidden: it.any( it.any( it.not(it.licensed), @@ -5155,21 +4308,20 @@ const AdminDefinition: AdminDefinitionType = { ), schema: { id: 'OAuthSettings', - name: t('admin.authentication.oauth'), - name_default: 'OAuth 2.0', + name: defineMessage({id: 'admin.authentication.oauth', defaultMessage: 'OAuth 2.0'}), onConfigLoad: (config) => { - const newState: any = {}; - if (config.GitLabSettings && config.GitLabSettings.Enable) { + const newState: {oauthType?: string; 'GitLabSettings.Url'?: string} = {}; + if (config.GitLabSettings?.Enable) { newState.oauthType = Constants.GITLAB_SERVICE; } - if (config.Office365Settings && config.Office365Settings.Enable) { + if (config.Office365Settings?.Enable) { newState.oauthType = Constants.OFFICE365_SERVICE; } - if (config.GoogleSettings && config.GoogleSettings.Enable) { + if (config.GoogleSettings?.Enable) { newState.oauthType = Constants.GOOGLE_SERVICE; } - newState['GitLabSettings.Url'] = config.GitLabSettings.UserAPIEndpoint.replace('/api/v4/user', ''); + newState['GitLabSettings.Url'] = config.GitLabSettings?.UserAPIEndpoint?.replace('/api/v4/user', ''); return newState; }, @@ -5212,29 +4364,27 @@ const AdminDefinition: AdminDefinitionType = { { type: 'dropdown', key: 'oauthType', - label: t('admin.openid.select'), - label_default: 'Select service provider:', + label: defineMessage({id: 'admin.openid.select', defaultMessage: 'Select service provider:'}), options: [ { value: 'off', - display_name: t('admin.oauth.off'), - display_name_default: 'Do not allow sign-in via an OAuth 2.0 provider.', + display_name: defineMessage({id: 'admin.oauth.off', defaultMessage: 'Do not allow sign-in via an OAuth 2.0 provider.'}), }, { value: Constants.GITLAB_SERVICE, - display_name: t('admin.oauth.gitlab'), - display_name_default: 'GitLab', - help_text: t('admin.gitlab.EnableMarkdownDesc'), - help_text_default: '1. Log in to your GitLab account and go to Profile Settings -> Applications.\n2. Enter Redirect URIs "/login/gitlab/complete" (example: http://localhost:8065/login/gitlab/complete) and "/signup/gitlab/complete".\n3. Then use "Application Secret Key" and "Application ID" fields from GitLab to complete the options below.\n4. Complete the Endpoint URLs below.', + display_name: defineMessage({id: 'admin.oauth.gitlab', defaultMessage: 'GitLab'}), + help_text: defineMessage({id: 'admin.gitlab.EnableMarkdownDesc', defaultMessage: '1. Log in to your GitLab account and go to Profile Settings -> Applications.\n2. Enter Redirect URIs "your-mattermost-url" (example: http://localhost:8065/login/gitlab/complete) and "your-mattermost-url".\n3. Then use "Application Secret Key" and "Application ID" fields from GitLab to complete the options below.\n4. Complete the Endpoint URLs below.'}), + help_text_values: { + loginUrlChunk: (chunk: string) => `<${chunk}>/login/gitlab/complete`, + signupUrlChunk: (chunk: string) => `<${chunk}>/signup/gitlab/complete`, + }, help_text_markdown: true, }, { value: Constants.GOOGLE_SERVICE, - display_name: t('admin.oauth.google'), - display_name_default: 'Google Apps', + display_name: defineMessage({id: 'admin.oauth.google', defaultMessage: 'Google Apps'}), isHidden: it.all(it.not(it.licensedForFeature('GoogleOAuth')), it.not(it.cloudLicensed)), - help_text: t('admin.google.EnableMarkdownDesc'), - help_text_default: '1. Log in to your Google account.\n2. Go to https://console.developers.google.com, click Credentials in the left hand sidebar and enter "Mattermost - your-company-name" as the Project Name, then click Create.\n3. Click the OAuth consent screen header and enter "Mattermost" as the Product name shown to users, then click Save.\n4. Under the Credentials header, click Create credentials, choose OAuth client ID and select Web Application.\n5. Under Restrictions and Authorized redirect URIs enter your-mattermost-url/signup/google/complete (example: http://localhost:8065/signup/google/complete). Click Create.\n6. Paste the Client ID and Client Secret to the fields below, then click Save.\n7. Go to the Google People API and click Enable.', + help_text: defineMessage({id: 'admin.google.EnableMarkdownDesc', defaultMessage: '1. Log in to your Google account.\n2. Go to https://console.developers.google.com, click Credentials in the left hand sidebar and enter "Mattermost - your-company-name" as the Project Name, then click Create.\n3. Click the OAuth consent screen header and enter "Mattermost" as the Product name shown to users, then click Save.\n4. Under the Credentials header, click Create credentials, choose OAuth client ID and select Web Application.\n5. Under Restrictions and Authorized redirect URIs enter your-mattermost-url/signup/google/complete (example: http://localhost:8065/signup/google/complete). Click Create.\n6. Paste the Client ID and Client Secret to the fields below, then click Save.\n7. Go to the Google People API and click Enable.'}), help_text_markdown: false, help_text_values: { linkLogin: (msg: string) => ( @@ -5266,11 +4416,9 @@ const AdminDefinition: AdminDefinitionType = { }, { value: Constants.OFFICE365_SERVICE, - display_name: t('admin.oauth.office365'), - display_name_default: 'Office 365', + display_name: defineMessage({id: 'admin.oauth.office365', defaultMessage: 'Office 365'}), isHidden: it.all(it.not(it.licensedForFeature('Office365OAuth')), it.not(it.cloudLicensed)), - help_text: t('admin.office365.EnableMarkdownDesc'), - help_text_default: '1. Log in to your Microsoft or Office 365 account. Make sure it`s the account on the same tenant that you would like users to log in with.\n2. Go to https://apps.dev.microsoft.com, click Go to app list > Add an app and use "Mattermost - your-company-name" as the Application Name.\n3. Under Application Secrets, click Generate New Password and paste it to the Application Secret Password field below.\n4. Under Platforms, click Add Platform, choose Web and enter your-mattermost-url/signup/office365/complete (example: http://localhost:8065/signup/office365/complete) under Redirect URIs. Also uncheck Allow Implicit Flow.\n5. Finally, click Save and then paste the Application ID below.', + help_text: defineMessage({id: 'admin.office365.EnableMarkdownDesc', defaultMessage: '1. Log in to your Microsoft or Office 365 account. Make sure it`s the account on the same tenant that you would like users to log in with.\n2. Go to https://apps.dev.microsoft.com, click Go to app list > Add an app and use "Mattermost - your-company-name" as the Application Name.\n3. Under Application Secrets, click Generate New Password and paste it to the Application Secret Password field below.\n4. Under Platforms, click Add Platform, choose Web and enter your-mattermost-url/signup/office365/complete (example: http://localhost:8065/signup/office365/complete) under Redirect URIs. Also uncheck Allow Implicit Flow.\n5. Finally, click Save and then paste the Application ID below.'}), help_text_markdown: false, help_text_values: { linkLogin: (msg: string) => ( @@ -5306,44 +4454,34 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'GitLabSettings.Id', - label: t('admin.gitlab.clientIdTitle'), - label_default: 'Application ID:', - help_text: t('admin.gitlab.clientIdDescription'), - help_text_default: 'Obtain this value via the instructions above for logging into GitLab.', - placeholder: t('admin.gitlab.clientIdExample'), - placeholder_default: 'E.g.: "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"', + label: defineMessage({id: 'admin.gitlab.clientIdTitle', defaultMessage: 'Application ID:'}), + help_text: defineMessage({id: 'admin.gitlab.clientIdDescription', defaultMessage: 'Obtain this value via the instructions above for logging into GitLab.'}), + placeholder: defineMessage({id: 'admin.gitlab.clientIdExample', defaultMessage: 'E.g.: "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'}), isHidden: it.not(it.stateEquals('oauthType', 'gitlab')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'GitLabSettings.Secret', - label: t('admin.gitlab.clientSecretTitle'), - label_default: 'Application Secret Key:', - help_text: t('admin.gitlab.clientSecretDescription'), - help_text_default: 'Obtain this value via the instructions above for logging into GitLab.', - placeholder: t('admin.gitlab.clientSecretExample'), - placeholder_default: 'E.g.: "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"', + label: defineMessage({id: 'admin.gitlab.clientSecretTitle', defaultMessage: 'Application Secret Key:'}), + help_text: defineMessage({id: 'admin.gitlab.clientSecretDescription', defaultMessage: 'Obtain this value via the instructions above for logging into GitLab.'}), + placeholder: defineMessage({id: 'admin.gitlab.clientSecretExample', defaultMessage: 'E.g.: "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'}), isHidden: it.not(it.stateEquals('oauthType', 'gitlab')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'GitLabSettings.Url', - label: t('admin.gitlab.siteUrl'), - label_default: 'GitLab Site URL:', - help_text: t('admin.gitlab.siteUrlDescription'), - help_text_default: 'Enter the URL of your GitLab instance, e.g. https://example.com:3000. If your GitLab instance is not set up with SSL, start the URL with http:// instead of https://.', - placeholder: t('admin.gitlab.siteUrlExample'), - placeholder_default: 'E.g.: https://', + label: defineMessage({id: 'admin.gitlab.siteUrl', defaultMessage: 'GitLab Site URL:'}), + help_text: defineMessage({id: 'admin.gitlab.siteUrlDescription', defaultMessage: 'Enter the URL of your GitLab instance, e.g. https://example.com:3000. If your GitLab instance is not set up with SSL, start the URL with http:// instead of https://.'}), + placeholder: defineMessage({id: 'admin.gitlab.siteUrlExample', defaultMessage: 'E.g.: https://'}), isHidden: it.not(it.stateEquals('oauthType', 'gitlab')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'GitLabSettings.UserAPIEndpoint', - label: t('admin.gitlab.userTitle'), - label_default: 'User API Endpoint:', + label: defineMessage({id: 'admin.gitlab.userTitle', defaultMessage: 'User API Endpoint:'}), dynamic_value: (value, config, state) => { if (state['GitLabSettings.Url']) { return state['GitLabSettings.Url'].replace(/\/$/, '') + '/api/v4/user'; @@ -5356,8 +4494,7 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'GitLabSettings.AuthEndpoint', - label: t('admin.gitlab.authTitle'), - label_default: 'Auth Endpoint:', + label: defineMessage({id: 'admin.gitlab.authTitle', defaultMessage: 'Auth Endpoint:'}), dynamic_value: (value, config, state) => { if (state['GitLabSettings.Url']) { return state['GitLabSettings.Url'].replace(/\/$/, '') + '/oauth/authorize'; @@ -5370,8 +4507,7 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'GitLabSettings.TokenEndpoint', - label: t('admin.gitlab.tokenTitle'), - label_default: 'Token Endpoint:', + label: defineMessage({id: 'admin.gitlab.tokenTitle', defaultMessage: 'Token Endpoint:'}), dynamic_value: (value, config, state) => { if (state['GitLabSettings.Url']) { return state['GitLabSettings.Url'].replace(/\/$/, '') + '/oauth/token'; @@ -5384,32 +4520,25 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'GoogleSettings.Id', - label: t('admin.google.clientIdTitle'), - label_default: 'Client ID:', - help_text: t('admin.google.clientIdDescription'), - help_text_default: 'The Client ID you received when registering your application with Google.', - placeholder: t('admin.google.clientIdExample'), - placeholder_default: 'E.g.: "7602141235235-url0fhs1mayfasbmop5qlfns8dh4.apps.googleusercontent.com"', + label: defineMessage({id: 'admin.google.clientIdTitle', defaultMessage: 'Client ID:'}), + help_text: defineMessage({id: 'admin.google.clientIdDescription', defaultMessage: 'The Client ID you received when registering your application with Google.'}), + placeholder: defineMessage({id: 'admin.google.clientIdExample', defaultMessage: 'E.g.: "7602141235235-url0fhs1mayfasbmop5qlfns8dh4.apps.googleusercontent.com"'}), isHidden: it.not(it.stateEquals('oauthType', 'google')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'GoogleSettings.Secret', - label: t('admin.google.clientSecretTitle'), - label_default: 'Client Secret:', - help_text: t('admin.google.clientSecretDescription'), - help_text_default: 'The Client Secret you received when registering your application with Google.', - placeholder: t('admin.google.clientSecretExample'), - placeholder_default: 'E.g.: "H8sz0Az-dDs2p15-7QzD231"', + label: defineMessage({id: 'admin.google.clientSecretTitle', defaultMessage: 'Client Secret:'}), + help_text: defineMessage({id: 'admin.google.clientSecretDescription', defaultMessage: 'The Client Secret you received when registering your application with Google.'}), + placeholder: defineMessage({id: 'admin.google.clientSecretExample', defaultMessage: 'E.g.: "H8sz0Az-dDs2p15-7QzD231"'}), isHidden: it.not(it.stateEquals('oauthType', 'google')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'GoogleSettings.UserAPIEndpoint', - label: t('admin.google.userTitle'), - label_default: 'User API Endpoint:', + label: defineMessage({id: 'admin.google.userTitle', defaultMessage: 'User API Endpoint:'}), dynamic_value: () => 'https://people.googleapis.com/v1/people/me?personFields=names,emailAddresses,nicknames,metadata', isDisabled: true, isHidden: it.not(it.stateEquals('oauthType', 'google')), @@ -5417,8 +4546,7 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'GoogleSettings.AuthEndpoint', - label: t('admin.google.authTitle'), - label_default: 'Auth Endpoint:', + label: defineMessage({id: 'admin.google.authTitle', defaultMessage: 'Auth Endpoint:'}), dynamic_value: () => 'https://accounts.google.com/o/oauth2/v2/auth', isDisabled: true, isHidden: it.not(it.stateEquals('oauthType', 'google')), @@ -5426,8 +4554,7 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'GoogleSettings.TokenEndpoint', - label: t('admin.google.tokenTitle'), - label_default: 'Token Endpoint:', + label: defineMessage({id: 'admin.google.tokenTitle', defaultMessage: 'Token Endpoint:'}), dynamic_value: () => 'https://www.googleapis.com/oauth2/v4/token', isDisabled: true, isHidden: it.not(it.stateEquals('oauthType', 'google')), @@ -5435,44 +4562,34 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'Office365Settings.Id', - label: t('admin.office365.clientIdTitle'), - label_default: 'Application ID:', - help_text: t('admin.office365.clientIdDescription'), - help_text_default: 'The Application/Client ID you received when registering your application with Microsoft.', - placeholder: t('admin.office365.clientIdExample'), - placeholder_default: 'E.g.: "adf3sfa2-ag3f-sn4n-ids0-sh1hdax192qq"', + label: defineMessage({id: 'admin.office365.clientIdTitle', defaultMessage: 'Application ID:'}), + help_text: defineMessage({id: 'admin.office365.clientIdDescription', defaultMessage: 'The Application/Client ID you received when registering your application with Microsoft.'}), + placeholder: defineMessage({id: 'admin.office365.clientIdExample', defaultMessage: 'E.g.: "adf3sfa2-ag3f-sn4n-ids0-sh1hdax192qq"'}), isHidden: it.not(it.stateEquals('oauthType', 'office365')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'Office365Settings.Secret', - label: t('admin.office365.clientSecretTitle'), - label_default: 'Application Secret Password:', - help_text: t('admin.office365.clientSecretDescription'), - help_text_default: 'The Application Secret Password you generated when registering your application with Microsoft.', - placeholder: t('admin.office365.clientSecretExample'), - placeholder_default: 'E.g.: "shAieM47sNBfgl20f8ci294"', + label: defineMessage({id: 'admin.office365.clientSecretTitle', defaultMessage: 'Application Secret Password:'}), + help_text: defineMessage({id: 'admin.office365.clientSecretDescription', defaultMessage: 'The Application Secret Password you generated when registering your application with Microsoft.'}), + placeholder: defineMessage({id: 'admin.office365.clientSecretExample', defaultMessage: 'E.g.: "shAieM47sNBfgl20f8ci294"'}), isHidden: it.not(it.stateEquals('oauthType', 'office365')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'Office365Settings.DirectoryId', - label: t('admin.office365.directoryIdTitle'), - label_default: 'Directory (tenant) ID:', - help_text: t('admin.office365.directoryIdDescription'), - help_text_default: 'The Directory (tenant) ID you received when registering your application with Microsoft.', - placeholder: t('admin.office365.directoryIdExample'), - placeholder_default: 'E.g.: "adf3sfa2-ag3f-sn4n-ids0-sh1hdax192qq"', + label: defineMessage({id: 'admin.office365.directoryIdTitle', defaultMessage: 'Directory (tenant) ID:'}), + help_text: defineMessage({id: 'admin.office365.directoryIdDescription', defaultMessage: 'The Directory (tenant) ID you received when registering your application with Microsoft.'}), + placeholder: defineMessage({id: 'admin.office365.directoryIdExample', defaultMessage: 'E.g.: "adf3sfa2-ag3f-sn4n-ids0-sh1hdax192qq"'}), isHidden: it.not(it.stateEquals('oauthType', 'office365')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'Office365Settings.UserAPIEndpoint', - label: t('admin.office365.userTitle'), - label_default: 'User API Endpoint:', + label: defineMessage({id: 'admin.office365.userTitle', defaultMessage: 'User API Endpoint:'}), dynamic_value: () => 'https://graph.microsoft.com/v1.0/me', isDisabled: true, isHidden: it.not(it.stateEquals('oauthType', 'office365')), @@ -5480,8 +4597,7 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'Office365Settings.AuthEndpoint', - label: t('admin.office365.authTitle'), - label_default: 'Auth Endpoint:', + label: defineMessage({id: 'admin.office365.authTitle', defaultMessage: 'Auth Endpoint:'}), dynamic_value: (value, config, state) => { if (state['Office365Settings.DirectoryId']) { return 'https://login.microsoftonline.com/' + state['Office365Settings.DirectoryId'] + '/oauth2/v2.0/authorize'; @@ -5494,8 +4610,7 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'Office365Settings.TokenEndpoint', - label: t('admin.office365.tokenTitle'), - label_default: 'Token Endpoint:', + label: defineMessage({id: 'admin.office365.tokenTitle', defaultMessage: 'Token Endpoint:'}), dynamic_value: (value, config, state) => { if (state['Office365Settings.DirectoryId']) { return 'https://login.microsoftonline.com/' + state['Office365Settings.DirectoryId'] + '/oauth2/v2.0/token'; @@ -5510,33 +4625,31 @@ const AdminDefinition: AdminDefinitionType = { }, openid: { url: 'authentication/openid', - title: t('admin.sidebar.openid'), - title_default: 'OpenID Connect', + title: defineMessage({id: 'admin.sidebar.openid', defaultMessage: 'OpenID Connect'}), isHidden: it.any( it.all(it.not(it.licensedForFeature('OpenId')), it.not(it.cloudLicensed)), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), ), schema: { id: 'OpenIdSettings', - name: t('admin.authentication.openid'), - name_default: 'OpenID Connect', + name: defineMessage({id: 'admin.authentication.openid', defaultMessage: 'OpenID Connect'}), onConfigLoad: (config) => { - const newState: any = {}; - if (config.Office365Settings && config.Office365Settings.Enable) { + const newState: {openidType?: string; 'GitLabSettings.Url'?: string} = {}; + if (config.Office365Settings?.Enable) { newState.openidType = Constants.OFFICE365_SERVICE; } - if (config.GoogleSettings && config.GoogleSettings.Enable) { + if (config.GoogleSettings?.Enable) { newState.openidType = Constants.GOOGLE_SERVICE; } - if (config.GitLabSettings && config.GitLabSettings.Enable) { + if (config.GitLabSettings?.Enable) { newState.openidType = Constants.GITLAB_SERVICE; } - if (config.OpenIdSettings && config.OpenIdSettings.Enable) { + if (config.OpenIdSettings?.Enable) { newState.openidType = Constants.OPENID_SERVICE; } - if (config.GitLabSettings.UserAPIEndpoint) { + if (config.GitLabSettings?.UserAPIEndpoint) { newState['GitLabSettings.Url'] = config.GitLabSettings.UserAPIEndpoint.replace('/api/v4/user', ''); - } else if (config.GitLabSettings.DiscoveryEndpoint) { + } else if (config.GitLabSettings?.DiscoveryEndpoint) { newState['GitLabSettings.Url'] = config.GitLabSettings.DiscoveryEndpoint.replace('/.well-known/openid-configuration', ''); } @@ -5589,29 +4702,27 @@ const AdminDefinition: AdminDefinitionType = { { type: 'dropdown', key: 'openidType', - label: t('admin.openid.select'), - label_default: 'Select service provider:', + label: defineMessage({id: 'admin.openid.select', defaultMessage: 'Select service provider:'}), isHelpHidden: it.all(it.stateEquals('openidType', Constants.OPENID_SERVICE), it.licensedForCloudStarter), options: [ { value: 'off', - display_name: t('admin.openid.off'), - display_name_default: 'Do not allow sign-in via an OpenID provider.', + display_name: defineMessage({id: 'admin.openid.off', defaultMessage: 'Do not allow sign-in via an OpenID provider.'}), }, { value: Constants.GITLAB_SERVICE, - display_name: t('admin.openid.gitlab'), - display_name_default: 'GitLab', - help_text: t('admin.gitlab.EnableMarkdownDesc'), - help_text_default: '1. Log in to your GitLab account and go to Profile Settings -> Applications.\n2. Enter Redirect URIs "/login/gitlab/complete" (example: http://localhost:8065/login/gitlab/complete) and "/signup/gitlab/complete".\n3. Then use "Application Secret Key" and "Application ID" fields from GitLab to complete the options below.\n4. Complete the Endpoint URLs below.', + display_name: defineMessage({id: 'admin.openid.gitlab', defaultMessage: 'GitLab'}), + help_text: defineMessage({id: 'admin.gitlab.EnableMarkdownDesc', defaultMessage: '1. Log in to your GitLab account and go to Profile Settings -> Applications.\n2. Enter Redirect URIs "your-mattermost-url" (example: http://localhost:8065/login/gitlab/complete) and "your-mattermost-url".\n3. Then use "Application Secret Key" and "Application ID" fields from GitLab to complete the options below.\n4. Complete the Endpoint URLs below.'}), + help_text_values: { + loginUrlChunk: (chunk: string) => `<${chunk}>/login/gitlab/complete`, + signupUrlChunk: (chunk: string) => `<${chunk}>/signup/gitlab/complete`, + }, help_text_markdown: false, }, { value: Constants.GOOGLE_SERVICE, - display_name: t('admin.openid.google'), - display_name_default: 'Google Apps', - help_text: t('admin.google.EnableMarkdownDesc'), - help_text_default: '1. Log in to your Google account.\n2. Go to https://console.developers.google.com], click Credentials in the left hand side.\n 3. Under the Credentials header, click Create credentials, choose OAuth client ID and select Web Application.\n 4. Enter "Mattermost - your-company-name" as the Name.\n 5. Under Authorized redirect URIs enter your-mattermost-url/signup/google/complete (example: http://localhost:8065/signup/google/complete). Click Create.\n 6. Paste the Client ID and Client Secret to the fields below, then click Save.\n 7. Go to the Google People API and click Enable.', + display_name: defineMessage({id: 'admin.openid.google', defaultMessage: 'Google Apps'}), + help_text: defineMessage({id: 'admin.google.EnableMarkdownDesc', defaultMessage: '1. Log in to your Google account.\n2. Go to https://console.developers.google.com], click Credentials in the left hand side.\n 3. Under the Credentials header, click Create credentials, choose OAuth client ID and select Web Application.\n 4. Enter "Mattermost - your-company-name" as the Name.\n 5. Under Authorized redirect URIs enter your-mattermost-url/signup/google/complete (example: http://localhost:8065/signup/google/complete). Click Create.\n 6. Paste the Client ID and Client Secret to the fields below, then click Save.\n 7. Go to the Google People API and click Enable.'}), help_text_markdown: false, help_text_values: { linkLogin: (msg: string) => ( @@ -5643,10 +4754,8 @@ const AdminDefinition: AdminDefinitionType = { }, { value: Constants.OFFICE365_SERVICE, - display_name: t('admin.openid.office365'), - display_name_default: 'Office 365', - help_text: t('admin.office365.EnableMarkdownDesc'), - help_text_default: '1. Log in to your Microsoft or Office 365 account. Make sure it`s the account on the same tenant that you would like users to log in with.\n2. Go to https://apps.dev.microsoft.com, click Go to Azure Portal > click New Registration.\n3. Use "Mattermost - your-company-name" as the Application Name, click Registration, paste Client ID and Tenant ID below.\n4. Click Authentication, under Platforms, click Add Platform, choose Web and enter your-mattermost-url/signup/office365/complete (example: http://localhost:8065/signup/office365/complete) under Redirect URIs. Also uncheck Allow Implicit Flow.\n5. Click Certificates & secrets, Generate New client secret and paste secret value in Client Secret field below.', + display_name: defineMessage({id: 'admin.openid.office365', defaultMessage: 'Office 365'}), + help_text: defineMessage({id: 'admin.office365.EnableMarkdownDesc', defaultMessage: '1. Log in to your Microsoft or Office 365 account. Make sure it`s the account on the same tenant that you would like users to log in with.\n2. Go to https://apps.dev.microsoft.com, click Go to Azure Portal > click New Registration.\n3. Use "Mattermost - your-company-name" as the Application Name, click Registration, paste Client ID and Tenant ID below.\n4. Click Authentication, under Platforms, click Add Platform, choose Web and enter your-mattermost-url/signup/office365/complete (example: http://localhost:8065/signup/office365/complete) under Redirect URIs. Also uncheck Allow Implicit Flow.\n5. Click Certificates & secrets, Generate New client secret and paste secret value in Client Secret field below.'}), help_text_markdown: false, help_text_values: { linkLogin: (msg: string) => ( @@ -5678,10 +4787,8 @@ const AdminDefinition: AdminDefinitionType = { }, { value: Constants.OPENID_SERVICE, - display_name: t('admin.oauth.openid'), - display_name_default: 'OpenID Connect (Other)', - help_text: t('admin.openid.EnableMarkdownDesc'), - help_text_default: 'Follow provider directions for creating an OpenID Application. Most OpenID Connect providers require authorization of all redirect URIs. In the appropriate field, enter your-mattermost-url/signup/openid/complete (example: http://domain.com/signup/openid/complete)', + display_name: defineMessage({id: 'admin.oauth.openid', defaultMessage: 'OpenID Connect (Other)'}), + help_text: defineMessage({id: 'admin.openid.EnableMarkdownDesc', defaultMessage: 'Follow provider directions for creating an OpenID Application. Most OpenID Connect providers require authorization of all redirect URIs. In the appropriate field, enter your-mattermost-url/signup/openid/complete (example: http://domain.com/signup/openid/complete)'}), help_text_markdown: false, }, ], @@ -5690,24 +4797,19 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'GitLabSettings.Url', - label: t('admin.gitlab.siteUrl'), - label_default: 'GitLab Site URL:', - help_text: t('admin.gitlab.siteUrlDescription'), - help_text_default: 'Enter the URL of your GitLab instance, e.g. https://example.com:3000. If your GitLab instance is not set up with SSL, start the URL with http:// instead of https://.', - placeholder: t('admin.gitlab.siteUrlExample'), - placeholder_default: 'E.g.: https://', + label: defineMessage({id: 'admin.gitlab.siteUrl', defaultMessage: 'GitLab Site URL:'}), + help_text: defineMessage({id: 'admin.gitlab.siteUrlDescription', defaultMessage: 'Enter the URL of your GitLab instance, e.g. https://example.com:3000. If your GitLab instance is not set up with SSL, start the URL with http:// instead of https://.'}), + placeholder: defineMessage({id: 'admin.gitlab.siteUrlExample', defaultMessage: 'E.g.: https://'}), isHidden: it.not(it.stateEquals('openidType', Constants.GITLAB_SERVICE)), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'GitLabSettings.DiscoveryEndpoint', - label: t('admin.openid.discoveryEndpointTitle'), - label_default: 'Discovery Endpoint:', - help_text: t('admin.gitlab.discoveryEndpointDesc'), - help_text_default: 'The URL of the discovery document for OpenID Connect with GitLab.', + label: defineMessage({id: 'admin.openid.discoveryEndpointTitle', defaultMessage: 'Discovery Endpoint:'}), + help_text: defineMessage({id: 'admin.gitlab.discoveryEndpointDesc', defaultMessage: 'The URL of the discovery document for OpenID Connect with GitLab.'}), help_text_markdown: false, - dynamic_value: (value: any, config: DeepPartial, state: any) => { + dynamic_value: (value, config, state) => { if (state['GitLabSettings.Url']) { return state['GitLabSettings.Url'].replace(/\/$/, '') + '/.well-known/openid-configuration'; } @@ -5719,34 +4821,26 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'GitLabSettings.Id', - label: t('admin.openid.clientIdTitle'), - label_default: 'Client ID:', - help_text: t('admin.openid.clientIdDescription'), - help_text_default: 'Obtaining the Client ID differs across providers. Please check you provider\'s documentation', - placeholder: t('admin.gitlab.clientIdExample'), - placeholder_default: 'E.g.: "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"', + label: defineMessage({id: 'admin.openid.clientIdTitle', defaultMessage: 'Client ID:'}), + help_text: defineMessage({id: 'admin.openid.clientIdDescription', defaultMessage: 'Obtaining the Client ID differs across providers. Please check you provider\'s documentation'}), + placeholder: defineMessage({id: 'admin.gitlab.clientIdExample', defaultMessage: 'E.g.: "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'}), isHidden: it.not(it.stateEquals('openidType', Constants.GITLAB_SERVICE)), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'GitLabSettings.Secret', - label: t('admin.openid.clientSecretTitle'), - label_default: 'Client Secret:', - help_text: t('admin.openid.clientSecretDescription'), - help_text_default: 'Obtaining the Client Secret differs across providers. Please check you provider\'s documentation', - placeholder: t('admin.gitlab.clientSecretExample'), - placeholder_default: 'E.g.: "jcuS8PuvcpGhpgHhlcpT1Mx442pnqMxQY"', + label: defineMessage({id: 'admin.openid.clientSecretTitle', defaultMessage: 'Client Secret:'}), + help_text: defineMessage({id: 'admin.openid.clientSecretDescription', defaultMessage: 'Obtaining the Client Secret differs across providers. Please check you provider\'s documentation'}), + placeholder: defineMessage({id: 'admin.gitlab.clientSecretExample', defaultMessage: 'E.g.: "jcuS8PuvcpGhpgHhlcpT1Mx442pnqMxQY"'}), isHidden: it.not(it.stateEquals('openidType', Constants.GITLAB_SERVICE)), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'GoogleSettings.DiscoveryEndpoint', - label: t('admin.openid.discoveryEndpointTitle'), - label_default: 'Discovery Endpoint:', - help_text: t('admin.google.discoveryEndpointDesc'), - help_text_default: 'The URL of the discovery document for OpenID Connect with Google.', + label: defineMessage({id: 'admin.openid.discoveryEndpointTitle', defaultMessage: 'Discovery Endpoint:'}), + help_text: defineMessage({id: 'admin.google.discoveryEndpointDesc', defaultMessage: 'The URL of the discovery document for OpenID Connect with Google.'}), help_text_markdown: false, dynamic_value: () => 'https://accounts.google.com/.well-known/openid-configuration', isDisabled: true, @@ -5755,48 +4849,37 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'GoogleSettings.Id', - label: t('admin.openid.clientIdTitle'), - label_default: 'Client ID:', - help_text: t('admin.openid.clientIdDescription'), - help_text_default: 'Obtaining the Client ID differs across providers. Please check you provider\'s documentation', - placeholder: t('admin.google.clientIdExample'), - placeholder_default: 'E.g.: "7602141235235-url0fhs1mayfasbmop5qlfns8dh4.apps.googleusercontent.com"', + label: defineMessage({id: 'admin.openid.clientIdTitle', defaultMessage: 'Client ID:'}), + help_text: defineMessage({id: 'admin.openid.clientIdDescription', defaultMessage: 'Obtaining the Client ID differs across providers. Please check you provider\'s documentation'}), + placeholder: defineMessage({id: 'admin.google.clientIdExample', defaultMessage: 'E.g.: "7602141235235-url0fhs1mayfasbmop5qlfns8dh4.apps.googleusercontent.com"'}), isHidden: it.not(it.stateEquals('openidType', Constants.GOOGLE_SERVICE)), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'GoogleSettings.Secret', - label: t('admin.openid.clientSecretTitle'), - label_default: 'Client Secret:', - help_text: t('admin.openid.clientSecretDescription'), - help_text_default: 'Obtaining the Client Secret differs across providers. Please check you provider\'s documentation', - placeholder: t('admin.google.clientSecretExample'), - placeholder_default: 'E.g.: "H8sz0Az-dDs2p15-7QzD231"', + label: defineMessage({id: 'admin.openid.clientSecretTitle', defaultMessage: 'Client Secret:'}), + help_text: defineMessage({id: 'admin.openid.clientSecretDescription', defaultMessage: 'Obtaining the Client Secret differs across providers. Please check you provider\'s documentation'}), + placeholder: defineMessage({id: 'admin.google.clientSecretExample', defaultMessage: 'E.g.: "H8sz0Az-dDs2p15-7QzD231"'}), isHidden: it.not(it.stateEquals('openidType', Constants.GOOGLE_SERVICE)), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'Office365Settings.DirectoryId', - label: t('admin.office365.directoryIdTitle'), - label_default: 'Directory (tenant) ID:', - help_text: t('admin.office365.directoryIdDescription'), - help_text_default: 'The Directory (tenant) ID you received when registering your application with Microsoft.', - placeholder: t('admin.office365.directoryIdExample'), - placeholder_default: 'E.g.: "adf3sfa2-ag3f-sn4n-ids0-sh1hdax192qq"', + label: defineMessage({id: 'admin.office365.directoryIdTitle', defaultMessage: 'Directory (tenant) ID:'}), + help_text: defineMessage({id: 'admin.office365.directoryIdDescription', defaultMessage: 'The Directory (tenant) ID you received when registering your application with Microsoft.'}), + placeholder: defineMessage({id: 'admin.office365.directoryIdExample', defaultMessage: 'E.g.: "adf3sfa2-ag3f-sn4n-ids0-sh1hdax192qq"'}), isHidden: it.not(it.stateEquals('openidType', Constants.OFFICE365_SERVICE)), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'Office365Settings.DiscoveryEndpoint', - label: t('admin.openid.discoveryEndpointTitle'), - label_default: 'Discovery Endpoint:', - help_text: t('admin.office365.discoveryEndpointDesc'), - help_text_default: 'The URL of the discovery document for OpenID Connect with Office 365.', + label: defineMessage({id: 'admin.openid.discoveryEndpointTitle', defaultMessage: 'Discovery Endpoint:'}), + help_text: defineMessage({id: 'admin.office365.discoveryEndpointDesc', defaultMessage: 'The URL of the discovery document for OpenID Connect with Office 365.'}), help_text_markdown: false, - dynamic_value: (value: any, config: DeepPartial, state: any) => { + dynamic_value: (value, config, state) => { if (state['Office365Settings.DirectoryId']) { return 'https://login.microsoftonline.com/' + state['Office365Settings.DirectoryId'] + '/v2.0/.well-known/openid-configuration'; } @@ -5808,24 +4891,18 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'Office365Settings.Id', - label: t('admin.openid.clientIdTitle'), - label_default: 'Client ID:', - help_text: t('admin.openid.clientIdDescription'), - help_text_default: 'Obtaining the Client ID differs across providers. Please check you provider\'s documentation', - placeholder: t('admin.office365.clientIdExample'), - placeholder_default: 'E.g.: "adf3sfa2-ag3f-sn4n-ids0-sh1hdax192qq"', + label: defineMessage({id: 'admin.openid.clientIdTitle', defaultMessage: 'Client ID:'}), + help_text: defineMessage({id: 'admin.openid.clientIdDescription', defaultMessage: 'Obtaining the Client ID differs across providers. Please check you provider\'s documentation'}), + placeholder: defineMessage({id: 'admin.office365.clientIdExample', defaultMessage: 'E.g.: "adf3sfa2-ag3f-sn4n-ids0-sh1hdax192qq"'}), isHidden: it.not(it.stateEquals('openidType', Constants.OFFICE365_SERVICE)), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'Office365Settings.Secret', - label: t('admin.openid.clientSecretTitle'), - label_default: 'Client Secret:', - help_text: t('admin.openid.clientSecretDescription'), - help_text_default: 'Obtaining the Client Secret differs across providers. Please check you provider\'s documentation', - placeholder: t('admin.office365.clientSecretExample'), - placeholder_default: 'E.g.: "shAieM47sNBfgl20f8ci294"', + label: defineMessage({id: 'admin.openid.clientSecretTitle', defaultMessage: 'Client Secret:'}), + help_text: defineMessage({id: 'admin.openid.clientSecretDescription', defaultMessage: 'Obtaining the Client Secret differs across providers. Please check you provider\'s documentation'}), + placeholder: defineMessage({id: 'admin.office365.clientSecretExample', defaultMessage: 'E.g.: "shAieM47sNBfgl20f8ci294"'}), isHidden: it.not(it.stateEquals('openidType', Constants.OFFICE365_SERVICE)), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, @@ -5833,22 +4910,17 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'OpenIdSettings.ButtonText', - label: t('admin.openid.buttonTextTitle'), - label_default: 'Button Name:', - placeholder: t('admin.openid.buttonTextEx'), - placeholder_default: 'Custom Button Name', - help_text: t('admin.openid.buttonTextDesc'), - help_text_default: 'The text that will show on the login button.', + label: defineMessage({id: 'admin.openid.buttonTextTitle', defaultMessage: 'Button Name:'}), + placeholder: defineMessage({id: 'admin.openid.buttonTextEx', defaultMessage: 'Custom Button Name'}), + help_text: defineMessage({id: 'admin.openid.buttonTextDesc', defaultMessage: 'The text that will show on the login button.'}), isHidden: it.any(it.not(it.stateEquals('openidType', Constants.OPENID_SERVICE)), it.licensedForCloudStarter), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'color', key: 'OpenIdSettings.ButtonColor', - label: t('admin.openid.buttonColorTitle'), - label_default: 'Button Color:', - help_text: t('admin.openid.buttonColorDesc'), - help_text_default: 'Specify the color of the OpenID login button for white labeling purposes. Use a hex code with a #-sign before the code.', + label: defineMessage({id: 'admin.openid.buttonColorTitle', defaultMessage: 'Button Color:'}), + help_text: defineMessage({id: 'admin.openid.buttonColorDesc', defaultMessage: 'Specify the color of the OpenID login button for white labeling purposes. Use a hex code with a #-sign before the code.'}), help_text_markdown: false, isHidden: it.any(it.not(it.stateEquals('openidType', Constants.OPENID_SERVICE)), it.licensedForCloudStarter), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), @@ -5856,12 +4928,9 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'OpenIdSettings.DiscoveryEndpoint', - label: t('admin.openid.discoveryEndpointTitle'), - label_default: 'Discovery Endpoint:', - placeholder: t('admin.openid.discovery.placeholder'), - placeholder_default: 'https://id.mydomain.com/.well-known/openid-configuration', - help_text: t('admin.openid.discoveryEndpointDesc'), - help_text_default: 'Enter the URL of the discovery document of the OpenID Connect provider you want to connect with.', + label: defineMessage({id: 'admin.openid.discoveryEndpointTitle', defaultMessage: 'Discovery Endpoint:'}), + placeholder: defineMessage({id: 'admin.openid.discovery.placeholder', defaultMessage: 'https://id.mydomain.com/.well-known/openid-configuration'}), + help_text: defineMessage({id: 'admin.openid.discoveryEndpointDesc', defaultMessage: 'Enter the URL of the discovery document of the OpenID Connect provider you want to connect with.'}), help_text_markdown: false, isHidden: it.any(it.not(it.stateEquals('openidType', Constants.OPENID_SERVICE)), it.licensedForCloudStarter), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), @@ -5869,24 +4938,18 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'OpenIdSettings.Id', - label: t('admin.openid.clientIdTitle'), - label_default: 'Client ID:', - help_text: t('admin.openid.clientIdDescription'), - help_text_default: 'Obtaining the Client ID differs across providers. Please check you provider\'s documentation', - placeholder: t('admin.openid.clientIdExample'), - placeholder_default: 'E.g.: "adf3sfa2-ag3f-sn4n-ids0-sh1hdax192qq"', + label: defineMessage({id: 'admin.openid.clientIdTitle', defaultMessage: 'Client ID:'}), + help_text: defineMessage({id: 'admin.openid.clientIdDescription', defaultMessage: 'Obtaining the Client ID differs across providers. Please check you provider\'s documentation'}), + placeholder: defineMessage({id: 'admin.openid.clientIdExample', defaultMessage: 'E.g.: "adf3sfa2-ag3f-sn4n-ids0-sh1hdax192qq"'}), isHidden: it.any(it.not(it.stateEquals('openidType', Constants.OPENID_SERVICE)), it.licensedForCloudStarter), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, { type: 'text', key: 'OpenIdSettings.Secret', - label: t('admin.openid.clientSecretTitle'), - label_default: 'Client Secret:', - help_text: t('admin.openid.clientSecretDescription'), - help_text_default: 'Obtaining the Client Secret differs across providers. Please check you provider\'s documentation', - placeholder: t('admin.openid.clientSecretExample'), - placeholder_default: 'E.g.: "H8sz0Az-dDs2p15-7QzD231"', + label: defineMessage({id: 'admin.openid.clientSecretTitle', defaultMessage: 'Client Secret:'}), + help_text: defineMessage({id: 'admin.openid.clientSecretDescription', defaultMessage: 'Obtaining the Client Secret differs across providers. Please check you provider\'s documentation'}), + placeholder: defineMessage({id: 'admin.openid.clientSecretExample', defaultMessage: 'E.g.: "H8sz0Az-dDs2p15-7QzD231"'}), isHidden: it.any(it.not(it.stateEquals('openidType', Constants.OPENID_SERVICE)), it.licensedForCloudStarter), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.OPENID)), }, @@ -5904,16 +4967,14 @@ const AdminDefinition: AdminDefinitionType = { openid_feature_discovery: { url: 'authentication/openid', isDiscovery: true, - title: t('admin.sidebar.openid'), - title_default: 'OpenID Connect', + title: defineMessage({id: 'admin.sidebar.openid', defaultMessage: 'OpenID Connect'}), isHidden: it.any( it.any(it.licensedForFeature('OpenId'), it.cloudLicensed), it.not(it.enterpriseReady), ), schema: { id: 'OpenIdSettings', - name: t('admin.authentication.openid'), - name_default: 'OpenID Connect', + name: defineMessage({id: 'admin.authentication.openid', defaultMessage: 'OpenID Connect'}), settings: [ { type: 'custom', @@ -5927,16 +4988,14 @@ const AdminDefinition: AdminDefinitionType = { }, guest_access: { url: 'authentication/guest_access', - title: t('admin.sidebar.guest_access'), - title_default: 'Guest Access', + title: defineMessage({id: 'admin.sidebar.guest_access', defaultMessage: 'Guest Access'}), isHidden: it.any( it.not(it.licensedForFeature('GuestAccounts')), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.GUEST_ACCESS)), ), schema: { id: 'GuestAccountsSettings', - name: t('admin.authentication.guest_access'), - name_default: 'Guest Access', + name: defineMessage({id: 'admin.authentication.guest_access', defaultMessage: 'Guest Access'}), settings: [ { type: 'custom', @@ -5947,32 +5006,25 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'GuestAccountsSettings.HideTags', - label: t('admin.guest_access.hideTags'), - label_default: 'Hide guest tag', - help_text: t('admin.guest_access.hideTagsDescription'), - help_text_default: 'When true, the "guest" tag will not be shown next to the name of all guest users in the Mattermost chat interface.', + label: defineMessage({id: 'admin.guest_access.hideTags', defaultMessage: 'Hide guest tag'}), + help_text: defineMessage({id: 'admin.guest_access.hideTagsDescription', defaultMessage: 'When true, the "guest" tag will not be shown next to the name of all guest users in the Mattermost chat interface.'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.GUEST_ACCESS)), }, { type: 'text', key: 'GuestAccountsSettings.RestrictCreationToDomains', - label: t('admin.guest_access.whitelistedDomainsTitle'), - label_default: 'Whitelisted Guest Domains:', - help_text: t('admin.guest_access.whitelistedDomainsDescription'), - help_text_default: '(Optional) Guest accounts can be created at the system level from this list of allowed guest domains.', + label: defineMessage({id: 'admin.guest_access.whitelistedDomainsTitle', defaultMessage: 'Whitelisted Guest Domains:'}), + help_text: defineMessage({id: 'admin.guest_access.whitelistedDomainsDescription', defaultMessage: '(Optional) Guest accounts can be created at the system level from this list of allowed guest domains.'}), help_text_markdown: true, - placeholder: t('admin.guest_access.whitelistedDomainsExample'), - placeholder_default: 'E.g.: "company.com, othercorp.org"', + placeholder: defineMessage({id: 'admin.guest_access.whitelistedDomainsExample', defaultMessage: 'E.g.: "company.com, othercorp.org"'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.GUEST_ACCESS)), }, { type: 'bool', key: 'GuestAccountsSettings.EnforceMultifactorAuthentication', - label: t('admin.guest_access.mfaTitle'), - label_default: 'Enforce Multi-factor Authentication: ', - help_text: t('admin.guest_access.mfaDescriptionMFANotEnabled'), - help_text_default: '[Multi-factor authentication](./mfa) is currently not enabled.', + label: defineMessage({id: 'admin.guest_access.mfaTitle', defaultMessage: 'Enforce Multi-factor Authentication: '}), + help_text: defineMessage({id: 'admin.guest_access.mfaDescriptionMFANotEnabled', defaultMessage: '[Multi-factor authentication](./mfa) is currently not enabled.'}), help_text_markdown: true, isHidden: it.configIsTrue('ServiceSettings', 'EnableMultifactorAuthentication'), isDisabled: () => true, @@ -5980,10 +5032,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'GuestAccountsSettings.EnforceMultifactorAuthentication', - label: t('admin.guest_access.mfaTitle'), - label_default: 'Enforce Multi-factor Authentication: ', - help_text: t('admin.guest_access.mfaDescriptionMFANotEnforced'), - help_text_default: '[Multi-factor authentication](./mfa) is currently not enforced.', + label: defineMessage({id: 'admin.guest_access.mfaTitle', defaultMessage: 'Enforce Multi-factor Authentication: '}), + help_text: defineMessage({id: 'admin.guest_access.mfaDescriptionMFANotEnforced', defaultMessage: '[Multi-factor authentication](./mfa) is currently not enforced.'}), help_text_markdown: true, isHidden: it.any( it.configIsFalse('ServiceSettings', 'EnableMultifactorAuthentication'), @@ -5994,10 +5044,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'GuestAccountsSettings.EnforceMultifactorAuthentication', - label: t('admin.guest_access.mfaTitle'), - label_default: 'Enforce Multi-factor Authentication: ', - help_text: t('admin.guest_access.mfaDescription'), - help_text_default: 'When true, multi-factor authentication for guests is required for login. New guest users will be required to configure MFA on signup. Logged in guest users without MFA configured are redirected to the MFA setup page until configuration is complete.\n \nIf your system has guest users with login methods other than AD/LDAP and email, MFA must be enforced with the authentication provider outside of Mattermost.', + label: defineMessage({id: 'admin.guest_access.mfaTitle', defaultMessage: 'Enforce Multi-factor Authentication: '}), + help_text: defineMessage({id: 'admin.guest_access.mfaDescription', defaultMessage: 'When true, multi-factor authentication for guests is required for login. New guest users will be required to configure MFA on signup. Logged in guest users without MFA configured are redirected to the MFA setup page until configuration is complete.\n \nIf your system has guest users with login methods other than AD/LDAP and email, MFA must be enforced with the authentication provider outside of Mattermost.'}), help_text_values: { link: (msg: string) => ( ), - sectionTitle: t('admin.sidebar.plugins'), - sectionTitleDefault: 'Plugins', + sectionTitle: defineMessage({id: 'admin.sidebar.plugins', defaultMessage: 'Plugins'}), id: 'plugins', isHidden: it.not(it.userHasReadPermissionOnResource('plugins')), subsections: { plugin_management: { url: 'plugins/plugin_management', - title: t('admin.plugins.pluginManagement'), - title_default: 'Plugin Management', - searchableStrings: [ - 'admin.plugin.management.title', - 'admin.plugins.settings.enable', - 'admin.plugins.settings.enableDesc', - 'admin.plugin.uploadTitle', - 'admin.plugin.installedTitle', - 'admin.plugin.installedDesc', - 'admin.plugin.uploadDesc', - 'admin.plugin.uploadDisabledDesc', - 'admin.plugins.settings.enableMarketplace', - 'admin.plugins.settings.enableMarketplaceDesc', - 'admin.plugins.settings.enableRemoteMarketplace', - 'admin.plugins.settings.enableRemoteMarketplaceDesc', - 'admin.plugins.settings.automaticPrepackagedPlugins', - 'admin.plugins.settings.automaticPrepackagedPluginsDesc', - 'admin.plugins.settings.marketplaceUrl', - 'admin.plugins.settings.marketplaceUrlDesc', - ], + title: defineMessage({id: 'admin.plugins.pluginManagement', defaultMessage: 'Plugin Management'}), + searchableStrings: pluginManagementSearchableStrings, isDisabled: it.not(it.userHasWritePermissionOnResource('plugins')), schema: { id: 'PluginManagementSettings', @@ -6102,30 +5129,25 @@ const AdminDefinition: AdminDefinitionType = { color={'currentColor'} /> ), - sectionTitle: t('admin.sidebar.integrations'), - sectionTitleDefault: 'Integrations', + sectionTitle: defineMessage({id: 'admin.sidebar.integrations', defaultMessage: 'Integrations'}), id: 'integrations', isHidden: it.not(it.userHasReadPermissionOnSomeResources(RESOURCE_KEYS.INTEGRATIONS)), subsections: { integration_management: { url: 'integrations/integration_management', - title: t('admin.integrations.integrationManagement'), - title_default: 'Integration Management', + title: defineMessage({id: 'admin.integrations.integrationManagement', defaultMessage: 'Integration Management'}), isHidden: it.all( it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.INTEGRATIONS.INTEGRATION_MANAGEMENT)), ), schema: { id: 'CustomIntegrationSettings', - name: t('admin.integrations.integrationManagement.title'), - name_default: 'Integration Management', + name: defineMessage({id: 'admin.integrations.integrationManagement.title', defaultMessage: 'Integration Management'}), settings: [ { type: 'bool', key: 'ServiceSettings.EnableIncomingWebhooks', - label: t('admin.service.webhooksTitle'), - label_default: 'Enable Incoming Webhooks: ', - help_text: t('admin.service.webhooksDescription'), - help_text_default: 'When true, incoming webhooks will be allowed. To help combat phishing attacks, all posts from webhooks will be labelled by a BOT tag. See documentation to learn more.', + label: defineMessage({id: 'admin.service.webhooksTitle', defaultMessage: 'Enable Incoming Webhooks: '}), + help_text: defineMessage({id: 'admin.service.webhooksDescription', defaultMessage: 'When true, incoming webhooks will be allowed. To help combat phishing attacks, all posts from webhooks will be labelled by a BOT tag. See documentation to learn more.'}), help_text_values: { link: (msg: string) => ( documentation to learn more.', + label: defineMessage({id: 'admin.service.outWebhooksTitle', defaultMessage: 'Enable Outgoing Webhooks: '}), + help_text: defineMessage({id: 'admin.service.outWebhooksDesc', defaultMessage: 'When true, outgoing webhooks will be allowed. See documentation to learn more.'}), help_text_values: { link: (msg: string) => ( documentation to learn more.', + label: defineMessage({id: 'admin.service.cmdsTitle', defaultMessage: 'Enable Custom Slash Commands: '}), + help_text: defineMessage({id: 'admin.service.cmdsDesc', defaultMessage: 'When true, custom slash commands will be allowed. See documentation to learn more.'}), help_text_values: { link: (msg: string) => ( documentation to learn more.', + label: defineMessage({id: 'admin.oauth.providerTitle', defaultMessage: 'Enable OAuth 2.0 Service Provider: '}), + help_text: defineMessage({id: 'admin.oauth.providerDescription', defaultMessage: 'When true, Mattermost can act as an OAuth 2.0 service provider allowing Mattermost to authorize API requests from external applications. See documentation to learn more.'}), help_text_values: { link: (msg: string) => ( Slash Commands, Outgoing Webhooks, Interactive Messages and Interactive Dialogs.', + label: defineMessage({id: 'admin.service.integrationRequestTitle', defaultMessage: 'Integration request timeout: '}), + help_text: defineMessage({id: 'admin.service.integrationRequestDesc', defaultMessage: 'The number of seconds to wait for Integration requests. That includes Slash Commands, Outgoing Webhooks, Interactive Messages and Interactive Dialogs.'}), help_text_values: { slashCommands: (msg: string) => ( user access tokens for integrations in Account Menu > Account Settings > Security. They can be used to authenticate against the API and give full access to the account.\n\n To manage who can create personal access tokens or to search users by token ID, go to the User Management > Users page.', + label: defineMessage({id: 'admin.service.userAccessTokensTitle', defaultMessage: 'Enable User Access Tokens: '}), + help_text: defineMessage({id: 'admin.service.userAccessTokensDescription', defaultMessage: 'When true, users can create user access tokens for integrations in Account Menu > Account Settings > Security. They can be used to authenticate against the API and give full access to the account.\n\n To manage who can create personal access tokens or to search users by token ID, go to the User Management > Users page.'}), help_text_values: { link: (msg: string) => ( Integrations > Bot Accounts. Bot accounts are similar to user accounts except they cannot be used to log in. See documentation to learn more.', + label: defineMessage({id: 'admin.service.enableBotTitle', defaultMessage: 'Enable Bot Account Creation: '}), + help_text: defineMessage({id: 'admin.service.enableBotAccountCreation', defaultMessage: 'When true, System Admins can create bot accounts for integrations in Integrations > Bot Accounts. Bot accounts are similar to user accounts except they cannot be used to log in. See documentation to learn more.'}), help_text_markdown: false, help_text_values: { siteURL: getSiteURL(), @@ -6330,10 +5334,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'ServiceSettings.DisableBotsWhenOwnerIsDeactivated', - label: t('admin.service.disableBotOwnerDeactivatedTitle'), - label_default: 'Disable bot accounts when owner is deactivated:', - help_text: t('admin.service.disableBotWhenOwnerIsDeactivated'), - help_text_default: 'When a user is deactivated, disables all bot accounts managed by the user. To re-enable bot accounts, go to [Integrations > Bot Accounts]({siteURL}/_redirect/integrations/bots).', + label: defineMessage({id: 'admin.service.disableBotOwnerDeactivatedTitle', defaultMessage: 'Disable bot accounts when owner is deactivated:'}), + help_text: defineMessage({id: 'admin.service.disableBotWhenOwnerIsDeactivated', defaultMessage: 'When a user is deactivated, disables all bot accounts managed by the user. To re-enable bot accounts, go to [Integrations > Bot Accounts]({siteURL}/_redirect/integrations/bots).'}), help_text_markdown: true, help_text_values: {siteURL: getSiteURL()}, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.INTEGRATIONS.BOT_ACCOUNTS)), @@ -6343,23 +5345,19 @@ const AdminDefinition: AdminDefinitionType = { }, gif: { url: 'integrations/gif', - title: t('admin.sidebar.gif'), - title_default: 'GIF (Beta)', + title: defineMessage({id: 'admin.sidebar.gif', defaultMessage: 'GIF (Beta)'}), isHidden: it.all( it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.INTEGRATIONS.GIF)), ), schema: { id: 'GifSettings', - name: t('admin.integrations.gif'), - name_default: 'GIF (Beta)', + name: defineMessage({id: 'admin.integrations.gif', defaultMessage: 'GIF (Beta)'}), settings: [ { type: 'bool', key: 'ServiceSettings.EnableGifPicker', - label: t('admin.customization.enableGifPickerTitle'), - label_default: 'Enable GIF Picker:', - help_text: t('admin.customization.enableGifPickerDesc'), - help_text_default: 'Allows users to select GIFs from the emoji picker.', + label: defineMessage({id: 'admin.customization.enableGifPickerTitle', defaultMessage: 'Enable GIF Picker:'}), + help_text: defineMessage({id: 'admin.customization.enableGifPickerDesc', defaultMessage: 'Allows users to select GIFs from the emoji picker.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.INTEGRATIONS.GIF)), }, ], @@ -6367,55 +5365,43 @@ const AdminDefinition: AdminDefinitionType = { }, cors: { url: 'integrations/cors', - title: t('admin.sidebar.cors'), - title_default: 'CORS', + title: defineMessage({id: 'admin.sidebar.cors', defaultMessage: 'CORS'}), isHidden: it.any( it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.INTEGRATIONS.CORS)), ), schema: { id: 'CORS', - name: t('admin.integrations.cors'), - name_default: 'CORS', + name: defineMessage({id: 'admin.integrations.cors', defaultMessage: 'CORS'}), settings: [ { type: 'text', key: 'ServiceSettings.AllowCorsFrom', - label: t('admin.service.corsTitle'), - label_default: 'Enable cross-origin requests from:', - placeholder: t('admin.service.corsEx'), - placeholder_default: 'http://example.com', - help_text: t('admin.service.corsDescription'), - help_text_default: 'Enable HTTP Cross origin request from a specific domain. Use "*" if you want to allow CORS from any domain or leave it blank to disable it. Should not be set to "*" in production.', + label: defineMessage({id: 'admin.service.corsTitle', defaultMessage: 'Enable cross-origin requests from:'}), + placeholder: defineMessage({id: 'admin.service.corsEx', defaultMessage: 'http://example.com'}), + help_text: defineMessage({id: 'admin.service.corsDescription', defaultMessage: 'Enable HTTP Cross origin request from a specific domain. Use "*" if you want to allow CORS from any domain or leave it blank to disable it. Should not be set to "*" in production.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.INTEGRATIONS.CORS)), }, { type: 'text', key: 'ServiceSettings.CorsExposedHeaders', - label: t('admin.service.corsExposedHeadersTitle'), - label_default: 'CORS Exposed Headers:', - placeholder: t('admin.service.corsHeadersEx'), - placeholder_default: 'X-My-Header', - help_text: t('admin.service.corsExposedHeadersDescription'), - help_text_default: 'Whitelist of headers that will be accessible to the requester.', + label: defineMessage({id: 'admin.service.corsExposedHeadersTitle', defaultMessage: 'CORS Exposed Headers:'}), + placeholder: defineMessage({id: 'admin.service.corsHeadersEx', defaultMessage: 'X-My-Header'}), + help_text: defineMessage({id: 'admin.service.corsExposedHeadersDescription', defaultMessage: 'Whitelist of headers that will be accessible to the requester.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.INTEGRATIONS.CORS)), }, { type: 'bool', key: 'ServiceSettings.CorsAllowCredentials', - label: t('admin.service.corsAllowCredentialsLabel'), - label_default: 'CORS Allow Credentials:', - help_text: t('admin.service.corsAllowCredentialsDescription'), - help_text_default: 'When true, requests that pass validation will include the Access-Control-Allow-Credentials header.', + label: defineMessage({id: 'admin.service.corsAllowCredentialsLabel', defaultMessage: 'CORS Allow Credentials:'}), + help_text: defineMessage({id: 'admin.service.corsAllowCredentialsDescription', defaultMessage: 'When true, requests that pass validation will include the Access-Control-Allow-Credentials header.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.INTEGRATIONS.CORS)), }, { type: 'bool', key: 'ServiceSettings.CorsDebug', - label: t('admin.service.CorsDebugLabel'), - label_default: 'CORS Debug:', - help_text: t('admin.service.corsDebugDescription'), - help_text_default: 'When true, prints messages to the logs to help when developing an integration that uses CORS. These messages will include the structured key value pair "source":"cors".', + label: defineMessage({id: 'admin.service.CorsDebugLabel', defaultMessage: 'CORS Debug:'}), + help_text: defineMessage({id: 'admin.service.corsDebugDescription', defaultMessage: 'When true, prints messages to the logs to help when developing an integration that uses CORS. These messages will include the structured key value pair "source":"cors".'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.INTEGRATIONS.CORS)), }, ], @@ -6430,8 +5416,7 @@ const AdminDefinition: AdminDefinitionType = { color={'currentColor'} /> ), - sectionTitle: t('admin.sidebar.compliance'), - sectionTitleDefault: 'Compliance', + sectionTitle: defineMessage({id: 'admin.sidebar.compliance', defaultMessage: 'Compliance'}), isHidden: it.not(it.userHasReadPermissionOnSomeResources(RESOURCE_KEYS.COMPLIANCE)), subsections: { custom_policy_form_edit: { @@ -6474,19 +5459,10 @@ const AdminDefinition: AdminDefinitionType = { }, data_retention: { url: 'compliance/data_retention_settings', - title: t('admin.sidebar.dataRetentionSettingsPolicies'), - title_default: 'Data Retention Policies', + title: defineMessage({id: 'admin.sidebar.dataRetentionSettingsPolicies', defaultMessage: 'Data Retention Policies'}), searchableStrings: [ - 'admin.data_retention.title', - 'admin.data_retention.createJob.title', - 'admin.data_retention.settings.title', - 'admin.data_retention.globalPolicy.title', - 'admin.data_retention.globalPolicy.subTitle', - 'admin.data_retention.customPolicies.title', - 'admin.data_retention.customPolicies.subTitle', - 'admin.data_retention.jobCreation.title', - 'admin.data_retention.jobCreation.subTitle', - 'admin.data_retention.createJob.instructions', + adminDefinitionMessages.data_retention_title, + ...dataRetentionSearchableStrings, ], isHidden: it.any( it.not(it.licensedForFeature('DataRetention')), @@ -6502,16 +5478,14 @@ const AdminDefinition: AdminDefinitionType = { data_retention_feature_discovery: { url: 'compliance/data_retention', isDiscovery: true, - title: t('admin.sidebar.dataRetentionPolicy'), - title_default: 'Data Retention Policy', + title: defineMessage({id: 'admin.sidebar.dataRetentionPolicy', defaultMessage: 'Data Retention Policy'}), isHidden: it.any( it.licensedForFeature('DataRetention'), it.not(it.enterpriseReady), ), schema: { id: 'DataRetentionSettings', - name: t('admin.data_retention.title'), - name_default: 'Data Retention Policy', + name: adminDefinitionMessages.data_retention_title, settings: [ { type: 'custom', @@ -6525,26 +5499,8 @@ const AdminDefinition: AdminDefinitionType = { }, message_export: { url: 'compliance/export', - title: t('admin.sidebar.complianceExport'), - title_default: 'Compliance Export', - searchableStrings: [ - 'admin.service.complianceExportTitle', - 'admin.service.complianceExportDesc', - 'admin.complianceExport.exportJobStartTime.title', - 'admin.complianceExport.exportJobStartTime.description', - 'admin.complianceExport.exportFormat.title', - ['admin.complianceExport.exportFormat.description', {siteURL: ''}], - 'admin.complianceExport.createJob.title', - 'admin.complianceExport.createJob.help', - 'admin.complianceExport.globalRelayCustomerType.title', - 'admin.complianceExport.globalRelayCustomerType.description', - 'admin.complianceExport.globalRelaySMTPUsername.title', - 'admin.complianceExport.globalRelaySMTPUsername.description', - 'admin.complianceExport.globalRelaySMTPPassword.title', - 'admin.complianceExport.globalRelaySMTPPassword.description', - 'admin.complianceExport.globalRelayEmailAddress.title', - 'admin.complianceExport.globalRelayEmailAddress.description', - ], + title: defineMessage({id: 'admin.sidebar.complianceExport', defaultMessage: 'Compliance Export'}), + searchableStrings: messageExportSearchableStrings, isHidden: it.any( it.not(it.licensedForFeature('MessageExport')), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.COMPLIANCE.COMPLIANCE_EXPORT)), @@ -6559,16 +5515,14 @@ const AdminDefinition: AdminDefinitionType = { compliance_export_feature_discovery: { isDiscovery: true, url: 'compliance/export', - title: t('admin.sidebar.complianceExport'), - title_default: 'Compliance Export', + title: defineMessage({id: 'admin.sidebar.complianceExport', defaultMessage: 'Compliance Export'}), isHidden: it.any( it.licensedForFeature('MessageExport'), it.not(it.enterpriseReady), ), schema: { id: 'MessageExportSettings', - name: t('admin.complianceExport.title'), - name_default: 'Compliance Export', + name: defineMessage({id: 'admin.complianceExport.title', defaultMessage: 'Compliance Export'}), settings: [ { type: 'custom', @@ -6582,29 +5536,23 @@ const AdminDefinition: AdminDefinitionType = { }, audits: { url: 'compliance/monitoring', - title: t('admin.sidebar.complianceMonitoring'), - title_default: 'Compliance Monitoring', + title: defineMessage({id: 'admin.sidebar.complianceMonitoring', defaultMessage: 'Compliance Monitoring'}), isHidden: it.any( it.not(it.licensedForFeature('Compliance')), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.COMPLIANCE.COMPLIANCE_MONITORING)), ), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.COMPLIANCE.COMPLIANCE_MONITORING)), - searchableStrings: [ - 'admin.audits.title', - 'admin.audits.reload', - ], + searchableStrings: auditSearchableStrings, schema: { id: 'Audits', - name: t('admin.compliance.complianceMonitoring'), - name_default: 'Compliance Monitoring', + name: defineMessage({id: 'admin.compliance.complianceMonitoring', defaultMessage: 'Compliance Monitoring'}), component: Audits, isHidden: it.not(it.licensedForFeature('Compliance')), settings: [ { type: 'banner', - label: t('admin.compliance.newComplianceExportBanner'), + label: defineMessage({id: 'admin.compliance.newComplianceExportBanner', defaultMessage: 'This feature is replaced by a new [Compliance Export]({siteURL}/admin_console/compliance/export) feature, and will be removed in a future release. We recommend migrating to the new system.'}), label_markdown: true, - label_default: 'This feature is replaced by a new [Compliance Export]({siteURL}/admin_console/compliance/export) feature, and will be removed in a future release. We recommend migrating to the new system.', label_values: {siteURL: getSiteURL()}, banner_type: 'info', isHidden: it.not(it.licensedForFeature('Compliance')), @@ -6612,10 +5560,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'ComplianceSettings.Enable', - label: t('admin.compliance.enableTitle'), - label_default: 'Enable Compliance Reporting:', - help_text: t('admin.compliance.enableDesc'), - help_text_default: 'When true, Mattermost allows compliance reporting from the Compliance and Auditing tab. See documentation to learn more.', + label: defineMessage({id: 'admin.compliance.enableTitle', defaultMessage: 'Enable Compliance Reporting:'}), + help_text: defineMessage({id: 'admin.compliance.enableDesc', defaultMessage: 'When true, Mattermost allows compliance reporting from the Compliance and Auditing tab. See documentation to learn more.'}), help_text_values: { link: (msg: string) => ( ), - sectionTitle: t('admin.sidebar.experimental'), - sectionTitleDefault: 'Experimental', + sectionTitle: defineMessage({id: 'admin.sidebar.experimental', defaultMessage: 'Experimental'}), isHidden: it.not(it.userHasReadPermissionOnSomeResources(RESOURCE_KEYS.EXPERIMENTAL)), subsections: { experimental_features: { url: 'experimental/features', - title: t('admin.sidebar.experimentalFeatures'), - title_default: 'Features', + title: defineMessage({id: 'admin.sidebar.experimentalFeatures', defaultMessage: 'Features'}), isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), schema: { id: 'ExperimentalSettings', - name: t('admin.experimental.experimentalFeatures'), - name_default: 'Experimental Features', + name: defineMessage({id: 'admin.experimental.experimentalFeatures', defaultMessage: 'Experimental Features'}), settings: [ { type: 'color', key: 'LdapSettings.LoginButtonColor', - label: t('admin.experimental.ldapSettingsLoginButtonColor.title'), - label_default: 'AD/LDAP Login Button Color:', - help_text: t('admin.experimental.ldapSettingsLoginButtonColor.desc'), - help_text_default: 'Specify the color of the AD/LDAP login button for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.', + label: defineMessage({id: 'admin.experimental.ldapSettingsLoginButtonColor.title', defaultMessage: 'AD/LDAP Login Button Color:'}), + help_text: defineMessage({id: 'admin.experimental.ldapSettingsLoginButtonColor.desc', defaultMessage: 'Specify the color of the AD/LDAP login button for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.'}), help_text_markdown: false, isHidden: it.not(it.licensedForFeature('LDAP')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), @@ -6747,10 +5672,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'color', key: 'LdapSettings.LoginButtonBorderColor', - label: t('admin.experimental.ldapSettingsLoginButtonBorderColor.title'), - label_default: 'AD/LDAP Login Button Border Color:', - help_text: t('admin.experimental.ldapSettingsLoginButtonBorderColor.desc'), - help_text_default: 'Specify the color of the AD/LDAP login button border for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.', + label: defineMessage({id: 'admin.experimental.ldapSettingsLoginButtonBorderColor.title', defaultMessage: 'AD/LDAP Login Button Border Color:'}), + help_text: defineMessage({id: 'admin.experimental.ldapSettingsLoginButtonBorderColor.desc', defaultMessage: 'Specify the color of the AD/LDAP login button border for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.'}), help_text_markdown: false, isHidden: it.not(it.licensedForFeature('LDAP')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), @@ -6758,10 +5681,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'color', key: 'LdapSettings.LoginButtonTextColor', - label: t('admin.experimental.ldapSettingsLoginButtonTextColor.title'), - label_default: 'AD/LDAP Login Button Text Color:', - help_text: t('admin.experimental.ldapSettingsLoginButtonTextColor.desc'), - help_text_default: 'Specify the color of the AD/LDAP login button text for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.', + label: defineMessage({id: 'admin.experimental.ldapSettingsLoginButtonTextColor.title', defaultMessage: 'AD/LDAP Login Button Text Color:'}), + help_text: defineMessage({id: 'admin.experimental.ldapSettingsLoginButtonTextColor.desc', defaultMessage: 'Specify the color of the AD/LDAP login button text for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.'}), help_text_markdown: false, isHidden: it.not(it.licensedForFeature('LDAP')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), @@ -6769,10 +5690,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'ServiceSettings.ExperimentalEnableAuthenticationTransfer', - label: t('admin.experimental.experimentalEnableAuthenticationTransfer.title'), - label_default: 'Allow Authentication Transfer:', - help_text: t('admin.experimental.experimentalEnableAuthenticationTransfer.desc'), - help_text_default: 'When true, users can change their sign-in method to any that is enabled on the server, any via Account Settings or the APIs. When false, Users cannot change their sign-in method, regardless of which authentication options are enabled.', + label: defineMessage({id: 'admin.experimental.experimentalEnableAuthenticationTransfer.title', defaultMessage: 'Allow Authentication Transfer:'}), + help_text: defineMessage({id: 'admin.experimental.experimentalEnableAuthenticationTransfer.desc', defaultMessage: 'When true, users can change their sign-in method to any that is enabled on the server, any via Account Settings or the APIs. When false, Users cannot change their sign-in method, regardless of which authentication options are enabled.'}), help_text_markdown: false, isHidden: it.any( // documented as E20 and higher, but only E10 in the code it.not(it.licensed), @@ -6783,106 +5702,83 @@ const AdminDefinition: AdminDefinitionType = { { type: 'number', key: 'ExperimentalSettings.LinkMetadataTimeoutMilliseconds', - label: t('admin.experimental.linkMetadataTimeoutMilliseconds.title'), - label_default: 'Link Metadata Timeout:', - help_text: t('admin.experimental.linkMetadataTimeoutMilliseconds.desc'), - help_text_default: 'The number of milliseconds to wait for metadata from a third-party link. Used with Post Metadata.', + label: defineMessage({id: 'admin.experimental.linkMetadataTimeoutMilliseconds.title', defaultMessage: 'Link Metadata Timeout:'}), + help_text: defineMessage({id: 'admin.experimental.linkMetadataTimeoutMilliseconds.desc', defaultMessage: 'The number of milliseconds to wait for metadata from a third-party link. Used with Post Metadata.'}), help_text_markdown: false, - placeholder: t('admin.experimental.linkMetadataTimeoutMilliseconds.example'), - placeholder_default: 'E.g.: "5000"', + placeholder: defineMessage({id: 'admin.experimental.linkMetadataTimeoutMilliseconds.example', defaultMessage: 'E.g.: "5000"'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'number', key: 'EmailSettings.EmailBatchingBufferSize', - label: t('admin.experimental.emailBatchingBufferSize.title'), - label_default: 'Email Batching Buffer Size:', - help_text: t('admin.experimental.emailBatchingBufferSize.desc'), - help_text_default: 'Specify the maximum number of notifications batched into a single email.', + label: defineMessage({id: 'admin.experimental.emailBatchingBufferSize.title', defaultMessage: 'Email Batching Buffer Size:'}), + help_text: defineMessage({id: 'admin.experimental.emailBatchingBufferSize.desc', defaultMessage: 'Specify the maximum number of notifications batched into a single email.'}), help_text_markdown: false, - placeholder: t('admin.experimental.emailBatchingBufferSize.example'), - placeholder_default: 'E.g.: "256"', + placeholder: defineMessage({id: 'admin.experimental.emailBatchingBufferSize.example', defaultMessage: 'E.g.: "256"'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'number', key: 'EmailSettings.EmailBatchingInterval', - label: t('admin.experimental.emailBatchingInterval.title'), - label_default: 'Email Batching Interval:', - help_text: t('admin.experimental.emailBatchingInterval.desc'), - help_text_default: 'Specify the maximum frequency, in seconds, which the batching job checks for new notifications. Longer batching intervals will increase performance.', + label: defineMessage({id: 'admin.experimental.emailBatchingInterval.title', defaultMessage: 'Email Batching Interval:'}), + help_text: defineMessage({id: 'admin.experimental.emailBatchingInterval.desc', defaultMessage: 'Specify the maximum frequency, in seconds, which the batching job checks for new notifications. Longer batching intervals will increase performance.'}), help_text_markdown: false, - placeholder: t('admin.experimental.emailBatchingInterval.example'), - placeholder_default: 'E.g.: "30"', + placeholder: defineMessage({id: 'admin.experimental.emailBatchingInterval.example', defaultMessage: 'E.g.: "30"'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'color', key: 'EmailSettings.LoginButtonColor', - label: t('admin.experimental.emailSettingsLoginButtonColor.title'), - label_default: 'Email Login Button Color:', - help_text: t('admin.experimental.emailSettingsLoginButtonColor.desc'), - help_text_default: 'Specify the color of the email login button for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.', + label: defineMessage({id: 'admin.experimental.emailSettingsLoginButtonColor.title', defaultMessage: 'Email Login Button Color:'}), + help_text: defineMessage({id: 'admin.experimental.emailSettingsLoginButtonColor.desc', defaultMessage: 'Specify the color of the email login button for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'color', key: 'EmailSettings.LoginButtonBorderColor', - label: t('admin.experimental.emailSettingsLoginButtonBorderColor.title'), - label_default: 'Email Login Button Border Color:', - help_text: t('admin.experimental.emailSettingsLoginButtonBorderColor.desc'), - help_text_default: 'Specify the color of the email login button border for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.', + label: defineMessage({id: 'admin.experimental.emailSettingsLoginButtonBorderColor.title', defaultMessage: 'Email Login Button Border Color:'}), + help_text: defineMessage({id: 'admin.experimental.emailSettingsLoginButtonBorderColor.desc', defaultMessage: 'Specify the color of the email login button border for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'color', key: 'EmailSettings.LoginButtonTextColor', - label: t('admin.experimental.emailSettingsLoginButtonTextColor.title'), - label_default: 'Email Login Button Text Color:', - help_text: t('admin.experimental.emailSettingsLoginButtonTextColor.desc'), - help_text_default: 'Specify the color of the email login button text for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.', + label: defineMessage({id: 'admin.experimental.emailSettingsLoginButtonTextColor.title', defaultMessage: 'Email Login Button Text Color:'}), + help_text: defineMessage({id: 'admin.experimental.emailSettingsLoginButtonTextColor.desc', defaultMessage: 'Specify the color of the email login button text for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'bool', key: 'TeamSettings.EnableUserDeactivation', - label: t('admin.experimental.enableUserDeactivation.title'), - label_default: 'Enable Account Deactivation:', - help_text: t('admin.experimental.enableUserDeactivation.desc'), - help_text_default: 'When true, users may deactivate their own account from **Settings > Advanced**. If a user deactivates their own account, they will get an email notification confirming they were deactivated. When false, users may not deactivate their own account.', + label: defineMessage({id: 'admin.experimental.enableUserDeactivation.title', defaultMessage: 'Enable Account Deactivation:'}), + help_text: defineMessage({id: 'admin.experimental.enableUserDeactivation.desc', defaultMessage: 'When true, users may deactivate their own account from **Settings > Advanced**. If a user deactivates their own account, they will get an email notification confirming they were deactivated. When false, users may not deactivate their own account.'}), help_text_markdown: true, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'bool', key: 'TeamSettings.ExperimentalEnableAutomaticReplies', - label: t('admin.experimental.experimentalEnableAutomaticReplies.title'), - label_default: 'Enable Automatic Replies:', - help_text: t('admin.experimental.experimentalEnableAutomaticReplies.desc'), - help_text_default: 'When true, users can enable Automatic Replies in **Settings > Notifications**. Users set a custom message that will be automatically sent in response to Direct Messages. When false, disables the Automatic Direct Message Replies feature and hides it from Settings.', + label: defineMessage({id: 'admin.experimental.experimentalEnableAutomaticReplies.title', defaultMessage: 'Enable Automatic Replies:'}), + help_text: defineMessage({id: 'admin.experimental.experimentalEnableAutomaticReplies.desc', defaultMessage: 'When true, users can enable Automatic Replies in **Settings > Notifications**. Users set a custom message that will be automatically sent in response to Direct Messages. When false, disables the Automatic Direct Message Replies feature and hides it from Settings.'}), help_text_markdown: true, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'bool', key: 'ServiceSettings.EnableChannelViewedMessages', - label: t('admin.experimental.enableChannelViewedMessages.title'), - label_default: 'Enable Channel Viewed WebSocket Messages:', - help_text: t('admin.experimental.enableChannelViewedMessages.desc'), - help_text_default: 'This setting determines whether `channel_viewed` WebSocket events are sent, which synchronize unread notifications across clients and devices. Disabling the setting in larger deployments may improve server performance.', + label: defineMessage({id: 'admin.experimental.enableChannelViewedMessages.title', defaultMessage: 'Enable Channel Viewed WebSocket Messages:'}), + help_text: defineMessage({id: 'admin.experimental.enableChannelViewedMessages.desc', defaultMessage: 'This setting determines whether `channel_viewed` WebSocket events are sent, which synchronize unread notifications across clients and devices. Disabling the setting in larger deployments may improve server performance.'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'bool', key: 'ExperimentalSettings.ClientSideCertEnable', - label: t('admin.experimental.clientSideCertEnable.title'), - label_default: 'Enable Client-Side Certification:', - help_text: t('admin.experimental.clientSideCertEnable.desc'), - help_text_default: 'Enables client-side certification for your Mattermost server. See documentation to learn more.', + label: defineMessage({id: 'admin.experimental.clientSideCertEnable.title', defaultMessage: 'Enable Client-Side Certification:'}), + help_text: defineMessage({id: 'admin.experimental.clientSideCertEnable.desc', defaultMessage: 'Enables client-side certification for your Mattermost server. See documentation to learn more.'}), help_text_values: { link: (msg: string) => ( documentation to learn more.', + label: defineMessage({id: 'admin.experimental.experimentalEnableHardenedMode.title', defaultMessage: 'Enable Hardened Mode:'}), + help_text: defineMessage({id: 'admin.experimental.experimentalEnableHardenedMode.desc', defaultMessage: 'Enables a hardened mode for Mattermost that makes user experience trade-offs in the interest of security. See documentation to learn more.'}), help_text_values: { link: (msg: string) => ( Advanced > Preview pre-release features**. When false, disables and hides preview features from **Settings > Advanced > Preview pre-release features**.', + label: defineMessage({id: 'admin.experimental.enablePreviewFeatures.title', defaultMessage: 'Enable Preview Features:'}), + help_text: defineMessage({id: 'admin.experimental.enablePreviewFeatures.desc', defaultMessage: 'When true, preview features can be enabled from **Settings > Advanced > Preview pre-release features**. When false, disables and hides preview features from **Settings > Advanced > Preview pre-release features**.'}), help_text_markdown: true, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'bool', key: 'ThemeSettings.EnableThemeSelection', - label: t('admin.experimental.enableThemeSelection.title'), - label_default: 'Enable Theme Selection:', - help_text: t('admin.experimental.enableThemeSelection.desc'), - help_text_default: 'Enables the **Display > Theme** tab in Settings so users can select their theme.', + label: defineMessage({id: 'admin.experimental.enableThemeSelection.title', defaultMessage: 'Enable Theme Selection:'}), + help_text: defineMessage({id: 'admin.experimental.enableThemeSelection.desc', defaultMessage: 'Enables the **Display > Theme** tab in Settings so users can select their theme.'}), help_text_markdown: true, isHidden: it.any( it.not(it.licensed), @@ -6984,10 +5868,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'ThemeSettings.AllowCustomThemes', - label: t('admin.experimental.allowCustomThemes.title'), - label_default: 'Allow Custom Themes:', - help_text: t('admin.experimental.allowCustomThemes.desc'), - help_text_default: 'Enables the **Display > Theme > Custom Theme** section in Settings.', + label: defineMessage({id: 'admin.experimental.allowCustomThemes.title', defaultMessage: 'Allow Custom Themes:'}), + help_text: defineMessage({id: 'admin.experimental.allowCustomThemes.desc', defaultMessage: 'Enables the **Display > Theme > Custom Theme** section in Settings.'}), help_text_markdown: true, isHidden: it.any( it.not(it.licensed), @@ -7001,36 +5883,29 @@ const AdminDefinition: AdminDefinitionType = { { type: 'dropdown', key: 'ThemeSettings.DefaultTheme', - label: t('admin.experimental.defaultTheme.title'), - label_default: 'Default Theme:', - help_text: t('admin.experimental.defaultTheme.desc'), - help_text_default: 'Set a default theme that applies to all new users on the system.', + label: defineMessage({id: 'admin.experimental.defaultTheme.title', defaultMessage: 'Default Theme:'}), + help_text: defineMessage({id: 'admin.experimental.defaultTheme.desc', defaultMessage: 'Set a default theme that applies to all new users on the system.'}), help_text_markdown: true, options: [ { value: 'denim', - display_name: 'Denim', - display_name_default: 'Denim', + display_name: defineMessage({id: 'admin.experimental.defaultTheme.options.denim', defaultMessage: 'Denim'}), }, { value: 'sapphire', - display_name: 'Sapphire', - display_name_default: 'Sapphire', + display_name: defineMessage({id: 'admin.experimental.defaultTheme.options.sapphire', defaultMessage: 'Sapphire'}), }, { value: 'quartz', - display_name: 'Quartz', - display_name_default: 'Quartz', + display_name: defineMessage({id: 'admin.experimental.defaultTheme.options.quartz', defaultMessage: 'Quartz'}), }, { value: 'indigo', - display_name: 'Indigo', - display_name_default: 'Indigo', + display_name: defineMessage({id: 'admin.experimental.defaultTheme.options.indigo', defaultMessage: 'Indigo'}), }, { value: 'onyx', - display_name: 'Onyx', - display_name_default: 'Onyx', + display_name: defineMessage({id: 'admin.experimental.defaultTheme.options.onyx', defaultMessage: 'Onyx'}), }, ], isHidden: it.any( @@ -7042,43 +5917,34 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'ServiceSettings.EnableTutorial', - label: t('admin.experimental.enableTutorial.title'), - label_default: 'Enable Tutorial:', - help_text: t('admin.experimental.enableTutorial.desc'), - help_text_default: 'When true, users are prompted with a tutorial when they open Mattermost for the first time after account creation. When false, the tutorial is disabled, and users are placed in Town Square when they open Mattermost for the first time after account creation.', + label: defineMessage({id: 'admin.experimental.enableTutorial.title', defaultMessage: 'Enable Tutorial:'}), + help_text: defineMessage({id: 'admin.experimental.enableTutorial.desc', defaultMessage: 'When true, users are prompted with a tutorial when they open Mattermost for the first time after account creation. When false, the tutorial is disabled, and users are placed in Town Square when they open Mattermost for the first time after account creation.'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'bool', key: 'ServiceSettings.EnableOnboardingFlow', - label: t('admin.experimental.enableOnboardingFlow.title'), - label_default: 'Enable Onboarding:', - help_text: t('admin.experimental.enableOnboardingFlow.desc'), - help_text_default: 'When true, new users are shown steps to complete as part of an onboarding process', + label: defineMessage({id: 'admin.experimental.enableOnboardingFlow.title', defaultMessage: 'Enable Onboarding:'}), + help_text: defineMessage({id: 'admin.experimental.enableOnboardingFlow.desc', defaultMessage: 'When true, new users are shown steps to complete as part of an onboarding process'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'bool', key: 'ServiceSettings.EnableUserTypingMessages', - label: t('admin.experimental.enableUserTypingMessages.title'), - label_default: 'Enable User Typing Messages:', - help_text: t('admin.experimental.enableUserTypingMessages.desc'), - help_text_default: 'This setting determines whether "user is typing..." messages are displayed below the message box. Disabling the setting in larger deployments may improve server performance.', + label: defineMessage({id: 'admin.experimental.enableUserTypingMessages.title', defaultMessage: 'Enable User Typing Messages:'}), + help_text: defineMessage({id: 'admin.experimental.enableUserTypingMessages.desc', defaultMessage: 'This setting determines whether "user is typing..." messages are displayed below the message box. Disabling the setting in larger deployments may improve server performance.'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'number', key: 'ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds', - label: t('admin.experimental.timeBetweenUserTypingUpdatesMilliseconds.title'), - label_default: 'User Typing Timeout:', - help_text: t('admin.experimental.timeBetweenUserTypingUpdatesMilliseconds.desc'), - help_text_default: 'The number of milliseconds to wait between emitting user typing websocket events.', + label: defineMessage({id: 'admin.experimental.timeBetweenUserTypingUpdatesMilliseconds.title', defaultMessage: 'User Typing Timeout:'}), + help_text: defineMessage({id: 'admin.experimental.timeBetweenUserTypingUpdatesMilliseconds.desc', defaultMessage: 'The number of milliseconds to wait between emitting user typing websocket events.'}), help_text_markdown: false, - placeholder: t('admin.experimental.timeBetweenUserTypingUpdatesMilliseconds.example'), - placeholder_default: 'E.g.: "5000"', + placeholder: defineMessage({id: 'admin.experimental.timeBetweenUserTypingUpdatesMilliseconds.example', defaultMessage: 'E.g.: "5000"'}), isDisabled: it.any( it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), it.stateIsFalse('ServiceSettings.EnableUserTypingMessages'), @@ -7087,22 +5953,17 @@ const AdminDefinition: AdminDefinitionType = { { type: 'text', key: 'TeamSettings.ExperimentalPrimaryTeam', - label: t('admin.experimental.experimentalPrimaryTeam.title'), - label_default: 'Primary Team:', - help_text: t('admin.experimental.experimentalPrimaryTeam.desc'), - help_text_default: 'The primary team of which users on the server are members. When a primary team is set, the options to join other teams or leave the primary team are disabled.', + label: defineMessage({id: 'admin.experimental.experimentalPrimaryTeam.title', defaultMessage: 'Primary Team:'}), + help_text: defineMessage({id: 'admin.experimental.experimentalPrimaryTeam.desc', defaultMessage: 'The primary team of which users on the server are members. When a primary team is set, the options to join other teams or leave the primary team are disabled.'}), help_text_markdown: true, - placeholder: t('admin.experimental.experimentalPrimaryTeam.example'), - placeholder_default: 'E.g.: "teamname"', + placeholder: defineMessage({id: 'admin.experimental.experimentalPrimaryTeam.example', defaultMessage: 'E.g.: "teamname"'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'color', key: 'SamlSettings.LoginButtonColor', - label: t('admin.experimental.samlSettingsLoginButtonColor.title'), - label_default: 'SAML Login Button Color:', - help_text: t('admin.experimental.samlSettingsLoginButtonColor.desc'), - help_text_default: 'Specify the color of the SAML login button for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.', + label: defineMessage({id: 'admin.experimental.samlSettingsLoginButtonColor.title', defaultMessage: 'SAML Login Button Color:'}), + help_text: defineMessage({id: 'admin.experimental.samlSettingsLoginButtonColor.desc', defaultMessage: 'Specify the color of the SAML login button for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.'}), help_text_markdown: false, isHidden: it.not(it.licensedForFeature('SAML')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), @@ -7110,10 +5971,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'color', key: 'SamlSettings.LoginButtonBorderColor', - label: t('admin.experimental.samlSettingsLoginButtonBorderColor.title'), - label_default: 'SAML Login Button Border Color:', - help_text: t('admin.experimental.samlSettingsLoginButtonBorderColor.desc'), - help_text_default: 'Specify the color of the SAML login button border for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.', + label: defineMessage({id: 'admin.experimental.samlSettingsLoginButtonBorderColor.title', defaultMessage: 'SAML Login Button Border Color:'}), + help_text: defineMessage({id: 'admin.experimental.samlSettingsLoginButtonBorderColor.desc', defaultMessage: 'Specify the color of the SAML login button border for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.'}), help_text_markdown: false, isHidden: it.not(it.licensedForFeature('SAML')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), @@ -7121,10 +5980,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'color', key: 'SamlSettings.LoginButtonTextColor', - label: t('admin.experimental.samlSettingsLoginButtonTextColor.title'), - label_default: 'SAML Login Button Text Color:', - help_text: t('admin.experimental.samlSettingsLoginButtonTextColor.desc'), - help_text_default: 'Specify the color of the SAML login button text for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.', + label: defineMessage({id: 'admin.experimental.samlSettingsLoginButtonTextColor.title', defaultMessage: 'SAML Login Button Text Color:'}), + help_text: defineMessage({id: 'admin.experimental.samlSettingsLoginButtonTextColor.desc', defaultMessage: 'Specify the color of the SAML login button text for white labeling purposes. Use a hex code with a #-sign before the code. This setting only applies to the mobile apps.'}), help_text_markdown: false, isHidden: it.not(it.licensedForFeature('SAML')), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), @@ -7132,32 +5989,25 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'EmailSettings.UseChannelInEmailNotifications', - label: t('admin.experimental.useChannelInEmailNotifications.title'), - label_default: 'Use Channel Name in Email Notifications:', - help_text: t('admin.experimental.useChannelInEmailNotifications.desc'), - help_text_default: 'When true, channel and team name appears in email notification subject lines. Useful for servers using only one team. When false, only team name appears in email notification subject line.', + label: defineMessage({id: 'admin.experimental.useChannelInEmailNotifications.title', defaultMessage: 'Use Channel Name in Email Notifications:'}), + help_text: defineMessage({id: 'admin.experimental.useChannelInEmailNotifications.desc', defaultMessage: 'When true, channel and team name appears in email notification subject lines. Useful for servers using only one team. When false, only team name appears in email notification subject line.'}), help_text_markdown: false, isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'number', key: 'TeamSettings.UserStatusAwayTimeout', - label: t('admin.experimental.userStatusAwayTimeout.title'), - label_default: 'User Status Away Timeout:', - help_text: t('admin.experimental.userStatusAwayTimeout.desc'), - help_text_default: 'This setting defines the number of seconds after which the user’s status indicator changes to "Away", when they are away from Mattermost.', + label: defineMessage({id: 'admin.experimental.userStatusAwayTimeout.title', defaultMessage: 'User Status Away Timeout:'}), + help_text: defineMessage({id: 'admin.experimental.userStatusAwayTimeout.desc', defaultMessage: 'This setting defines the number of seconds after which the user’s status indicator changes to "Away", when they are away from Mattermost.'}), help_text_markdown: false, - placeholder: t('admin.experimental.userStatusAwayTimeout.example'), - placeholder_default: 'E.g.: "300"', + placeholder: defineMessage({id: 'admin.experimental.userStatusAwayTimeout.example', defaultMessage: 'E.g.: "300"'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'bool', key: 'ExperimentalSettings.EnableSharedChannels', - label: t('admin.experimental.enableSharedChannels.title'), - label_default: 'Enable Shared Channels:', - help_text: t('admin.experimental.enableSharedChannels.desc'), - help_text_default: 'Toggles Shared Channels', + label: defineMessage({id: 'admin.experimental.enableSharedChannels.title', defaultMessage: 'Enable Shared Channels:'}), + help_text: defineMessage({id: 'admin.experimental.enableSharedChannels.desc', defaultMessage: 'Toggles Shared Channels'}), help_text_markdown: false, isHidden: it.not(it.any( it.licensedForFeature('SharedChannels'), @@ -7169,10 +6019,8 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'ExperimentalSettings.DisableAppBar', - label: t('admin.experimental.disableAppBar.title'), - label_default: 'Disable Apps Bar:', - help_text: t('admin.experimental.disableAppBar.desc'), - help_text_default: 'When false, all integrations move from the channel header to the Apps Bar. Channel header plugin icons that haven\'t explicitly registered an Apps Bar icon will be moved to the Apps Bar which may result in rendering issues.', + label: defineMessage({id: 'admin.experimental.disableAppBar.title', defaultMessage: 'Disable Apps Bar:'}), + help_text: defineMessage({id: 'admin.experimental.disableAppBar.desc', defaultMessage: 'When false, all integrations move from the channel header to the Apps Bar. Channel header plugin icons that haven\'t explicitly registered an Apps Bar icon will be moved to the Apps Bar which may result in rendering issues.'}), help_text_markdown: true, isHidden: it.licensedForFeature('Cloud'), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), @@ -7180,19 +6028,15 @@ const AdminDefinition: AdminDefinitionType = { { type: 'bool', key: 'ExperimentalSettings.DisableRefetchingOnBrowserFocus', - label: t('admin.experimental.disableRefetchingOnBrowserFocus.title'), - label_default: 'Disable data refetching on browser refocus:', - help_text: t('admin.experimental.disableRefetchingOnBrowserFocus.desc'), - help_text_default: 'When true, Mattermost will not refetch channels and channel members when the browser regains focus. This may result in improved performance for users with many channels and channel members.', + label: defineMessage({id: 'admin.experimental.disableRefetchingOnBrowserFocus.title', defaultMessage: 'Disable data refetching on browser refocus:'}), + help_text: defineMessage({id: 'admin.experimental.disableRefetchingOnBrowserFocus.desc', defaultMessage: 'When true, Mattermost will not refetch channels and channel members when the browser regains focus. This may result in improved performance for users with many channels and channel members.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, { type: 'bool', key: 'ExperimentalSettings.DelayChannelAutocomplete', - label: t('admin.experimental.delayChannelAutocomplete.title'), - label_default: 'Delay Channel Autocomplete:', - help_text: t('admin.experimental.delayChannelAutocomplete.desc'), - help_text_default: 'When true, the autocomplete for channel links (such as ~town-square) will only trigger after typing a tilde followed by a couple letters. When false, the autocomplete will appear as soon as the user types a tilde.', + label: defineMessage({id: 'admin.experimental.delayChannelAutocomplete.title', defaultMessage: 'Delay Channel Autocomplete:'}), + help_text: defineMessage({id: 'admin.experimental.delayChannelAutocomplete.desc', defaultMessage: 'When true, the autocomplete for channel links (such as ~town-square) will only trigger after typing a tilde followed by a couple letters. When false, the autocomplete will appear as soon as the user types a tilde.'}), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), }, ], @@ -7200,14 +6044,13 @@ const AdminDefinition: AdminDefinitionType = { }, feature_flags: { url: 'experimental/feature_flags', - title: t('admin.feature_flags.title'), - title_default: 'Feature Flags', + title: featureFlagsMessages.title, isHidden: it.any( it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURE_FLAGS)), ), isDisabled: true, searchableStrings: [ - 'admin.feature_flags.title', + featureFlagsMessages.title, ], schema: { id: 'Feature Flags', @@ -7216,26 +6059,13 @@ const AdminDefinition: AdminDefinitionType = { }, bleve: { url: 'experimental/blevesearch', - title: t('admin.sidebar.blevesearch'), - title_default: 'Bleve', + title: defineMessage({id: 'admin.sidebar.blevesearch', defaultMessage: 'Bleve'}), isHidden: it.any( it.configIsTrue('ExperimentalSettings', 'RestrictSystemAdmin'), it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.BLEVE)), ), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.BLEVE)), - searchableStrings: [ - 'admin.bleve.title', - 'admin.bleve.enableIndexingTitle', - ['admin.bleve.enableIndexingDescription', {documentationLink: ''}], - 'admin.bleve.enableIndexingDescription.documentationLinkText', - 'admin.bleve.bulkIndexingTitle', - 'admin.bleve.createJob.help', - 'admin.bleve.purgeIndexesHelpText', - 'admin.bleve.purgeIndexesButton', - 'admin.bleve.purgeIndexesButton.label', - 'admin.bleve.enableSearchingTitle', - 'admin.bleve.enableSearchingDescription', - ], + searchableStrings: bleveSearchableStrings, schema: { id: 'BleveSettings', component: BleveSettings, @@ -7245,26 +6075,4 @@ const AdminDefinition: AdminDefinitionType = { }, }; -t('admin.field_names.allowBannerDismissal'); -t('admin.field_names.bannerColor'); -t('admin.field_names.bannerText'); -t('admin.field_names.bannerTextColor'); -t('admin.field_names.enableBanner'); -t('admin.field_names.enableCommands'); -t('admin.field_names.enableConfirmNotificationsToChannel'); -t('admin.field_names.enableIncomingWebhooks'); -t('admin.field_names.enableOAuthServiceProvider'); -t('admin.field_names.enableOutgoingWebhooks'); -t('admin.field_names.enablePostIconOverride'); -t('admin.field_names.enablePostUsernameOverride'); -t('admin.field_names.enableUserAccessTokens'); -t('admin.field_names.enableUserCreation'); -t('admin.field_names.maxChannelsPerTeam'); -t('admin.field_names.maxNotificationsPerChannel'); -t('admin.field_names.maxUsersPerTeam'); -t('admin.field_names.postEditTimeLimit'); -t('admin.field_names.restrictCreationToDomains'); -t('admin.field_names.restrictDirectMessage'); -t('admin.field_names.teammateNameDisplay'); - export default AdminDefinition; diff --git a/webapp/channels/src/components/admin_console/admin_definition_constants.tsx b/webapp/channels/src/components/admin_console/admin_definition_constants.tsx index 08e21b00d5..816b0c37a4 100644 --- a/webapp/channels/src/components/admin_console/admin_definition_constants.tsx +++ b/webapp/channels/src/components/admin_console/admin_definition_constants.tsx @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import React from 'react'; -import {FormattedMessage} from 'react-intl'; +import {FormattedMessage, defineMessage} from 'react-intl'; const SECTION_NONE = ( @@ -156,17 +156,14 @@ export const WEBSERVER_MODE_HELP_TEXT = ( export const LOG_LEVEL_OPTIONS = [ { value: 'DEBUG', - display_name: 'DEBUG', - display_name_default: 'DEBUG', + display_name: defineMessage({id: 'admin.log.levelOptions.DEBUG', defaultMessage: 'DEBUG'}), }, { value: 'INFO', - display_name: 'INFO', - display_name_default: 'INFO', + display_name: defineMessage({id: 'admin.log.levelOptions.INFO', defaultMessage: 'INFO'}), }, { value: 'ERROR', - display_name: 'ERROR', - display_name_default: 'ERROR', + display_name: defineMessage({id: 'admin.log.levelOptions.ERROR', defaultMessage: 'ERROR'}), }, ]; diff --git a/webapp/channels/src/components/admin_console/admin_sidebar/__snapshots__/admin_sidebar.test.tsx.snap b/webapp/channels/src/components/admin_console/admin_sidebar/__snapshots__/admin_sidebar.test.tsx.snap index 21b1280aab..e7cc7ee2b9 100644 --- a/webapp/channels/src/components/admin_console/admin_sidebar/__snapshots__/admin_sidebar.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/admin_sidebar/__snapshots__/admin_sidebar.test.tsx.snap @@ -1157,7 +1157,7 @@ exports[`components/AdminSidebar should match snapshot 1`] = ` name="experimental/feature_flags" title={ } @@ -1851,7 +1851,7 @@ exports[`components/AdminSidebar should match snapshot with workspace optimizati name="experimental/feature_flags" title={ } @@ -2600,7 +2600,7 @@ exports[`components/AdminSidebar should match snapshot, not prevent the console name="experimental/feature_flags" title={ } @@ -3294,7 +3294,7 @@ exports[`components/AdminSidebar should match snapshot, render plugins without a name="experimental/feature_flags" title={ } @@ -4150,7 +4150,7 @@ exports[`components/AdminSidebar should match snapshot, with license (with all f name="experimental/feature_flags" title={ } @@ -5103,7 +5103,7 @@ exports[`components/AdminSidebar should match snapshot, with license (without an name="experimental/feature_flags" title={ } diff --git a/webapp/channels/src/components/admin_console/admin_sidebar/admin_sidebar.test.tsx b/webapp/channels/src/components/admin_console/admin_sidebar/admin_sidebar.test.tsx index 7308f00570..8356c0d607 100644 --- a/webapp/channels/src/components/admin_console/admin_sidebar/admin_sidebar.test.tsx +++ b/webapp/channels/src/components/admin_console/admin_sidebar/admin_sidebar.test.tsx @@ -2,7 +2,6 @@ // See LICENSE.txt for license information. import React from 'react'; -import type {IntlShape} from 'react-intl'; import {SelfHostedSignupProgress} from '@mattermost/types/cloud'; import type {ExperimentalSettings, PluginSettings, SSOSettings, Office365Settings} from '@mattermost/types/config'; @@ -11,7 +10,7 @@ import {RESOURCE_KEYS} from 'mattermost-redux/constants/permissions_sysconsole'; import AdminDefinition from 'components/admin_console/admin_definition'; import AdminSidebar from 'components/admin_console/admin_sidebar/admin_sidebar'; -import type {Props} from 'components/admin_console/admin_sidebar/admin_sidebar'; +import type {Props as OriginalProps} from 'components/admin_console/admin_sidebar/admin_sidebar'; import {samplePlugin1} from 'tests/helpers/admin_console_plugin_index_sample_pluings'; import {shallowWithIntl} from 'tests/helpers/intl-test-helper'; @@ -27,8 +26,10 @@ jest.mock('utils/utils', () => { jest.mock('utils/admin_console_index'); +type Props = Omit; + describe('components/AdminSidebar', () => { - const defaultProps: Props = { + const defaultProps: Omit = { license: {}, config: { ExperimentalSettings: { @@ -40,7 +41,6 @@ describe('components/AdminSidebar', () => { } as PluginSettings, FeatureFlags: {}, }, - intl: {} as IntlShape, adminDefinition: AdminDefinition, buildEnterpriseReady: false, navigationBlocked: false, @@ -155,7 +155,6 @@ describe('components/AdminSidebar', () => { EnableUploads: true, } as PluginSettings, }, - intl: {} as IntlShape, adminDefinition: AdminDefinition, buildEnterpriseReady: false, siteName: 'test snap', @@ -202,7 +201,6 @@ describe('components/AdminSidebar', () => { EnableUploads: true, } as PluginSettings, }, - intl: {} as IntlShape, adminDefinition: AdminDefinition, buildEnterpriseReady: false, siteName: 'test snap', @@ -251,7 +249,6 @@ describe('components/AdminSidebar', () => { EnableUploads: true, } as PluginSettings, }, - intl: {} as IntlShape, adminDefinition: AdminDefinition, buildEnterpriseReady: true, navigationBlocked: false, @@ -328,7 +325,6 @@ describe('components/AdminSidebar', () => { Scope: 'scope', } as Office365Settings, }, - intl: {} as IntlShape, adminDefinition: AdminDefinition, buildEnterpriseReady: true, navigationBlocked: false, @@ -375,7 +371,6 @@ describe('components/AdminSidebar', () => { EnableUploads: true, } as PluginSettings, }, - intl: {} as IntlShape, adminDefinition: AdminDefinition, buildEnterpriseReady: true, navigationBlocked: false, @@ -464,7 +459,6 @@ describe('components/AdminSidebar', () => { EnableUploads: true, } as PluginSettings, }, - intl: {} as IntlShape, adminDefinition: AdminDefinition, buildEnterpriseReady: true, navigationBlocked: false, diff --git a/webapp/channels/src/components/admin_console/admin_sidebar/admin_sidebar.tsx b/webapp/channels/src/components/admin_console/admin_sidebar/admin_sidebar.tsx index 261c7e8486..81f80af137 100644 --- a/webapp/channels/src/components/admin_console/admin_sidebar/admin_sidebar.tsx +++ b/webapp/channels/src/components/admin_console/admin_sidebar/admin_sidebar.tsx @@ -20,7 +20,6 @@ import SearchIcon from 'components/widgets/icons/search_icon'; import {generateIndex} from 'utils/admin_console_index'; import type {Index} from 'utils/admin_console_index'; import {getHistory} from 'utils/browser_history'; -import {localizeMessage} from 'utils/utils'; import type AdminDefinition from '../admin_definition'; @@ -140,7 +139,7 @@ class AdminSidebar extends React.PureComponent { currentSiteName = ' - ' + this.props.siteName; } - document.title = localizeMessage('sidebar_right_menu.console', 'System Console') + currentSiteName; + document.title = this.props.intl.formatMessage({id: 'sidebar_right_menu.console', defaultMessage: 'System Console'}) + currentSiteName; }; visibleSections = () => { @@ -210,10 +209,11 @@ class AdminSidebar extends React.PureComponent { name={item.url} restrictedIndicator={item.restrictedIndicator?.shouldDisplay(license, subscriptionProduct) ? item.restrictedIndicator.value(cloud) : undefined} title={ - + typeof item.title === 'string' ? + item.title : + } /> )); @@ -238,10 +238,11 @@ class AdminSidebar extends React.PureComponent { icon={section.icon} sectionClass='' title={ - + typeof section.sectionTitle === 'string' ? + section.sectionTitle : + } > {sidebarItems} @@ -303,7 +304,7 @@ class AdminSidebar extends React.PureComponent { type='text' onChange={this.onFilterChange} value={this.state.filter} - placeholder={localizeMessage('admin.sidebar.filter', 'Find settings')} + placeholder={this.props.intl.formatMessage({id: 'admin.sidebar.filter', defaultMessage: 'Find settings'})} ref={this.searchRef} id='adminSidebarFilter' clearable={true} diff --git a/webapp/channels/src/components/admin_console/admin_sidebar/admin_sidebar_category.tsx b/webapp/channels/src/components/admin_console/admin_sidebar/admin_sidebar_category.tsx index 6b4d18f74a..caeee7034c 100644 --- a/webapp/channels/src/components/admin_console/admin_sidebar/admin_sidebar_category.tsx +++ b/webapp/channels/src/components/admin_console/admin_sidebar/admin_sidebar_category.tsx @@ -7,7 +7,7 @@ import {NavLink, Route} from 'react-router-dom'; type Props = { icon: JSX.Element; - title: JSX.Element; + title: string | JSX.Element; action?: JSX.Element; children?: JSX.Element[]; definitionKey?: string; diff --git a/webapp/channels/src/components/admin_console/audits/audits.tsx b/webapp/channels/src/components/admin_console/audits/audits.tsx index d3e6264225..2048c40946 100644 --- a/webapp/channels/src/components/admin_console/audits/audits.tsx +++ b/webapp/channels/src/components/admin_console/audits/audits.tsx @@ -3,7 +3,7 @@ import React from 'react'; import type {CSSProperties} from 'react'; -import {FormattedMessage} from 'react-intl'; +import {FormattedMessage, defineMessages} from 'react-intl'; import type {Audit} from '@mattermost/types/audits'; @@ -27,6 +27,14 @@ type State = { loadingAudits: boolean; }; +const messages = defineMessages({ + reload: {id: 'admin.audits.reload', defaultMessage: 'Reload User Activity Logs'}, +}); + +export const searchableStrings = [ + messages.reload, +]; + export default class Audits extends React.PureComponent { public constructor(props: Props) { super(props); @@ -71,10 +79,7 @@ export default class Audits extends React.PureComponent { onClick={this.reload} > - +
); diff --git a/webapp/channels/src/components/admin_console/billing/billing_history.tsx b/webapp/channels/src/components/admin_console/billing/billing_history.tsx index 1d691f44d5..c351adddfa 100644 --- a/webapp/channels/src/components/admin_console/billing/billing_history.tsx +++ b/webapp/channels/src/components/admin_console/billing/billing_history.tsx @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import React, {useEffect} from 'react'; -import {FormattedMessage} from 'react-intl'; +import {FormattedMessage, defineMessages} from 'react-intl'; import {useDispatch, useSelector} from 'react-redux'; import {getInvoices} from 'mattermost-redux/actions/cloud'; @@ -24,6 +24,14 @@ import BillingHistoryTable from './billing_history_table'; import './billing_history.scss'; +const messages = defineMessages({ + title: {id: 'admin.billing.history.title', defaultMessage: 'Billing History'}, +}); + +export const searchableStrings = [ + messages.title, +]; + interface NoBillingHistorySectionProps { selfHosted: boolean; } @@ -73,8 +81,7 @@ const BillingHistory = () => {
diff --git a/webapp/channels/src/components/admin_console/billing/billing_subscriptions/index.tsx b/webapp/channels/src/components/admin_console/billing/billing_subscriptions/index.tsx index 59552bef67..3829a6a0fb 100644 --- a/webapp/channels/src/components/admin_console/billing/billing_subscriptions/index.tsx +++ b/webapp/channels/src/components/admin_console/billing/billing_subscriptions/index.tsx @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import React, {useEffect, useState} from 'react'; -import {FormattedMessage} from 'react-intl'; +import {FormattedMessage, defineMessages} from 'react-intl'; import {useDispatch, useSelector} from 'react-redux'; import type {GlobalState} from '@mattermost/types/store'; @@ -49,6 +49,14 @@ import PlanDetails from '../plan_details'; import './billing_subscriptions.scss'; +const messages = defineMessages({ + title: {id: 'admin.billing.subscription.title', defaultMessage: 'Subscription'}, +}); + +export const searchableStrings = [ + messages.title, +]; + const BillingSubscriptions = () => { const dispatch = useDispatch(); const subscription = useSelector(selectCloudSubscription); @@ -121,10 +129,7 @@ const BillingSubscriptions = () => { return (
- +
diff --git a/webapp/channels/src/components/admin_console/billing/company_info.tsx b/webapp/channels/src/components/admin_console/billing/company_info.tsx index ced1df332d..b91e73b9ec 100644 --- a/webapp/channels/src/components/admin_console/billing/company_info.tsx +++ b/webapp/channels/src/components/admin_console/billing/company_info.tsx @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import React, {useEffect} from 'react'; -import {FormattedMessage} from 'react-intl'; +import {FormattedMessage, defineMessages} from 'react-intl'; import {useDispatch, useSelector} from 'react-redux'; import {getCloudCustomer} from 'mattermost-redux/actions/cloud'; @@ -18,6 +18,14 @@ import CompanyInfoDisplay from './company_info_display'; type Props = Record; +const messages = defineMessages({ + title: {id: 'admin.billing.company_info.title', defaultMessage: 'Company Information'}, +}); + +export const searchableStrings = [ + messages.title, +]; + const CompanyInfo: React.FC = () => { const dispatch = useDispatch(); const {customer: customerError} = useSelector(getCloudErrors); @@ -31,10 +39,7 @@ const CompanyInfo: React.FC = () => { return (
- +
diff --git a/webapp/channels/src/components/admin_console/billing/delete_workspace/delete_workspace_cta.tsx b/webapp/channels/src/components/admin_console/billing/delete_workspace/delete_workspace_cta.tsx index 9f094b5416..05a9a2fb55 100644 --- a/webapp/channels/src/components/admin_console/billing/delete_workspace/delete_workspace_cta.tsx +++ b/webapp/channels/src/components/admin_console/billing/delete_workspace/delete_workspace_cta.tsx @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import React from 'react'; -import {FormattedMessage} from 'react-intl'; +import {FormattedMessage, defineMessages} from 'react-intl'; import {useDispatch, useSelector} from 'react-redux'; import {getCloudSubscription, getSubscriptionProduct} from 'mattermost-redux/selectors/entities/cloud'; @@ -16,6 +16,9 @@ import {isCloudLicense} from 'utils/license_utils'; import DeleteWorkspaceModal from './delete_workspace_modal'; +export const messages = defineMessages({ + title: {id: 'admin.billing.subscription.deleteWorkspaceSection.title', defaultMessage: 'Delete your workspace'}, +}); export default function DeleteWorkspaceCTA() { const dispatch = useDispatch(); @@ -61,10 +64,7 @@ export default function DeleteWorkspaceCTA() {
- +
(); const openDowngradeModal = useOpenDowngradeModal(); @@ -226,10 +230,7 @@ export default function DeleteWorkspaceModal(props: Props) { className='btn DeleteWorkspaceModal__Buttons-Delete' onClick={handleClickDeleteWorkspace} > - + {!isStarter && !isEnterprise && @@ -761,14 +753,8 @@ export default class GroupDetails extends React.PureComponent { { ); }; } + +export default injectIntl(GroupDetails); diff --git a/webapp/channels/src/components/admin_console/group_settings/group_details/group_profile.test.tsx b/webapp/channels/src/components/admin_console/group_settings/group_details/group_profile.test.tsx index c31c47f4d6..8f67c8c56a 100644 --- a/webapp/channels/src/components/admin_console/group_settings/group_details/group_profile.test.tsx +++ b/webapp/channels/src/components/admin_console/group_settings/group_details/group_profile.test.tsx @@ -13,8 +13,7 @@ describe('components/admin_console/group_settings/group_details/GroupProfile', ( isDisabled={false} name='Test' showAtMention={true} - title='admin.group_settings.group_details.group_profile.name' - titleDefault='Name:' + title={{id: 'admin.group_settings.group_details.group_profile.name', defaultMessage: 'Name:'}} />, ); expect(wrapper).toMatchSnapshot(); diff --git a/webapp/channels/src/components/admin_console/group_settings/group_details/group_profile.tsx b/webapp/channels/src/components/admin_console/group_settings/group_details/group_profile.tsx index 9ac54c9e76..7603ad203d 100644 --- a/webapp/channels/src/components/admin_console/group_settings/group_details/group_profile.tsx +++ b/webapp/channels/src/components/admin_console/group_settings/group_details/group_profile.tsx @@ -2,14 +2,14 @@ // See LICENSE.txt for license information. import React, {memo} from 'react'; +import type {MessageDescriptor} from 'react-intl'; import {FormattedMessage} from 'react-intl'; import MentionsIcon from 'components/widgets/icons/mentions_icon'; type Props = { name?: string; - title: string; - titleDefault: string; + title: MessageDescriptor; customID?: string; isDisabled?: boolean; showAtMention: boolean; @@ -19,7 +19,6 @@ type Props = { const GroupProfile = ({ name, title, - titleDefault, customID, isDisabled, showAtMention, @@ -31,10 +30,7 @@ const GroupProfile = ({ >
diff --git a/webapp/channels/src/components/admin_console/group_settings/group_details/group_profile_and_settings.tsx b/webapp/channels/src/components/admin_console/group_settings/group_details/group_profile_and_settings.tsx index 59aa8b7a81..2c75098e7c 100644 --- a/webapp/channels/src/components/admin_console/group_settings/group_details/group_profile_and_settings.tsx +++ b/webapp/channels/src/components/admin_console/group_settings/group_details/group_profile_and_settings.tsx @@ -2,15 +2,13 @@ // See LICENSE.txt for license information. import React from 'react'; -import {FormattedMessage} from 'react-intl'; +import {FormattedMessage, defineMessage} from 'react-intl'; import GroupProfile from 'components/admin_console/group_settings/group_details/group_profile'; import LineSwitch from 'components/admin_console/team_channel_settings/line_switch'; import FormattedMarkdownMessage from 'components/formatted_markdown_message'; import AdminPanel from 'components/widgets/admin_console/admin_panel'; -import {t} from 'utils/i18n'; - type GroupSettingsToggleProps = { isDefault: boolean; allowReference: boolean; @@ -70,17 +68,12 @@ export const GroupProfileAndSettings = ({ }: GroupProfileAndSettingsProps) => ( { AD/LDAP configuration page.'} + title={defineMessage({id: 'admin.group_settings.ldapGroupsTitle', defaultMessage: 'AD/LDAP Groups'})} + subtitle={defineMessage({id: 'admin.group_settings.ldapGroupsDescription', defaultMessage: 'Connect AD/LDAP and create groups in Mattermost. To get started, configure group attributes on the AD/LDAP configuration page.'})} subtitleValues={{ link: (msg: React.ReactNode) => ( void; cancelJob: (jobId: string) => Promise; diff --git a/webapp/channels/src/components/admin_console/license_settings/enterprise_edition/enterprise_edition_left_panel.tsx b/webapp/channels/src/components/admin_console/license_settings/enterprise_edition/enterprise_edition_left_panel.tsx index beb60f49f0..fa6f6307c3 100644 --- a/webapp/channels/src/components/admin_console/license_settings/enterprise_edition/enterprise_edition_left_panel.tsx +++ b/webapp/channels/src/components/admin_console/license_settings/enterprise_edition/enterprise_edition_left_panel.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames'; import React, {useEffect, useState} from 'react'; import type {RefObject} from 'react'; -import {FormattedDate, FormattedMessage, FormattedNumber, FormattedTime, useIntl} from 'react-intl'; +import {FormattedDate, FormattedMessage, FormattedNumber, FormattedTime, defineMessages, useIntl} from 'react-intl'; import {useSelector} from 'react-redux'; import type {ClientLicense} from '@mattermost/types/config'; @@ -44,6 +44,10 @@ export interface EnterpriseEditionProps { statsActiveUsers: number; } +export const messages = defineMessages({ + keyRemove: {id: 'admin.license.keyRemove', defaultMessage: 'Remove license and downgrade to Mattermost Free'}, +}); + const EnterpriseEditionLeftPanel = ({ openEELicenseModal, upgradedFromTE, @@ -179,6 +183,7 @@ const EnterpriseEditionLeftPanel = ({ }
+ {/* This notice should not be translated */} {upgradedFromTE ? <>

{'When using Mattermost Enterprise Edition, the software is offered under a commercial license. See '} @@ -347,12 +352,7 @@ const renderRemoveButton = ( isDisabled: boolean, removing: boolean, ) => { - let removeButtonText = ( - - ); + let removeButtonText = (); if (removing) { removeButtonText = ( { return (

- +
diff --git a/webapp/channels/src/components/admin_console/license_settings/starter_edition/starter_left_panel.tsx b/webapp/channels/src/components/admin_console/license_settings/starter_edition/starter_left_panel.tsx index c38455caa4..8bc2403cc0 100644 --- a/webapp/channels/src/components/admin_console/license_settings/starter_edition/starter_left_panel.tsx +++ b/webapp/channels/src/components/admin_console/license_settings/starter_edition/starter_left_panel.tsx @@ -3,7 +3,7 @@ import React from 'react'; import type {RefObject} from 'react'; -import {FormattedMessage, useIntl} from 'react-intl'; +import {FormattedMessage, defineMessages, useIntl} from 'react-intl'; import useOpenPricingModal from 'components/common/hooks/useOpenPricingModal'; @@ -18,6 +18,10 @@ export interface StarterEditionProps { handleChange: () => void; } +export const messages = defineMessages({ + key: {id: 'admin.license.key', defaultMessage: 'License Key: '}, +}); + const StarterLeftPanel: React.FC = ({ openEELicenseModal, currentPlan, @@ -86,13 +90,8 @@ const StarterLeftPanel: React.FC = ({ }
-
- +
+
@@ -185,13 +179,11 @@ exports[`components/admin_console/permission_schemes_settings/permission_group s - + name
@@ -240,13 +232,11 @@ exports[`components/admin_console/permission_schemes_settings/permission_group s - + name
@@ -297,13 +287,11 @@ exports[`components/admin_console/permission_schemes_settings/permission_group s - + name
@@ -370,13 +358,11 @@ exports[`components/admin_console/permission_schemes_settings/permission_group s - + name
@@ -425,13 +411,11 @@ exports[`components/admin_console/permission_schemes_settings/permission_group s - + name
@@ -482,13 +466,11 @@ exports[`components/admin_console/permission_schemes_settings/permission_group s - + name
@@ -546,13 +528,11 @@ exports[`components/admin_console/permission_schemes_settings/permission_group s - + name
@@ -601,13 +581,11 @@ exports[`components/admin_console/permission_schemes_settings/permission_group s - + name
@@ -656,13 +634,11 @@ exports[`components/admin_console/permission_schemes_settings/permission_group s - + name
diff --git a/webapp/channels/src/components/admin_console/permission_schemes_settings/__snapshots__/permission_row.test.tsx.snap b/webapp/channels/src/components/admin_console/permission_schemes_settings/__snapshots__/permission_row.test.tsx.snap index 96237595bc..5f043e4859 100644 --- a/webapp/channels/src/components/admin_console/permission_schemes_settings/__snapshots__/permission_row.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/permission_schemes_settings/__snapshots__/permission_row.test.tsx.snap @@ -13,19 +13,17 @@ exports[`components/admin_console/permission_schemes_settings/permission_row sho - + id
@@ -44,14 +42,12 @@ exports[`components/admin_console/permission_schemes_settings/permission_row sho - + id
@@ -70,14 +66,12 @@ exports[`components/admin_console/permission_schemes_settings/permission_row sho - + id
@@ -96,14 +90,12 @@ exports[`components/admin_console/permission_schemes_settings/permission_row sho - + id
diff --git a/webapp/channels/src/components/admin_console/permission_schemes_settings/__snapshots__/permission_schemes_settings.test.tsx.snap b/webapp/channels/src/components/admin_console/permission_schemes_settings/__snapshots__/permission_schemes_settings.test.tsx.snap index dbdc404b2d..133d1a385d 100644 --- a/webapp/channels/src/components/admin_console/permission_schemes_settings/__snapshots__/permission_schemes_settings.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/permission_schemes_settings/__snapshots__/permission_schemes_settings.test.tsx.snap @@ -38,36 +38,59 @@ exports[`components/admin_console/permission_schemes_settings/permission_schemes
Team Override Scheme is applied.", + "id": "admin.permissions.systemSchemeBannerText", + } + } subtitleValues={ Object { "link": [Function], } } - titleDefault="System Scheme" - titleId="admin.permissions.systemSchemeBannerTitle" + title={ + Object { + "defaultMessage": "System Scheme", + "id": "admin.permissions.systemSchemeBannerTitle", + } + } url="/admin_console/user_management/permissions/system_scheme" /> System Scheme", + "id": "admin.permissions.teamOverrideSchemesBannerText", + } + } subtitleValues={ Object { "link": [Function], } } - titleDefault="Team Override Schemes" - titleId="admin.permissions.teamOverrideSchemesTitle" + title={ + Object { + "defaultMessage": "Team Override Schemes", + "id": "admin.permissions.teamOverrideSchemesTitle", + } + } url="/admin_console/user_management/permissions/team_override_scheme" >
Team Override Scheme is applied.", + "id": "admin.permissions.systemSchemeBannerText", + } + } subtitleValues={ Object { "link": [Function], } } - titleDefault="System Scheme" - titleId="admin.permissions.systemSchemeBannerTitle" + title={ + Object { + "defaultMessage": "System Scheme", + "id": "admin.permissions.systemSchemeBannerTitle", + } + } url="/admin_console/user_management/permissions/system_scheme" /> System Scheme", + "id": "admin.permissions.teamOverrideSchemesBannerText", + } + } subtitleValues={ Object { "link": [Function], } } - titleDefault="Team Override Schemes" - titleId="admin.permissions.teamOverrideSchemesTitle" + title={ + Object { + "defaultMessage": "Team Override Schemes", + "id": "admin.permissions.teamOverrideSchemesTitle", + } + } url="/admin_console/user_management/permissions/team_override_scheme" >
Team Override Scheme is applied.", + "id": "admin.permissions.systemSchemeBannerText", + } + } subtitleValues={ Object { "link": [Function], } } - titleDefault="System Scheme" - titleId="admin.permissions.systemSchemeBannerTitle" + title={ + Object { + "defaultMessage": "System Scheme", + "id": "admin.permissions.systemSchemeBannerTitle", + } + } url="/admin_console/user_management/permissions/system_scheme" /> System Scheme", + "id": "admin.permissions.teamOverrideSchemesBannerText", + } + } subtitleValues={ Object { "link": [Function], } } - titleDefault="Team Override Schemes" - titleId="admin.permissions.teamOverrideSchemesTitle" + title={ + Object { + "defaultMessage": "Team Override Schemes", + "id": "admin.permissions.teamOverrideSchemesTitle", + } + } url="/admin_console/user_management/permissions/team_override_scheme" >
- - , + "link": [Function], } } /> @@ -354,37 +415,60 @@ exports[`components/admin_console/permission_schemes_settings/permission_schemes
Team Override Scheme is applied.", + "id": "admin.permissions.systemSchemeBannerText", + } + } subtitleValues={ Object { "link": [Function], } } - titleDefault="System Scheme" - titleId="admin.permissions.systemSchemeBannerTitle" + title={ + Object { + "defaultMessage": "System Scheme", + "id": "admin.permissions.systemSchemeBannerTitle", + } + } url="/admin_console/user_management/permissions/system_scheme" /> System Scheme", + "id": "admin.permissions.teamOverrideSchemesBannerText", + } + } subtitleValues={ Object { "link": [Function], } } - titleDefault="Team Override Schemes" - titleId="admin.permissions.teamOverrideSchemesTitle" + title={ + Object { + "defaultMessage": "Team Override Schemes", + "id": "admin.permissions.teamOverrideSchemesTitle", + } + } url="/admin_console/user_management/permissions/team_override_scheme" >
- - , + "link": [Function], } } /> @@ -484,36 +560,59 @@ exports[`components/admin_console/permission_schemes_settings/permission_schemes
Team Override Scheme is applied.", + "id": "admin.permissions.systemSchemeBannerText", + } + } subtitleValues={ Object { "link": [Function], } } - titleDefault="System Scheme" - titleId="admin.permissions.systemSchemeBannerTitle" + title={ + Object { + "defaultMessage": "System Scheme", + "id": "admin.permissions.systemSchemeBannerTitle", + } + } url="/admin_console/user_management/permissions/system_scheme" /> System Scheme", + "id": "admin.permissions.teamOverrideSchemesBannerText", + } + } subtitleValues={ Object { "link": [Function], } } - titleDefault="Team Override Schemes" - titleId="admin.permissions.teamOverrideSchemesTitle" + title={ + Object { + "defaultMessage": "Team Override Schemes", + "id": "admin.permissions.teamOverrideSchemesTitle", + } + } url="/admin_console/user_management/permissions/team_override_scheme" > ; selectRow: (id: string) => void; additionalValues?: AdditionalValues | AdditionalValues['edit_post']; + description: string | JSX.Element; } -const PermissionDescription = (props: Props): JSX.Element => { +const PermissionDescription = ({ + id, + selectRow, + description, + additionalValues, + inherited, +}: Props): JSX.Element => { const [open, setOpen] = useState(false); const randomId = generateId(); const contentRef = useRef(null); @@ -43,35 +50,24 @@ const PermissionDescription = (props: Props): JSX.Element => { if (parent?.className !== 'permission-description' && !isInheritLink) { e.stopPropagation(); } else if (isInheritLink) { - props.selectRow(props.id); + selectRow(id); e.stopPropagation(); } }; - const {inherited, id, rowType} = props; - let content: string | JSX.Element = ''; - if (inherited) { + if (inherited && inherited.name) { content = ( ); } else { - content = ( - - ); + content = description; } let tooltip: JSX.Element | null = ( { ); - if (content.props.values && Object.keys(content.props.values).length > 0) { + if (!inherited && additionalValues) { tooltip = null; } content = ( diff --git a/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_group.tsx b/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_group.tsx index 61023a4b94..d049ee7288 100644 --- a/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_group.tsx +++ b/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_group.tsx @@ -13,6 +13,7 @@ import PermissionCheckbox from './permission_checkbox'; import PermissionDescription from './permission_description'; import PermissionRow from './permission_row'; import type {AdditionalValues, Permission, Permissions} from './permissions_tree/types'; +import {groupRolesStrings} from './strings/groups'; type Props = { id: string; @@ -280,6 +281,9 @@ export default class PermissionGroup extends React.PureComponent { } const additionalValuesProp = additionalValues?.[id] ? additionalValues[id] : undefined; + const name = groupRolesStrings[id] ? : id; + const description = groupRolesStrings[id] ? : ''; + return (
{!root && @@ -298,14 +302,14 @@ export default class PermissionGroup extends React.PureComponent { id={`${uniqId}-checkbox`} /> - + {name}
} {!combined && diff --git a/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_row.tsx b/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_row.tsx index 53c1104167..578e5cf108 100644 --- a/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_row.tsx +++ b/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_row.tsx @@ -1,7 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React from 'react'; +import classNames from 'classnames'; +import React, {useCallback} from 'react'; import {FormattedMessage} from 'react-intl'; import type {Role} from '@mattermost/types/roles'; @@ -9,6 +10,7 @@ import type {Role} from '@mattermost/types/roles'; import PermissionCheckbox from './permission_checkbox'; import PermissionDescription from './permission_description'; import type {AdditionalValues} from './permissions_tree/types'; +import {permissionRolesStrings} from './strings/permissions'; type Props = { id: string; @@ -22,27 +24,30 @@ type Props = { additionalValues: AdditionalValues; } -const PermissionRow = (props: Props): JSX.Element => { - const toggleSelect = (): void => { - if (props.readOnly) { +const PermissionRow = ({ + additionalValues, + id, + onChange, + selectRow, + uniqId, + value, + inherited, + readOnly, + selected, +}: Props) => { + const toggleSelect = useCallback(() => { + if (readOnly) { return; } - props.onChange(props.id); - }; + onChange(id); + }, [readOnly, onChange, id]); - const {id, uniqId, inherited, value, readOnly, selected, additionalValues} = props; - let classes = 'permission-row'; - if (readOnly) { - classes += ' read-only'; - } - - if (selected === id) { - classes += ' selected'; - } + const name = permissionRolesStrings[id] ? : id; + const description = permissionRolesStrings[id] ? : ''; return (
@@ -51,15 +56,13 @@ const PermissionRow = (props: Props): JSX.Element => { id={`${uniqId}-checkbox`} /> - + {name}
diff --git a/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_schemes_settings.test.tsx b/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_schemes_settings.test.tsx index 1c0fa48d26..59cbd0098b 100644 --- a/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_schemes_settings.test.tsx +++ b/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_schemes_settings.test.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {shallow} from 'enzyme'; import React from 'react'; import type {ComponentProps} from 'react'; import type {RouteComponentProps} from 'react-router-dom'; @@ -10,6 +9,8 @@ import type {Scheme} from '@mattermost/types/schemes'; import PermissionSchemesSettings from 'components/admin_console/permission_schemes_settings/permission_schemes_settings'; +import {shallowWithIntl} from 'tests/helpers/intl-test-helper'; + describe('components/admin_console/permission_schemes_settings/permission_schemes_settings', () => { const defaultProps: ComponentProps = { schemes: { @@ -31,14 +32,14 @@ describe('components/admin_console/permission_schemes_settings/permission_scheme }; test('should match snapshot loading', () => { - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); expect(wrapper).toMatchSnapshot(); }); test('should match snapshot without schemes', () => { - const wrapper = shallow( + const wrapper = shallowWithIntl( { - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); wrapper.setState({loading: false, phase2MigrationIsComplete: true}); @@ -57,7 +58,7 @@ describe('components/admin_console/permission_schemes_settings/permission_scheme }); test('should show migration in-progress view', () => { - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); wrapper.setState({loading: false, phase2MigrationIsComplete: false}); @@ -67,7 +68,7 @@ describe('components/admin_console/permission_schemes_settings/permission_scheme test('should show migration on hold view', () => { const testProps = {...defaultProps}; testProps.jobsAreEnabled = false; - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); wrapper.setState({loading: false, phase2MigrationIsComplete: false}); @@ -77,7 +78,7 @@ describe('components/admin_console/permission_schemes_settings/permission_scheme test('should show normal view (jobs disabled after migration)', () => { const testProps = {...defaultProps}; testProps.jobsAreEnabled = false; - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); wrapper.setState({loading: false, phase2MigrationIsComplete: true}); diff --git a/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_schemes_settings.tsx b/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_schemes_settings.tsx index 9f9b3776e4..112e6a25bd 100644 --- a/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_schemes_settings.tsx +++ b/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_schemes_settings.tsx @@ -2,7 +2,8 @@ // See LICENSE.txt for license information. import React from 'react'; -import {FormattedMessage} from 'react-intl'; +import {injectIntl, type MessageDescriptor, type WrappedComponentProps} from 'react-intl'; +import {FormattedMessage, defineMessage, defineMessages} from 'react-intl'; import type {RouteComponentProps} from 'react-router-dom'; import type {Scheme, SchemeScope, SchemesState} from '@mattermost/types/schemes'; @@ -16,8 +17,6 @@ import AdminPanelWithLink from 'components/widgets/admin_console/admin_panel_wit import LoadingWrapper from 'components/widgets/loading/loading_wrapper'; import {DocLinks, LicenseSkus} from 'utils/constants'; -import {t} from 'utils/i18n'; -import * as Utils from 'utils/utils'; import PermissionsSchemeSummary from './permissions_scheme_summary'; @@ -37,7 +36,7 @@ export type Props = { loadSchemeTeams: (id: string) => Promise; }; isDisabled?: boolean; -}; +} & WrappedComponentProps; type State = { loading: boolean; @@ -45,7 +44,32 @@ type State = { page: number; phase2MigrationIsComplete: boolean; }; -export default class PermissionSchemesSettings extends React.PureComponent { + +const messages = defineMessages({ + teamOverrideSchemesNoSchemes: {id: 'admin.permissions.teamOverrideSchemesNoSchemes', defaultMessage: 'No team override schemes created.'}, + loadMoreSchemes: {id: 'admin.permissions.loadMoreSchemes', defaultMessage: 'Load more schemes'}, + introBanner: {id: 'admin.permissions.introBanner', defaultMessage: 'Permission Schemes set the default permissions for Team Admins, Channel Admins and everyone else. Learn more about permission schemes in our documentation.'}, + systemSchemeBannerTitle: {id: 'admin.permissions.systemSchemeBannerTitle', defaultMessage: 'System Scheme'}, + systemSchemeBannerText: {id: 'admin.permissions.systemSchemeBannerText', defaultMessage: 'Set the default permissions inherited by all teams unless a Team Override Scheme is applied.'}, + systemSchemeBannerButton: {id: 'admin.permissions.systemSchemeBannerButton', defaultMessage: 'Edit Scheme'}, + teamOverrideSchemesTitle: {id: 'admin.permissions.teamOverrideSchemesTitle', defaultMessage: 'Team Override Schemes'}, + teamOverrideSchemesBannerText: {id: 'admin.permissions.teamOverrideSchemesBannerText', defaultMessage: 'Use when specific teams need permission exceptions to the System Scheme'}, + teamOverrideSchemesNewButton: {id: 'admin.permissions.teamOverrideSchemesNewButton', defaultMessage: 'New Team Override Scheme'}, +}); + +export const searchableStrings = [ + messages.teamOverrideSchemesNoSchemes, + messages.loadMoreSchemes, + messages.introBanner, + messages.systemSchemeBannerTitle, + messages.systemSchemeBannerText, + messages.systemSchemeBannerButton, + messages.teamOverrideSchemesTitle, + messages.teamOverrideSchemesBannerText, + messages.teamOverrideSchemesNewButton, +]; + +class PermissionSchemesSettings extends React.PureComponent { constructor(props: Props & RouteComponentProps) { super(props); this.state = { @@ -98,41 +122,39 @@ export default class PermissionSchemesSettings extends React.PureComponent - - - ); - if (this.props.jobsAreEnabled && !this.props.clusterIsEnabled) { return this.teamOverrideUnavalableView( - t('admin.permissions.teamOverrideSchemesInProgress'), - 'Migration job in progress: Team Override Schemes are not available until the job server completes the permissions migration. Learn more in the {documentationLink}.', - docLink, + defineMessage({ + id: 'admin.permissions.teamOverrideSchemesInProgress', + defaultMessage: 'Migration job in progress: Team Override Schemes are not available until the job server completes the permissions migration. Learn more in the documentation.', + }), ); } return this.teamOverrideUnavalableView( - t('admin.permissions.teamOverrideSchemesNoJobsEnabled'), - 'Migration job on hold: Team Override Schemes are not available until the job server can execute the permissions migration. The job will be automatically started when the job server is enabled. Learn more in the {documentationLink}.', - docLink, + defineMessage({ + id: 'admin.permissions.teamOverrideSchemesNoJobsEnabled', + defaultMessage: 'Migration job on hold: Team Override Schemes are not available until the job server can execute the permissions migration. The job will be automatically started when the job server is enabled. Learn more in the documentation.', + }), ); }; - teamOverrideUnavalableView = (id: string, defaultMsg: string, documentationLink: React.ReactNode) => { + teamOverrideUnavalableView = (message: MessageDescriptor) => { return (
( + + {chunks} + + ), + }} />
@@ -158,10 +180,8 @@ export default class PermissionSchemesSettings extends React.PureComponent ( {schemes.length === 0 && teamOverrideView === null &&
} {teamOverrideView} @@ -195,12 +213,9 @@ export default class PermissionSchemesSettings extends React.PureComponent - + }
@@ -230,8 +245,7 @@ export default class PermissionSchemesSettings extends React.PureComponent ( ( {this.renderTeamOverrideSchemes()} @@ -277,182 +288,4 @@ export default class PermissionSchemesSettings extends React.PureComponent { const defaultRole: Role = { id: '', @@ -56,11 +64,11 @@ describe('components/admin_console/permission_schemes_settings/permission_system }; test('should match snapshot on roles without permissions', (done) => { - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); defaultProps.actions.loadRolesIfNeeded().then(() => { - expect(wrapper.state()).toMatchSnapshot(); + expect(getAnyState(wrapper)).toMatchSnapshot(); done(); }); }); @@ -70,7 +78,7 @@ describe('components/admin_console/permission_schemes_settings/permission_system IsLicensed: 'true', CustomPermissionsSchemes: 'false', }; - const wrapper = shallow( + const wrapper = shallowWithIntl( { - expect(wrapper.state()).toMatchSnapshot(); + expect(getAnyState(wrapper)).toMatchSnapshot(); done(); }); }); test('should save each role on handleSubmit except system_admin role', async () => { const editRole = jest.fn().mockImplementation(() => Promise.resolve({data: {}})); - const wrapper = shallow( + const wrapper = shallowWithIntl( Promise.resolve({data: {}})); - const wrapper = shallow( + const wrapper = shallowWithIntl( Promise.resolve({data: {}})); - const wrapper2 = shallow( + const wrapper2 = shallowWithIntl( { const editRole = jest.fn().mockImplementation(() => Promise.resolve({error: {message: 'test error'}})); - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); - await wrapper.instance().handleSubmit(); - await expect(wrapper.state().serverError).toBe('test error'); + await getAnyInstance(wrapper).handleSubmit(); + await expect(getAnyState(wrapper).serverError).toBe('test error'); }); test('should open and close correctly roles blocks', () => { - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); - const instance = wrapper.instance(); - expect(wrapper.state().openRoles.all_users).toBe(true); + const instance = getAnyInstance(wrapper); + expect(getAnyState(wrapper).openRoles.all_users).toBe(true); instance.toggleRole('all_users'); - expect(wrapper.state().openRoles.all_users).toBe(false); + expect(getAnyState(wrapper).openRoles.all_users).toBe(false); instance.toggleRole('all_users'); - expect(wrapper.state().openRoles.all_users).toBe(true); + expect(getAnyState(wrapper).openRoles.all_users).toBe(true); - expect(wrapper.state().openRoles.channel_admin).toBe(true); + expect(getAnyState(wrapper).openRoles.channel_admin).toBe(true); instance.toggleRole('channel_admin'); - expect(wrapper.state().openRoles.channel_admin).toBe(false); + expect(getAnyState(wrapper).openRoles.channel_admin).toBe(false); instance.toggleRole('channel_admin'); - expect(wrapper.state().openRoles.channel_admin).toBe(true); + expect(getAnyState(wrapper).openRoles.channel_admin).toBe(true); - expect(wrapper.state().openRoles.team_admin).toBe(true); + expect(getAnyState(wrapper).openRoles.team_admin).toBe(true); instance.toggleRole('team_admin'); - expect(wrapper.state().openRoles.team_admin).toBe(false); + expect(getAnyState(wrapper).openRoles.team_admin).toBe(false); instance.toggleRole('team_admin'); - expect(wrapper.state().openRoles.team_admin).toBe(true); + expect(getAnyState(wrapper).openRoles.team_admin).toBe(true); - expect(wrapper.state().openRoles.system_admin).toBe(true); + expect(getAnyState(wrapper).openRoles.system_admin).toBe(true); instance.toggleRole('system_admin'); - expect(wrapper.state().openRoles.system_admin).toBe(false); + expect(getAnyState(wrapper).openRoles.system_admin).toBe(false); instance.toggleRole('system_admin'); - expect(wrapper.state().openRoles.system_admin).toBe(true); + expect(getAnyState(wrapper).openRoles.system_admin).toBe(true); }); test('should open modal on click reset defaults', () => { - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); - expect(wrapper.state().showResetDefaultModal).toBe(false); + expect(getAnyState(wrapper).showResetDefaultModal).toBe(false); wrapper.find('.btn-quaternary').first().simulate('click'); - expect(wrapper.state().showResetDefaultModal).toBe(true); + expect(getAnyState(wrapper).showResetDefaultModal).toBe(true); }); test('should have default permissions that match the defaults constant', () => { - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); - expect(wrapper.state().roles.all_users.permissions?.length).toBe(0); - expect(wrapper.state().roles.channel_admin.permissions?.length).toBe(0); - expect(wrapper.state().roles.team_admin.permissions?.length).toBe(0); - wrapper.instance().resetDefaults(); - expect(wrapper.state().roles.all_users.permissions).toBe(DefaultRolePermissions.all_users); - expect(wrapper.state().roles.channel_admin.permissions).toBe(DefaultRolePermissions.channel_admin); - expect(wrapper.state().roles.team_admin.permissions).toBe(DefaultRolePermissions.team_admin); - expect(wrapper.state().roles.system_admin.permissions?.length).toBe(defaultProps.roles.system_admin.permissions.length); + expect(getAnyState(wrapper).roles.all_users.permissions?.length).toBe(0); + expect(getAnyState(wrapper).roles.channel_admin.permissions?.length).toBe(0); + expect(getAnyState(wrapper).roles.team_admin.permissions?.length).toBe(0); + getAnyInstance(wrapper).resetDefaults(); + expect(getAnyState(wrapper).roles.all_users.permissions).toBe(DefaultRolePermissions.all_users); + expect(getAnyState(wrapper).roles.channel_admin.permissions).toBe(DefaultRolePermissions.channel_admin); + expect(getAnyState(wrapper).roles.team_admin.permissions).toBe(DefaultRolePermissions.team_admin); + expect(getAnyState(wrapper).roles.system_admin.permissions?.length).toBe(defaultProps.roles.system_admin.permissions.length); }); }); diff --git a/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_system_scheme_settings/permission_system_scheme_settings.tsx b/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_system_scheme_settings/permission_system_scheme_settings.tsx index abb7a3b913..5f4e0a613a 100644 --- a/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_system_scheme_settings/permission_system_scheme_settings.tsx +++ b/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_system_scheme_settings/permission_system_scheme_settings.tsx @@ -2,7 +2,8 @@ // See LICENSE.txt for license information. import React from 'react'; -import {FormattedMessage} from 'react-intl'; +import type {WrappedComponentProps} from 'react-intl'; +import {FormattedMessage, defineMessage, injectIntl} from 'react-intl'; import type {ClientConfig, ClientLicense} from '@mattermost/types/config'; import type {Role} from '@mattermost/types/roles'; @@ -20,8 +21,6 @@ import AdminHeader from 'components/widgets/admin_console/admin_header'; import AdminPanelTogglable from 'components/widgets/admin_console/admin_panel_togglable'; import {PermissionsScope, DefaultRolePermissions, DocLinks} from 'utils/constants'; -import {t} from 'utils/i18n'; -import {localizeMessage} from 'utils/utils'; import GuestPermissionsTree, {GUEST_INCLUDED_PERMISSIONS} from '../guest_permissions_tree'; import PermissionsTree, {EXCLUDED_PERMISSIONS} from '../permissions_tree'; @@ -38,7 +37,7 @@ type Props = { setNavigationBlocked: (blocked: boolean) => void; }; location: Location; -} +} & WrappedComponentProps; type State = { showResetDefaultModal: boolean; @@ -52,7 +51,7 @@ type State = { urlParams: URLSearchParams; } -export default class PermissionSystemSchemeSettings extends React.PureComponent { +class PermissionSystemSchemeSettings extends React.PureComponent { private rolesNeeded: string[]; constructor(props: Props) { @@ -395,10 +394,8 @@ export default class PermissionSystemSchemeSettings extends React.PureComponent< open={this.state.openRoles.guests} id='all_users' onToggle={() => this.toggleRole('guests')} - titleId={t('admin.permissions.systemScheme.GuestsTitle')} - titleDefault='Guests' - subtitleId={t('admin.permissions.systemScheme.GuestsDescription')} - subtitleDefault='Permissions granted to guest users.' + title={defineMessage({id: 'admin.permissions.systemScheme.GuestsTitle', defaultMessage: 'Guests'})} + subtitle={defineMessage({id: 'admin.permissions.systemScheme.GuestsDescription', defaultMessage: 'Permissions granted to guest users.'})} > this.toggleRole('all_users')} - titleId={t('admin.permissions.systemScheme.allMembersTitle')} - titleDefault='All Members' - subtitleId={t('admin.permissions.systemScheme.allMembersDescription')} - subtitleDefault='Permissions granted to all members, including administrators and newly created users.' + title={defineMessage({id: 'admin.permissions.systemScheme.allMembersTitle', defaultMessage: 'All Members'})} + subtitle={defineMessage({id: 'admin.permissions.systemScheme.allMembersDescription', defaultMessage: 'Permissions granted to all members, including administrators and newly created users.'})} > this.toggleRole('channel_admin')} - titleId={t('admin.permissions.systemScheme.channelAdminsTitle')} - titleDefault='Channel Administrators' - subtitleId={t('admin.permissions.systemScheme.channelAdminsDescription')} - subtitleDefault='Permissions granted to channel creators and any users promoted to Channel Administrator.' + title={defineMessage({id: 'admin.permissions.systemScheme.channelAdminsTitle', defaultMessage: 'Channel Administrators'})} + subtitle={defineMessage({id: 'admin.permissions.systemScheme.channelAdminsDescription', defaultMessage: 'Permissions granted to channel creators and any users promoted to Channel Administrator.'})} > this.toggleRole('playbook_admin')} - titleId={t('admin.permissions.systemScheme.playbookAdmin')} - titleDefault='Playbook Administrator' - subtitleId={t('admin.permissions.systemScheme.playbookAdminSubtitle')} - subtitleDefault='Permissions granted to administrators of a playbook.' + title={defineMessage({id: 'admin.permissions.systemScheme.playbookAdmin', defaultMessage: 'Playbook Administrator'})} + subtitle={defineMessage({id: 'admin.permissions.systemScheme.playbookAdminSubtitle', defaultMessage: 'Permissions granted to administrators of a playbook.'})} > this.toggleRole('team_admin')} - titleId={t('admin.permissions.systemScheme.teamAdminsTitle')} - titleDefault='Team Administrators' - subtitleId={t('admin.permissions.systemScheme.teamAdminsDescription')} - subtitleDefault='Permissions granted to team creators and any users promoted to Team Administrator.' + title={defineMessage({id: 'admin.permissions.systemScheme.teamAdminsTitle', defaultMessage: 'Team Administrators'})} + subtitle={defineMessage({id: 'admin.permissions.systemScheme.teamAdminsDescription', defaultMessage: 'Permissions granted to team creators and any users promoted to Team Administrator.'})} > this.toggleRole('system_admin')} - titleId={t('admin.permissions.systemScheme.systemAdminsTitle')} - titleDefault='System Administrators' - subtitleId={t('admin.permissions.systemScheme.systemAdminsDescription')} - subtitleDefault='Full permissions granted to System Administrators.' + title={defineMessage({id: 'admin.permissions.systemScheme.systemAdminsTitle', defaultMessage: 'System Administrators'})} + subtitle={defineMessage({id: 'admin.permissions.systemScheme.systemAdminsDescription', defaultMessage: 'Full permissions granted to System Administrators.'})} >
@@ -126,15 +132,27 @@ exports[`components/admin_console/permission_schemes_settings/permission_team_sc
+ } />
@@ -450,15 +507,27 @@ exports[`components/admin_console/permission_schemes_settings/permission_team_sc
+ } />
@@ -797,15 +912,27 @@ exports[`components/admin_console/permission_schemes_settings/permission_team_sc
+ } />
@@ -1212,15 +1385,27 @@ exports[`components/admin_console/permission_schemes_settings/permission_team_sc
+ } /> { const defaultProps = { config: { @@ -110,11 +118,11 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s } as any; test('should match snapshot on new with default roles without permissions', (done) => { - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); defaultProps.actions.loadRolesIfNeeded().then(() => { - expect(wrapper.instance().getStateRoles()).toMatchSnapshot(); + expect(getAnyInstance(wrapper).getStateRoles()).toMatchSnapshot(); done(); }); }); @@ -149,7 +157,7 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s permissions: ['delete_post'], }, }; - const wrapper = shallow( + const wrapper = shallowWithIntl( { - expect(wrapper.state()).toMatchSnapshot(); + expect(getAnyState(wrapper)).toMatchSnapshot(); done(); }); }); @@ -181,7 +189,7 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s }, })); const updateTeamScheme = jest.fn().mockImplementation(() => Promise.resolve({})); - const wrapper = shallow( + const wrapper = shallowWithIntl( Promise.resolve({})); const createScheme = jest.fn().mockImplementation(() => Promise.resolve({error: {message: 'test error'}})); const updateTeamScheme = jest.fn().mockImplementation(() => Promise.resolve({})); - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); - await wrapper.instance().handleSubmit(); - expect(wrapper.state().serverError).toBe('test error'); + await getAnyInstance(wrapper).handleSubmit(); + expect(getAnyState(wrapper).serverError).toBe('test error'); }); test('should show error if editRole fails', async () => { @@ -226,45 +234,45 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s }, })); const updateTeamScheme = jest.fn().mockImplementation(() => Promise.resolve({})); - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); - await wrapper.instance().handleSubmit(); - expect(wrapper.state().serverError).toBe('test error'); + await getAnyInstance(wrapper).handleSubmit(); + expect(getAnyState(wrapper).serverError).toBe('test error'); }); test('should open and close correctly roles blocks', () => { - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); - const instance = wrapper.instance(); - expect(wrapper.state().openRoles.guests).toBe(true); + const instance = getAnyInstance(wrapper); + expect(getAnyState(wrapper).openRoles.guests).toBe(true); instance.toggleRole('guests'); - expect(wrapper.state().openRoles.guests).toBe(false); + expect(getAnyState(wrapper).openRoles.guests).toBe(false); instance.toggleRole('guests'); - expect(wrapper.state().openRoles.guests).toBe(true); + expect(getAnyState(wrapper).openRoles.guests).toBe(true); - expect(wrapper.state().openRoles.all_users).toBe(true); + expect(getAnyState(wrapper).openRoles.all_users).toBe(true); instance.toggleRole('all_users'); - expect(wrapper.state().openRoles.all_users).toBe(false); + expect(getAnyState(wrapper).openRoles.all_users).toBe(false); instance.toggleRole('all_users'); - expect(wrapper.state().openRoles.all_users).toBe(true); + expect(getAnyState(wrapper).openRoles.all_users).toBe(true); - expect(wrapper.state().openRoles.channel_admin).toBe(true); + expect(getAnyState(wrapper).openRoles.channel_admin).toBe(true); instance.toggleRole('channel_admin'); - expect(wrapper.state().openRoles.channel_admin).toBe(false); + expect(getAnyState(wrapper).openRoles.channel_admin).toBe(false); instance.toggleRole('channel_admin'); - expect(wrapper.state().openRoles.channel_admin).toBe(true); + expect(getAnyState(wrapper).openRoles.channel_admin).toBe(true); - expect(wrapper.state().openRoles.team_admin).toBe(true); + expect(getAnyState(wrapper).openRoles.team_admin).toBe(true); instance.toggleRole('team_admin'); - expect(wrapper.state().openRoles.team_admin).toBe(false); + expect(getAnyState(wrapper).openRoles.team_admin).toBe(false); instance.toggleRole('team_admin'); - expect(wrapper.state().openRoles.team_admin).toBe(true); + expect(getAnyState(wrapper).openRoles.team_admin).toBe(true); }); test('should match snapshot on edit without permissions', (done) => { @@ -289,12 +297,12 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s }, }; - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); expect(wrapper).toMatchSnapshot(); defaultProps.actions.loadRolesIfNeeded().then(() => { - expect(wrapper.instance().getStateRoles()).toMatchSnapshot(); + expect(getAnyInstance(wrapper).getStateRoles()).toMatchSnapshot(); done(); }); }); @@ -344,12 +352,12 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s }, }; - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); expect(wrapper).toMatchSnapshot(); defaultProps.actions.loadRolesIfNeeded().then(() => { - expect(wrapper.instance().getStateRoles()).toMatchSnapshot(); + expect(getAnyInstance(wrapper).getStateRoles()).toMatchSnapshot(); done(); }); }); @@ -379,12 +387,12 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s }, }; - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); expect(wrapper).toMatchSnapshot(); defaultProps.actions.loadRolesIfNeeded().then(() => { - expect(wrapper.instance().getStateRoles()).toMatchSnapshot(); + expect(getAnyInstance(wrapper).getStateRoles()).toMatchSnapshot(); done(); }); }); @@ -414,12 +422,12 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s }, }; - const wrapper = shallow( + const wrapper = shallowWithIntl( , ); expect(wrapper).toMatchSnapshot(); defaultProps.actions.loadRolesIfNeeded().then(() => { - expect(wrapper.instance().getStateRoles()).toMatchSnapshot(); + expect(getAnyInstance(wrapper).getStateRoles()).toMatchSnapshot(); done(); }); }); diff --git a/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_team_scheme_settings/permission_team_scheme_settings.tsx b/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_team_scheme_settings/permission_team_scheme_settings.tsx index e00207abc0..152218b9f3 100644 --- a/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_team_scheme_settings/permission_team_scheme_settings.tsx +++ b/webapp/channels/src/components/admin_console/permission_schemes_settings/permission_team_scheme_settings/permission_team_scheme_settings.tsx @@ -2,7 +2,8 @@ // See LICENSE.txt for license information. import React from 'react'; -import {FormattedMessage, injectIntl, type IntlShape} from 'react-intl'; +import {defineMessage, FormattedMessage, injectIntl} from 'react-intl'; +import type {WrappedComponentProps} from 'react-intl'; import type {RouteComponentProps} from 'react-router-dom'; import type {ClientConfig, ClientLicense} from '@mattermost/types/config'; @@ -25,8 +26,6 @@ import AdminPanelTogglable from 'components/widgets/admin_console/admin_panel_to import AdminPanelWithButton from 'components/widgets/admin_console/admin_panel_with_button'; import {PermissionsScope, ModalIdentifiers, DocLinks} from 'utils/constants'; -import {t} from 'utils/i18n'; -import {localizeMessage} from 'utils/utils'; import TeamInList from './team_in_list'; @@ -46,7 +45,6 @@ export type Props = { teams: Team[] | null; isDisabled: boolean; config: Partial; - intl: IntlShape; actions: { loadRolesIfNeeded: (roles: Iterable) => Promise; loadScheme: (schemeId: string) => Promise; @@ -57,7 +55,7 @@ export type Props = { createScheme: (scheme: Scheme) => Promise; setNavigationBlocked: (blocked: boolean) => void; }; -} +} & WrappedComponentProps; type State = { saving: boolean; @@ -81,7 +79,7 @@ type State = { schemeDescription: string | undefined; }; -export class PermissionTeamSchemeSettings extends React.PureComponent { +class PermissionTeamSchemeSettings extends React.PureComponent { constructor(props: Props & RouteComponentProps) { super(props); this.state = { @@ -633,10 +631,8 @@ export class PermissionTeamSchemeSettings extends React.PureComponent
@@ -674,7 +670,7 @@ export class PermissionTeamSchemeSettings extends React.PureComponent @@ -684,13 +680,10 @@ export class PermissionTeamSchemeSettings extends React.PureComponent
@@ -718,11 +711,8 @@ export class PermissionTeamSchemeSettings extends React.PureComponent this.toggleRole('guests')} - titleId={t('admin.permissions.systemScheme.GuestsTitle')} - titleDefault='Guests' - subtitleId={t('admin.permissions.systemScheme.GuestsDescription')} - subtitleDefault='Permissions granted to guest users.' - isDisabled={this.props.isDisabled} + title={defineMessage({id: 'admin.permissions.systemScheme.GuestsTitle', defaultMessage: 'Guests'})} + subtitle={defineMessage({id: 'admin.permissions.systemScheme.GuestsDescription', defaultMessage: 'Permissions granted to guest users.'})} > this.toggleRole('all_users')} - titleId={t('admin.permissions.systemScheme.allMembersTitle')} - titleDefault='All Members' - subtitleId={t('admin.permissions.systemScheme.allMembersDescription')} - subtitleDefault='Permissions granted to all members, including administrators and newly created users.' - isDisabled={this.props.isDisabled} + title={defineMessage({id: 'admin.permissions.systemScheme.allMembersTitle', defaultMessage: 'All Members'})} + subtitle={defineMessage({id: 'admin.permissions.systemScheme.allMembersDescription', defaultMessage: 'Permissions granted to all members, including administrators and newly created users.'})} > this.toggleRole('channel_admin')} - titleId={t('admin.permissions.systemScheme.channelAdminsTitle')} - titleDefault='Channel Administrators' - subtitleId={t('admin.permissions.systemScheme.channelAdminsDescription')} - subtitleDefault='Permissions granted to channel creators and any users promoted to Channel Administrator.' - isDisabled={this.props.isDisabled} + title={defineMessage({id: 'admin.permissions.systemScheme.channelAdminsTitle', defaultMessage: 'Channel Administrators'})} + subtitle={defineMessage({id: 'admin.permissions.systemScheme.channelAdminsDescription', defaultMessage: 'Permissions granted to channel creators and any users promoted to Channel Administrator.'})} > this.toggleRole('playbook_admin')} - titleId={t('admin.permissions.systemScheme.playbookAdmin')} - titleDefault='Playbook Administrator' - subtitleId={t('admin.permissions.systemScheme.playbookAdminSubtitle')} - subtitleDefault='Permissions granted to administrators of a playbook.' - isDisabled={this.props.isDisabled} + title={defineMessage({id: 'admin.permissions.systemScheme.playbookAdmin', defaultMessage: 'Playbook Administrator'})} + subtitle={defineMessage({id: 'admin.permissions.systemScheme.playbookAdminSubtitle', defaultMessage: 'Permissions granted to administrators of a playbook.'})} > this.toggleRole('team_admin')} - titleId={t('admin.permissions.systemScheme.teamAdminsTitle')} - titleDefault='Team Administrators' - subtitleId={t('admin.permissions.systemScheme.teamAdminsDescription')} - subtitleDefault='Permissions granted to team creators and any users promoted to Team Administrator.' - isDisabled={this.props.isDisabled} + title={defineMessage({id: 'admin.permissions.systemScheme.teamAdminsTitle', defaultMessage: 'Team Administrators'})} + subtitle={defineMessage({id: 'admin.permissions.systemScheme.teamAdminsDescription', defaultMessage: 'Permissions granted to team creators and any users promoted to Team Administrator.'})} > > = { + delete_posts: defineMessages({ + name: { + id: 'admin.permissions.group.delete_posts.name', + defaultMessage: 'Delete Posts', + }, + description: { + id: 'admin.permissions.group.delete_posts.description', + defaultMessage: 'Delete own and others\' posts.', + }, + }), + integrations: defineMessages({ + name: { + id: 'admin.permissions.group.integrations.name', + defaultMessage: 'Integrations & Customizations', + }, + description: { + id: 'admin.permissions.group.integrations.description', + defaultMessage: 'Manage OAuth 2.0, slash commands, webhooks and emoji.', + }, + }), + posts: defineMessages({ + name: { + id: 'admin.permissions.group.posts.name', + defaultMessage: 'Manage Posts', + }, + description: { + id: 'admin.permissions.group.posts.description', + defaultMessage: 'Write, edit and delete posts.', + }, + }), + private_channel: defineMessages({ + name: { + id: 'admin.permissions.group.private_channel.name', + defaultMessage: 'Manage Private Channels', + }, + description: { + id: 'admin.permissions.group.private_channel.description', + defaultMessage: 'Create and archive channels, manage settings and members.', + }, + }), + public_channel: defineMessages({ + name: { + id: 'admin.permissions.group.public_channel.name', + defaultMessage: 'Manage Public Channels', + }, + description: { + id: 'admin.permissions.group.public_channel.description', + defaultMessage: 'Join, create and archive channels, manage settings and members.', + }, + }), + reactions: defineMessages({ + name: { + id: 'admin.permissions.group.reactions.name', + defaultMessage: 'Post Reactions', + }, + description: { + id: 'admin.permissions.group.reactions.description', + defaultMessage: 'Add and delete reactions on posts.', + }, + }), + send_invites: defineMessages({ + name: { + id: 'admin.permissions.group.send_invites.name', + defaultMessage: 'Add Team Members', + }, + description: { + id: 'admin.permissions.group.send_invites.description', + defaultMessage: 'Add team members, send email invites and share team invite link.', + }, + }), + teams: defineMessages({ + name: { + id: 'admin.permissions.group.teams.name', + defaultMessage: 'Teams', + }, + description: { + id: 'admin.permissions.group.teams.description', + defaultMessage: 'Create teams and manage members.', + }, + }), + edit_posts: defineMessages({ + name: { + id: 'admin.permissions.group.edit_posts.name', + defaultMessage: 'Edit Posts', + }, + description: { + id: 'admin.permissions.group.edit_posts.description', + defaultMessage: 'Edit own and others\' posts.', + }, + }), + teams_team_scope: defineMessages({ + name: { + id: 'admin.permissions.group.teams_team_scope.name', + defaultMessage: 'Teams', + }, + description: { + id: 'admin.permissions.group.teams_team_scope.description', + defaultMessage: 'Manage team members.', + }, + }), + guest_reactions: defineMessages({ + name: { + id: 'admin.permissions.group.guest_reactions.name', + defaultMessage: 'Post Reactions', + }, + description: { + id: 'admin.permissions.group.guest_reactions.description', + defaultMessage: 'Add and delete reactions on posts.', + }, + }), + guest_create_post: defineMessages({ + name: { + id: 'admin.permissions.group.guest_create_post.name', + defaultMessage: 'Create Posts', + }, + description: { + id: 'admin.permissions.group.guest_create_post.description', + defaultMessage: 'Allow users to create posts.', + }, + }), + guest_create_private_channel: defineMessages({ + name: { + id: 'admin.permissions.group.guest_create_private_channel.name', + defaultMessage: 'Create Channels', + }, + description: { + id: 'admin.permissions.group.guest_create_private_channel.description', + defaultMessage: 'Create new private channels.', + }, + }), + guest_delete_post: defineMessages({ + name: { + id: 'admin.permissions.group.guest_delete_post.name', + defaultMessage: 'Delete Own Posts', + }, + description: { + id: 'admin.permissions.group.guest_delete_post.description', + defaultMessage: 'Author\'s own posts can be deleted.', + }, + }), + guest_edit_post: defineMessages({ + name: { + id: 'admin.permissions.group.guest_edit_post.name', + defaultMessage: 'Edit Own Posts', + }, + description: { + id: 'admin.permissions.group.guest_edit_post.description', + defaultMessage: '{editTimeLimitButton} after posting, allow users to edit their own posts.', + }, + }), + guest_use_channel_mentions: defineMessages({ + name: { + id: 'admin.permissions.group.guest_use_channel_mentions.name', + defaultMessage: 'Channel Mentions', + }, + description: { + id: 'admin.permissions.group.guest_use_channel_mentions.description', + defaultMessage: 'Notify channel members with @all, @channel and @here', + }, + }), + guest_use_group_mentions: defineMessages({ + name: { + id: 'admin.permissions.group.guest_use_group_mentions.name', + defaultMessage: 'Group Mentions', + }, + description: { + id: 'admin.permissions.group.guest_use_group_mentions.description', + defaultMessage: 'Notify group members with a group mention', + }, + }), + manage_private_channel_members_and_read_groups: defineMessages({ + name: { + id: 'admin.permissions.group.manage_private_channel_members_and_read_groups.name', + defaultMessage: 'Manage Channel Members', + }, + description: { + id: 'admin.permissions.group.manage_private_channel_members_and_read_groups.description', + defaultMessage: 'Add and remove private channel members.', + }, + }), + manage_public_channel_members_and_read_groups: defineMessages({ + name: { + id: 'admin.permissions.group.manage_public_channel_members_and_read_groups.name', + defaultMessage: 'Manage Channel Members', + }, + description: { + id: 'admin.permissions.group.manage_public_channel_members_and_read_groups.description', + defaultMessage: 'Add and remove public channel members.', + }, + }), + convert_public_channel_to_private: defineMessages({ + name: { + id: 'admin.permissions.group.convert_public_channel_to_private.name', + defaultMessage: 'Convert Channels', + }, + description: { + id: 'admin.permissions.group.convert_public_channel_to_private.description', + defaultMessage: 'Convert public channels to private', + }, + }), + manage_shared_channels: defineMessages({ + name: { + id: 'admin.permissions.group.manage_shared_channels.name', + defaultMessage: 'Shared Channels', + }, + description: { + id: 'admin.permissions.group.manage_shared_channels.description', + defaultMessage: 'Manage Shared Channels', + }, + }), + playbook_public: defineMessages({ + name: { + id: 'admin.permissions.group.playbook_public.name', + defaultMessage: 'Manage Public Playbooks', + }, + description: { + id: 'admin.permissions.group.playbook_public.description', + defaultMessage: 'Manage public playbooks.', + }, + }), + playbook_private: defineMessages({ + name: { + id: 'admin.permissions.group.playbook_private.name', + defaultMessage: 'Manage Private Playbooks', + }, + description: { + id: 'admin.permissions.group.playbook_private.description', + defaultMessage: 'Manage private playbooks.', + }, + }), + runs: defineMessages({ + name: { + id: 'admin.permissions.group.runs.name', + defaultMessage: 'Manage Runs', + }, + description: { + id: 'admin.permissions.group.runs.description', + defaultMessage: 'Manage runs.', + }, + }), + custom_groups: defineMessages({ + name: { + id: 'admin.permissions.group.custom_groups.name', + defaultMessage: 'Custom Groups', + }, + description: { + id: 'admin.permissions.group.custom_groups.description', + defaultMessage: 'Create, edit, delete and manage the members of custom groups.', + }, + }), +}; diff --git a/webapp/channels/src/components/admin_console/permission_schemes_settings/strings/permissions.tsx b/webapp/channels/src/components/admin_console/permission_schemes_settings/strings/permissions.tsx new file mode 100644 index 0000000000..65e4589660 --- /dev/null +++ b/webapp/channels/src/components/admin_console/permission_schemes_settings/strings/permissions.tsx @@ -0,0 +1,618 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import type {MessageDescriptor} from 'react-intl'; +import {defineMessages} from 'react-intl'; + +export const permissionRolesStrings: Record> = { + assign_system_admin_role: defineMessages({ + name: { + id: 'admin.permissions.permission.assign_system_admin_role.name', + defaultMessage: 'Assign system admin role', + }, + description: { + id: 'admin.permissions.permission.assign_system_admin_role.description', + defaultMessage: 'Assign system admin role', + }, + }), + convert_public_channel_to_private: defineMessages({ + name: { + id: 'admin.permissions.permission.convert_public_channel_to_private.name', + defaultMessage: 'Convert Channels', + }, + description: { + id: 'admin.permissions.permission.convert_public_channel_to_private.description', + defaultMessage: 'Convert public channels to private', + }, + }), + convert_private_channel_to_public: defineMessages({ + name: { + id: 'admin.permissions.permission.convert_private_channel_to_public.name', + defaultMessage: 'Convert Channels', + }, + description: { + id: 'admin.permissions.permission.convert_private_channel_to_public.description', + defaultMessage: 'Convert private channels to public', + }, + }), + create_direct_channel: defineMessages({ + name: { + id: 'admin.permissions.permission.create_direct_channel.name', + defaultMessage: 'Create direct channel', + }, + description: { + id: 'admin.permissions.permission.create_direct_channel.description', + defaultMessage: 'Create direct channel', + }, + }), + create_group_channel: defineMessages({ + name: { + id: 'admin.permissions.permission.create_group_channel.name', + defaultMessage: 'Create group channel', + }, + description: { + id: 'admin.permissions.permission.create_group_channel.description', + defaultMessage: 'Create group channel', + }, + }), + create_post: defineMessages({ + name: { + id: 'admin.permissions.permission.create_post.name', + defaultMessage: 'Create Posts', + }, + description: { + id: 'admin.permissions.permission.create_post.description', + defaultMessage: 'Allow users to create posts.', + }, + }), + create_private_channel: defineMessages({ + name: { + id: 'admin.permissions.permission.create_private_channel.name', + defaultMessage: 'Create Channels', + }, + description: { + id: 'admin.permissions.permission.create_private_channel.description', + defaultMessage: 'Create new private channels.', + }, + }), + create_public_channel: defineMessages({ + name: { + id: 'admin.permissions.permission.create_public_channel.name', + defaultMessage: 'Create Channels', + }, + description: { + id: 'admin.permissions.permission.create_public_channel.description', + defaultMessage: 'Create new public channels.', + }, + }), + create_team: defineMessages({ + name: { + id: 'admin.permissions.permission.create_team.name', + defaultMessage: 'Create Teams', + }, + description: { + id: 'admin.permissions.permission.create_team.description', + defaultMessage: 'Create new teams.', + }, + }), + create_user_access_token: defineMessages({ + name: { + id: 'admin.permissions.permission.create_user_access_token.name', + defaultMessage: 'Create user access token', + }, + description: { + id: 'admin.permissions.permission.create_user_access_token.description', + defaultMessage: 'Create user access token', + }, + }), + delete_others_posts: defineMessages({ + name: { + id: 'admin.permissions.permission.delete_others_posts.name', + defaultMessage: 'Delete Others\' Posts', + }, + description: { + id: 'admin.permissions.permission.delete_others_posts.description', + defaultMessage: 'Posts made by other users can be deleted.', + }, + }), + delete_post: defineMessages({ + name: { + id: 'admin.permissions.permission.delete_post.name', + defaultMessage: 'Delete Own Posts', + }, + description: { + id: 'admin.permissions.permission.delete_post.description', + defaultMessage: 'Author\'s own posts can be deleted.', + }, + }), + delete_private_channel: defineMessages({ + name: { + id: 'admin.permissions.permission.delete_private_channel.name', + defaultMessage: 'Archive Channels', + }, + description: { + id: 'admin.permissions.permission.delete_private_channel.description', + defaultMessage: 'Archive private channels.', + }, + }), + delete_public_channel: defineMessages({ + name: { + id: 'admin.permissions.permission.delete_public_channel.name', + defaultMessage: 'Archive Channels', + }, + description: { + id: 'admin.permissions.permission.delete_public_channel.description', + defaultMessage: 'Archive public channels.', + }, + }), + edit_other_users: defineMessages({ + name: { + id: 'admin.permissions.permission.edit_other_users.name', + defaultMessage: 'Edit other users', + }, + description: { + id: 'admin.permissions.permission.edit_other_users.description', + defaultMessage: 'Edit other users', + }, + }), + edit_post: defineMessages({ + name: { + id: 'admin.permissions.permission.edit_post.name', + defaultMessage: 'Edit Own Posts', + }, + description: { + id: 'admin.permissions.permission.edit_post.description', + defaultMessage: '{editTimeLimitButton} after posting, allow users to edit their own posts.', + }, + }), + import_team: defineMessages({ + name: { + id: 'admin.permissions.permission.import_team.name', + defaultMessage: 'Import team', + }, + description: { + id: 'admin.permissions.permission.import_team.description', + defaultMessage: 'Import team', + }, + }), + list_team_channels: defineMessages({ + name: { + id: 'admin.permissions.permission.list_team_channels.name', + defaultMessage: 'List team channels', + }, + description: { + id: 'admin.permissions.permission.list_team_channels.description', + defaultMessage: 'List team channels', + }, + }), + list_users_without_team: defineMessages({ + name: { + id: 'admin.permissions.permission.list_users_without_team.name', + defaultMessage: 'List users without team', + }, + description: { + id: 'admin.permissions.permission.list_users_without_team.description', + defaultMessage: 'List users without team', + }, + }), + manage_channel_roles: defineMessages({ + name: { + id: 'admin.permissions.permission.manage_channel_roles.name', + defaultMessage: 'Manage channel roles', + }, + description: { + id: 'admin.permissions.permission.manage_channel_roles.description', + defaultMessage: 'Manage channel roles', + }, + }), + create_emojis: defineMessages({ + name: { + id: 'admin.permissions.permission.create_emojis.name', + defaultMessage: 'Create Custom Emoji', + }, + description: { + id: 'admin.permissions.permission.create_emojis.description', + defaultMessage: 'Allow users to create custom emoji.', + }, + }), + delete_emojis: defineMessages({ + name: { + id: 'admin.permissions.permission.delete_emojis.name', + defaultMessage: 'Delete Own Custom Emoji', + }, + description: { + id: 'admin.permissions.permission.delete_emojis.description', + defaultMessage: 'Allow users to delete custom emoji that they created.', + }, + }), + delete_others_emojis: defineMessages({ + name: { + id: 'admin.permissions.permission.delete_others_emojis.name', + defaultMessage: 'Delete Others\' Custom Emoji', + }, + description: { + id: 'admin.permissions.permission.delete_others_emojis.description', + defaultMessage: 'Allow users to delete custom emoji that were created by other users.', + }, + }), + manage_jobs: defineMessages({ + name: { + id: 'admin.permissions.permission.manage_jobs.name', + defaultMessage: 'Manage jobs', + }, + description: { + id: 'admin.permissions.permission.manage_jobs.description', + defaultMessage: 'Manage jobs', + }, + }), + manage_oauth: defineMessages({ + name: { + id: 'admin.permissions.permission.manage_oauth.name', + defaultMessage: 'Manage OAuth Applications', + }, + description: { + id: 'admin.permissions.permission.manage_oauth.description', + defaultMessage: 'Create, edit and delete OAuth 2.0 application tokens.', + }, + }), + manage_private_channel_properties: defineMessages({ + name: { + id: 'admin.permissions.permission.manage_private_channel_properties.name', + defaultMessage: 'Manage Channel Settings', + }, + description: { + id: 'admin.permissions.permission.manage_private_channel_properties.description', + defaultMessage: 'Update private channel names, headers and purposes.', + }, + }), + manage_public_channel_properties: defineMessages({ + name: { + id: 'admin.permissions.permission.manage_public_channel_properties.name', + defaultMessage: 'Manage Channel Settings', + }, + description: { + id: 'admin.permissions.permission.manage_public_channel_properties.description', + defaultMessage: 'Update public channel names, headers and purposes.', + }, + }), + manage_roles: defineMessages({ + name: { + id: 'admin.permissions.permission.manage_roles.name', + defaultMessage: 'Manage roles', + }, + description: { + id: 'admin.permissions.permission.manage_roles.description', + defaultMessage: 'Manage roles', + }, + }), + manage_slash_commands: defineMessages({ + name: { + id: 'admin.permissions.permission.manage_slash_commands.name', + defaultMessage: 'Manage Slash Commands', + }, + description: { + id: 'admin.permissions.permission.manage_slash_commands.description', + defaultMessage: 'Create, edit and delete custom slash commands.', + }, + }), + manage_system: defineMessages({ + name: { + id: 'admin.permissions.permission.manage_system.name', + defaultMessage: 'Manage system', + }, + description: { + id: 'admin.permissions.permission.manage_system.description', + defaultMessage: 'Manage system', + }, + }), + manage_team: defineMessages({ + name: { + id: 'admin.permissions.permission.manage_team.name', + defaultMessage: 'Manage team', + }, + description: { + id: 'admin.permissions.permission.manage_team.description', + defaultMessage: 'Manage team', + }, + }), + manage_team_roles: defineMessages({ + name: { + id: 'admin.permissions.permission.manage_team_roles.name', + defaultMessage: 'Manage team roles', + }, + description: { + id: 'admin.permissions.permission.manage_team_roles.description', + defaultMessage: 'Manage team roles', + }, + }), + manage_incoming_webhooks: defineMessages({ + name: { + id: 'admin.permissions.permission.manage_incoming_webhooks.name', + defaultMessage: 'Manage Incoming Webhooks', + }, + description: { + id: 'admin.permissions.permission.manage_incoming_webhooks.description', + defaultMessage: 'Create, edit, and delete incoming webhooks.', + }, + }), + manage_outgoing_webhooks: defineMessages({ + name: { + id: 'admin.permissions.permission.manage_outgoing_webhooks.name', + defaultMessage: 'Manage Outgoing Webhooks', + }, + description: { + id: 'admin.permissions.permission.manage_outgoing_webhooks.description', + defaultMessage: 'Create, edit, and delete outgoing webhooks.', + }, + }), + permanent_delete_user: defineMessages({ + name: { + id: 'admin.permissions.permission.permanent_delete_user.name', + defaultMessage: 'Permanent delete user', + }, + description: { + id: 'admin.permissions.permission.permanent_delete_user.description', + defaultMessage: 'Permanent delete user', + }, + }), + read_channel: defineMessages({ + name: { + id: 'admin.permissions.permission.read_channel.name', + defaultMessage: 'Read channel', + }, + description: { + id: 'admin.permissions.permission.read_channel.description', + defaultMessage: 'Read channel', + }, + }), + read_user_access_token: defineMessages({ + name: { + id: 'admin.permissions.permission.read_user_access_token.name', + defaultMessage: 'Read user access token', + }, + description: { + id: 'admin.permissions.permission.read_user_access_token.description', + defaultMessage: 'Read user access token', + }, + }), + remove_user_from_team: defineMessages({ + name: { + id: 'admin.permissions.permission.remove_user_from_team.name', + defaultMessage: 'Remove user from team', + }, + description: { + id: 'admin.permissions.permission.remove_user_from_team.description', + defaultMessage: 'Remove user from team', + }, + }), + revoke_user_access_token: defineMessages({ + name: { + id: 'admin.permissions.permission.revoke_user_access_token.name', + defaultMessage: 'Revoke user access token', + }, + description: { + id: 'admin.permissions.permission.revoke_user_access_token.description', + defaultMessage: 'Revoke user access token', + }, + }), + upload_file: defineMessages({ + name: { + id: 'admin.permissions.permission.upload_file.name', + defaultMessage: 'Upload file', + }, + description: { + id: 'admin.permissions.permission.upload_file.description', + defaultMessage: 'Upload file', + }, + }), + use_channel_mentions: defineMessages({ + name: { + id: 'admin.permissions.permission.use_channel_mentions.name', + defaultMessage: 'Channel Mentions', + }, + description: { + id: 'admin.permissions.permission.use_channel_mentions.description', + defaultMessage: 'Notify channel members with @all, @channel and @here', + }, + }), + use_group_mentions: defineMessages({ + name: { + id: 'admin.permissions.permission.use_group_mentions.name', + defaultMessage: 'Group Mentions', + }, + description: { + id: 'admin.permissions.permission.use_group_mentions.description', + defaultMessage: 'Notify group members with a group mention', + }, + }), + view_team: defineMessages({ + name: { + id: 'admin.permissions.permission.view_team.name', + defaultMessage: 'View team', + }, + description: { + id: 'admin.permissions.permission.view_team.description', + defaultMessage: 'View team', + }, + }), + edit_others_posts: defineMessages({ + name: { + id: 'admin.permissions.permission.edit_others_posts.name', + defaultMessage: 'Edit Others\' Posts', + }, + description: { + id: 'admin.permissions.permission.edit_others_posts.description', + defaultMessage: 'Allow users to edit others\' posts.', + }, + }), + invite_guest: defineMessages({ + name: { + id: 'admin.permissions.permission.invite_guest.name', + defaultMessage: 'Invite guests', + }, + description: { + id: 'admin.permissions.permission.invite_guest.description', + defaultMessage: 'Invite guests to channels and send guest email invites.', + }, + }), + manage_shared_channels: defineMessages({ + name: { + id: 'admin.permissions.permission.manage_shared_channels.name', + defaultMessage: 'Manage Shared Channels', + }, + description: { + id: 'admin.permissions.permission.manage_shared_channels.description', + defaultMessage: 'Share, unshare and invite another instance to sync with a shared channel', + }, + }), + manage_secure_connections: defineMessages({ + name: { + id: 'admin.permissions.permission.manage_secure_connections.name', + defaultMessage: 'Manage Secure Connections', + }, + description: { + id: 'admin.permissions.permission.manage_secure_connections.description', + defaultMessage: 'Create, remove and view secure connections for shared channels', + }, + }), + playbook_public_create: defineMessages({ + name: { + id: 'admin.permissions.permission.playbook_public_create.name', + defaultMessage: 'Create Public Playbook', + }, + description: { + id: 'admin.permissions.permission.playbook_public_create.description', + defaultMessage: 'Create new public playbooks.', + }, + }), + playbook_public_manage_properties: defineMessages({ + name: { + id: 'admin.permissions.permission.playbook_public_manage_properties.name', + defaultMessage: 'Manage Playbook Configurations', + }, + description: { + id: 'admin.permissions.permission.playbook_public_manage_properties.description', + defaultMessage: 'Prescribe checklists, actions, and templates.', + }, + }), + playbook_public_manage_members: defineMessages({ + name: { + id: 'admin.permissions.permission.playbook_public_manage_members.name', + defaultMessage: 'Manage Playbook Members', + }, + description: { + id: 'admin.permissions.permission.playbook_public_manage_members.description', + defaultMessage: 'Add and remove public playbook members.', + }, + }), + playbook_public_make_private: defineMessages({ + name: { + id: 'admin.permissions.permission.playbook_public_make_private.name', + defaultMessage: 'Convert Playbooks', + }, + description: { + id: 'admin.permissions.permission.playbook_public_make_private.description', + defaultMessage: 'Convert public playbooks to private.', + }, + }), + playbook_private_create: defineMessages({ + name: { + id: 'admin.permissions.permission.playbook_private_create.name', + defaultMessage: 'Create Private Playbook', + }, + description: { + id: 'admin.permissions.permission.playbook_private_create.description', + defaultMessage: 'Create new private playbooks.', + }, + }), + playbook_private_manage_properties: defineMessages({ + name: { + id: 'admin.permissions.permission.playbook_private_manage_properties.name', + defaultMessage: 'Manage Playbook Configurations', + }, + description: { + id: 'admin.permissions.permission.playbook_private_manage_properties.description', + defaultMessage: 'Prescribe checklists, actions, and templates.', + }, + }), + playbook_private_manage_members: defineMessages({ + name: { + id: 'admin.permissions.permission.playbook_private_manage_members.name', + defaultMessage: 'Manage Playbook Members', + }, + description: { + id: 'admin.permissions.permission.playbook_private_manage_members.description', + defaultMessage: 'Add and remove private playbook members.', + }, + }), + playbook_private_make_public: defineMessages({ + name: { + id: 'admin.permissions.permission.playbook_private_make_public.name', + defaultMessage: 'Convert Playbooks', + }, + description: { + id: 'admin.permissions.permission.playbook_private_make_public.description', + defaultMessage: 'Convert private playbooks to public.', + }, + }), + run_create: defineMessages({ + name: { + id: 'admin.permissions.permission.run_create.name', + defaultMessage: 'Create Runs', + }, + description: { + id: 'admin.permissions.permission.run_create.description', + defaultMessage: 'Run playbooks.', + }, + }), + create_custom_group: defineMessages({ + name: { + id: 'admin.permissions.permission.create_custom_group.name', + defaultMessage: 'Create', + }, + description: { + id: 'admin.permissions.permission.create_custom_group.description', + defaultMessage: 'Create custom groups.', + }, + }), + manage_custom_group_members: defineMessages({ + name: { + id: 'admin.permissions.permission.manage_custom_group_members.name', + defaultMessage: 'Manage members', + }, + description: { + id: 'admin.permissions.permission.manage_custom_group_members.description', + defaultMessage: 'Add and remove custom group members.', + }, + }), + delete_custom_group: defineMessages({ + name: { + id: 'admin.permissions.permission.delete_custom_group.name', + defaultMessage: 'Delete', + }, + description: { + id: 'admin.permissions.permission.delete_custom_group.description', + defaultMessage: 'Delete custom groups.', + }, + }), + restore_custom_group: defineMessages({ + name: { + id: 'admin.permissions.permission.restore_custom_group.name', + defaultMessage: 'Restore', + }, + description: { + id: 'admin.permissions.permission.restore_custom_group.description', + defaultMessage: 'Restore archived user groups.', + }, + }), + edit_custom_group: defineMessages({ + name: { + id: 'admin.permissions.permission.edit_custom_group.name', + defaultMessage: 'Edit', + }, + description: { + id: 'admin.permissions.permission.edit_custom_group.description', + defaultMessage: 'Rename custom groups.', + }, + }), +}; diff --git a/webapp/channels/src/components/admin_console/permission_schemes_settings/strings/roles.tsx b/webapp/channels/src/components/admin_console/permission_schemes_settings/strings/roles.tsx new file mode 100644 index 0000000000..930ca015f2 --- /dev/null +++ b/webapp/channels/src/components/admin_console/permission_schemes_settings/strings/roles.tsx @@ -0,0 +1,36 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import type {MessageDescriptor} from 'react-intl'; +import {defineMessages} from 'react-intl'; + +export const rolesRolesStrings: Record = defineMessages({ + all_users: { + id: 'admin.permissions.roles.all_users.name', + defaultMessage: 'All Members', + }, + channel_admin: { + id: 'admin.permissions.roles.channel_admin.name', + defaultMessage: 'Channel Admin', + }, + channel_user: { + id: 'admin.permissions.roles.channel_user.name', + defaultMessage: 'Channel User', + }, + system_admin: { + id: 'admin.permissions.roles.system_admin.name', + defaultMessage: 'System Admin', + }, + system_user: { + id: 'admin.permissions.roles.system_user.name', + defaultMessage: 'System User', + }, + team_admin: { + id: 'admin.permissions.roles.team_admin.name', + defaultMessage: 'Team Admin', + }, + team_user: { + id: 'admin.permissions.roles.team_user.name', + defaultMessage: 'Team User', + }, +}); diff --git a/webapp/channels/src/components/admin_console/plugin_management/__snapshots__/plugin_management.test.tsx.snap b/webapp/channels/src/components/admin_console/plugin_management/__snapshots__/plugin_management.test.tsx.snap index 191bc42158..25e3a740a7 100644 --- a/webapp/channels/src/components/admin_console/plugin_management/__snapshots__/plugin_management.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/plugin_management/__snapshots__/plugin_management.test.tsx.snap @@ -106,7 +106,7 @@ exports[`components/PluginManagement should match snapshot 1`] = ` /> } helpText={ - @@ -260,7 +260,7 @@ exports[`components/PluginManagement should match snapshot 1`] = ` disabled={false} helpText={
- @@ -454,7 +454,7 @@ exports[`components/PluginManagement should match snapshot when \`Enable Marketp /> } helpText={ - @@ -608,7 +608,7 @@ exports[`components/PluginManagement should match snapshot when \`Enable Marketp disabled={true} helpText={
- @@ -898,7 +898,7 @@ exports[`components/PluginManagement should match snapshot when \`Enable Remote /> } helpText={ - @@ -1052,7 +1052,7 @@ exports[`components/PluginManagement should match snapshot when \`Enable Remote disabled={true} helpText={
- @@ -1246,7 +1246,7 @@ exports[`components/PluginManagement should match snapshot when \`Require Signat /> } helpText={ - @@ -1402,7 +1402,7 @@ exports[`components/PluginManagement should match snapshot when \`Require Signat disabled={false} helpText={
- @@ -1596,7 +1596,7 @@ exports[`components/PluginManagement should match snapshot, No installed plugins /> } helpText={ - @@ -1750,7 +1750,7 @@ exports[`components/PluginManagement should match snapshot, No installed plugins disabled={false} helpText={
- @@ -1948,7 +1948,7 @@ exports[`components/PluginManagement should match snapshot, allow insecure URL e /> } helpText={ - @@ -2102,7 +2102,7 @@ exports[`components/PluginManagement should match snapshot, allow insecure URL e disabled={false} helpText={
- @@ -2296,7 +2296,7 @@ exports[`components/PluginManagement should match snapshot, disabled 1`] = ` /> } helpText={ - @@ -2452,7 +2452,7 @@ exports[`components/PluginManagement should match snapshot, disabled 1`] = ` disabled={true} helpText={
- @@ -2621,7 +2621,7 @@ exports[`components/PluginManagement should match snapshot, text entered into th /> } helpText={ - @@ -2775,7 +2775,7 @@ exports[`components/PluginManagement should match snapshot, text entered into th disabled={false} helpText={
- @@ -2969,7 +2969,7 @@ exports[`components/PluginManagement should match snapshot, upload disabled 1`] /> } helpText={ - @@ -3324,7 +3324,7 @@ exports[`components/PluginManagement should match snapshot, with installed plugi /> } helpText={ - @@ -3478,7 +3478,7 @@ exports[`components/PluginManagement should match snapshot, with installed plugi disabled={false} helpText={
- @@ -3735,7 +3735,7 @@ exports[`components/PluginManagement should match snapshot, with installed plugi /> } helpText={ - @@ -3889,7 +3889,7 @@ exports[`components/PluginManagement should match snapshot, with installed plugi disabled={false} helpText={
- @@ -4114,7 +4114,7 @@ exports[`components/PluginManagement should match snapshot, with installed plugi /> } helpText={ - @@ -4268,7 +4268,7 @@ exports[`components/PluginManagement should match snapshot, with installed plugi disabled={false} helpText={
- @@ -4493,7 +4493,7 @@ exports[`components/PluginManagement should match snapshot, with installed plugi /> } helpText={ - @@ -4647,7 +4647,7 @@ exports[`components/PluginManagement should match snapshot, with installed plugi disabled={false} helpText={
- @@ -4872,7 +4872,7 @@ exports[`components/PluginManagement should match snapshot, with installed plugi /> } helpText={ - @@ -5026,7 +5026,7 @@ exports[`components/PluginManagement should match snapshot, with installed plugi disabled={false} helpText={
- diff --git a/webapp/channels/src/components/admin_console/plugin_management/plugin_management.test.tsx b/webapp/channels/src/components/admin_console/plugin_management/plugin_management.test.tsx index 8653eed6f5..3138f9f267 100644 --- a/webapp/channels/src/components/admin_console/plugin_management/plugin_management.test.tsx +++ b/webapp/channels/src/components/admin_console/plugin_management/plugin_management.test.tsx @@ -1,13 +1,14 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {shallow} from 'enzyme'; import React from 'react'; import PluginState from 'mattermost-redux/constants/plugins'; import PluginManagement from 'components/admin_console/plugin_management/plugin_management'; +import {shallowWithIntl} from 'tests/helpers/intl-test-helper'; + describe('components/PluginManagement', () => { const defaultProps = { config: { @@ -104,7 +105,7 @@ describe('components/PluginManagement', () => { test('should match snapshot', () => { const props = {...defaultProps}; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); @@ -119,7 +120,7 @@ describe('components/PluginManagement', () => { }, }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); @@ -133,7 +134,7 @@ describe('components/PluginManagement', () => { }, }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); @@ -148,7 +149,7 @@ describe('components/PluginManagement', () => { }, }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); @@ -163,7 +164,7 @@ describe('components/PluginManagement', () => { }, }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); @@ -178,7 +179,7 @@ describe('components/PluginManagement', () => { }, }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); @@ -193,7 +194,7 @@ describe('components/PluginManagement', () => { }, }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); @@ -208,14 +209,14 @@ describe('components/PluginManagement', () => { }, }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); test('should match snapshot, text entered into the URL install text box', () => { const props = defaultProps; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); wrapper.setState({pluginDownloadUrl: 'https://pluginsite.com/plugin.tar.gz'}); expect(wrapper).toMatchSnapshot(); }); @@ -244,13 +245,13 @@ describe('components/PluginManagement', () => { disablePlugin: jest.fn(), }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); wrapper.setState({loading: false}); expect(wrapper).toMatchSnapshot(); }); test('should match snapshot, with installed plugins', () => { - const wrapper = shallow(); + const wrapper = shallowWithIntl(); wrapper.setState({loading: false}); expect(wrapper).toMatchSnapshot(); }); @@ -334,7 +335,7 @@ describe('components/PluginManagement', () => { disablePlugin: jest.fn(), }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); wrapper.setState({loading: false}); expect(wrapper).toMatchSnapshot(); }); @@ -391,7 +392,7 @@ describe('components/PluginManagement', () => { disablePlugin: jest.fn(), }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); wrapper.setState({loading: false}); expect(wrapper).toMatchSnapshot(); }); @@ -449,7 +450,7 @@ describe('components/PluginManagement', () => { disablePlugin: jest.fn(), }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); wrapper.setState({loading: false}); expect(wrapper).toMatchSnapshot(); }); @@ -508,7 +509,7 @@ describe('components/PluginManagement', () => { disablePlugin: jest.fn(), }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); wrapper.setState({loading: false}); expect(wrapper).toMatchSnapshot(); }); diff --git a/webapp/channels/src/components/admin_console/plugin_management/plugin_management.tsx b/webapp/channels/src/components/admin_console/plugin_management/plugin_management.tsx index 4fa8ad4825..6be664a4cc 100644 --- a/webapp/channels/src/components/admin_console/plugin_management/plugin_management.tsx +++ b/webapp/channels/src/components/admin_console/plugin_management/plugin_management.tsx @@ -3,7 +3,8 @@ import classNames from 'classnames'; import React from 'react'; -import {FormattedMessage} from 'react-intl'; +import type {WrappedComponentProps} from 'react-intl'; +import {FormattedMessage, defineMessages, injectIntl} from 'react-intl'; import {Link} from 'react-router-dom'; import type {AdminConfig} from '@mattermost/types/config'; @@ -186,6 +187,44 @@ type PluginItemProps = { isDisabled?: boolean; }; +const messages = defineMessages({ + title: {id: 'admin.plugin.management.title', defaultMessage: 'Management'}, + enable: {id: 'admin.plugins.settings.enable', defaultMessage: 'Enable Plugins: '}, + enableDesc: {id: 'admin.plugins.settings.enableDesc', defaultMessage: 'When true, enables plugins on your Mattermost server. Use plugins to integrate with third-party systems, extend functionality, or customize the user interface of your Mattermost server. See documentation to learn more.'}, + uploadTitle: {id: 'admin.plugin.uploadTitle', defaultMessage: 'Upload Plugin: '}, + installedTitle: {id: 'admin.plugin.installedTitle', defaultMessage: 'Installed Plugins: '}, + installedDesc: {id: 'admin.plugin.installedDesc', defaultMessage: 'Installed plugins on your Mattermost server.'}, + uploadDesc: {id: 'admin.plugin.uploadDesc', defaultMessage: 'Upload a plugin for your Mattermost server. See documentation to learn more.'}, + uploadDisabledDesc: {id: 'admin.plugin.uploadDisabledDesc', defaultMessage: 'Enable plugin uploads in config.json. See documentation to learn more.'}, + enableMarketplace: {id: 'admin.plugins.settings.enableMarketplace', defaultMessage: 'Enable Marketplace:'}, + enableMarketplaceDesc: {id: 'admin.plugins.settings.enableMarketplaceDesc', defaultMessage: 'When true, enables System Administrators to install plugins from the marketplace.'}, + enableRemoteMarketplace: {id: 'admin.plugins.settings.enableRemoteMarketplace', defaultMessage: 'Enable Remote Marketplace:'}, + enableRemoteMarketplaceDesc: {id: 'admin.plugins.settings.enableRemoteMarketplaceDesc', defaultMessage: 'When true, marketplace fetches latest plugins from the configured Marketplace URL.'}, + automaticPrepackagedPlugins: {id: 'admin.plugins.settings.automaticPrepackagedPlugins', defaultMessage: 'Enable Automatic Prepackaged Plugins:'}, + automaticPrepackagedPluginsDesc: {id: 'admin.plugins.settings.automaticPrepackagedPluginsDesc', defaultMessage: 'When true, automatically installs any prepackaged plugin found to be enabled in the server configuration.'}, + marketplaceUrl: {id: 'admin.plugins.settings.marketplaceUrl', defaultMessage: 'Marketplace URL:'}, + marketplaceUrlDesc: {id: 'admin.plugins.settings.marketplaceUrlDesc', defaultMessage: 'URL of the marketplace server.'}, +}); + +export const searchableStrings = [ + messages.title, + messages.enable, + messages.enableDesc, + messages.uploadTitle, + messages.installedTitle, + messages.installedDesc, + messages.uploadDesc, + messages.uploadDisabledDesc, + messages.enableMarketplace, + messages.enableMarketplaceDesc, + messages.enableRemoteMarketplace, + messages.enableRemoteMarketplaceDesc, + messages.automaticPrepackagedPlugins, + messages.automaticPrepackagedPluginsDesc, + messages.marketplaceUrl, + messages.marketplaceUrlDesc, +]; + const PluginItem = ({ pluginStatus, removing, @@ -429,7 +468,7 @@ type Props = BaseProps & { disablePlugin: (pluginId: string) => any; installPluginFromUrl: (url: string, force: boolean) => any; }; -}; +} & WrappedComponentProps; type State = BaseState & { loading: boolean; @@ -456,7 +495,7 @@ type State = BaseState & { requirePluginSignature: boolean; removing: string | null; } -export default class PluginManagement extends AdminSettings { +class PluginManagement extends AdminSettings { private fileInput: React.RefObject; constructor(props: Props) { super(props); @@ -540,9 +579,9 @@ export default class PluginManagement extends AdminSettings { uploading: false, }); if (error.server_error_id === 'app.plugin.activate.app_error') { - this.setState({serverError: Utils.localizeMessage('admin.plugin.error.activate', 'Unable to upload the plugin. It may conflict with another plugin on your server.')}); + this.setState({serverError: this.props.intl.formatMessage({id: 'admin.plugin.error.activate', defaultMessage: 'Unable to upload the plugin. It may conflict with another plugin on your server.'})}); } else if (error.server_error_id === 'app.plugin.extract.app_error') { - this.setState({serverError: Utils.localizeMessage('admin.plugin.error.extract', 'Encountered an error when extracting the plugin. Review your plugin file content and try again.')}); + this.setState({serverError: this.props.intl.formatMessage({id: 'admin.plugin.error.extract', defaultMessage: 'Encountered an error when extracting the plugin. Review your plugin file content and try again.'})}); } else { this.setState({serverError: error.message}); } @@ -628,7 +667,7 @@ export default class PluginManagement extends AdminSettings { }); if (error.server_error_id === 'app.plugin.extract.app_error') { - this.setState({serverError: Utils.localizeMessage('admin.plugin.error.extract', 'Encountered an error when extracting the plugin. Review your plugin file content and try again.')}); + this.setState({serverError: this.props.intl.formatMessage({id: 'admin.plugin.error.extract', defaultMessage: 'Encountered an error when extracting the plugin. Review your plugin file content and try again.'})}); } else { this.setState({serverError: error.message}); } @@ -659,7 +698,7 @@ export default class PluginManagement extends AdminSettings { url === '' && enableUploads &&
- @@ -667,16 +706,12 @@ export default class PluginManagement extends AdminSettings { } { url !== '' && enableUploads && - + } { !enableUploads && ( { }; renderTitle() { - return ( - - ); + return (); } renderOverwritePluginModal = ( @@ -868,16 +898,10 @@ export default class PluginManagement extends AdminSettings { return ( - } + label={} helpText={ ( { if (enable) { pluginsContainer = (
-
} - className="" - subtitleDefault="Teams to which this user belongs" - subtitleId="admin.userManagement.userDetail.teamsSubtitle" - titleDefault="Team Membership" - titleId="admin.userManagement.userDetail.teamsTitle" + subtitle={ + Object { + "defaultMessage": "Teams to which this user belongs", + "id": "admin.userManagement.userDetail.teamsSubtitle", + } + } + title={ + Object { + "defaultMessage": "Team Membership", + "id": "admin.userManagement.userDetail.teamsTitle", + } + } >
} - className="" - subtitleDefault="Teams to which this user belongs" - subtitleId="admin.userManagement.userDetail.teamsSubtitle" - titleDefault="Team Membership" - titleId="admin.userManagement.userDetail.teamsTitle" + subtitle={ + Object { + "defaultMessage": "Teams to which this user belongs", + "id": "admin.userManagement.userDetail.teamsSubtitle", + } + } + title={ + Object { + "defaultMessage": "Team Membership", + "id": "admin.userManagement.userDetail.teamsTitle", + } + } >
} - className="" - subtitleDefault="Teams to which this user belongs" - subtitleId="admin.userManagement.userDetail.teamsSubtitle" - titleDefault="Team Membership" - titleId="admin.userManagement.userDetail.teamsTitle" + subtitle={ + Object { + "defaultMessage": "Teams to which this user belongs", + "id": "admin.userManagement.userDetail.teamsSubtitle", + } + } + title={ + Object { + "defaultMessage": "Team Membership", + "id": "admin.userManagement.userDetail.teamsTitle", + } + } >
} - className="" - subtitleDefault="Teams to which this user belongs" - subtitleId="admin.userManagement.userDetail.teamsSubtitle" - titleDefault="Team Membership" - titleId="admin.userManagement.userDetail.teamsTitle" + subtitle={ + Object { + "defaultMessage": "Teams to which this user belongs", + "id": "admin.userManagement.userDetail.teamsSubtitle", + } + } + title={ + Object { + "defaultMessage": "Team Membership", + "id": "admin.userManagement.userDetail.teamsTitle", + } + } >
} - className="" - subtitleDefault="Teams to which this user belongs" - subtitleId="admin.userManagement.userDetail.teamsSubtitle" - titleDefault="Team Membership" - titleId="admin.userManagement.userDetail.teamsTitle" + subtitle={ + Object { + "defaultMessage": "Teams to which this user belongs", + "id": "admin.userManagement.userDetail.teamsSubtitle", + } + } + title={ + Object { + "defaultMessage": "Team Membership", + "id": "admin.userManagement.userDetail.teamsTitle", + } + } > { test('should match default snapshot', () => { const props = defaultProps; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); @@ -40,7 +40,7 @@ describe('components/admin_console/system_user_detail', () => { id: null, }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); @@ -52,7 +52,7 @@ describe('components/admin_console/system_user_detail', () => { delete_at: 1561683854166, }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); @@ -65,7 +65,7 @@ describe('components/admin_console/system_user_detail', () => { }, mfaEnabled: true, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); @@ -77,7 +77,7 @@ describe('components/admin_console/system_user_detail', () => { nickname: null, }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); @@ -89,7 +89,7 @@ describe('components/admin_console/system_user_detail', () => { auth_service: Constants.LDAP_SERVICE, }, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); @@ -102,7 +102,7 @@ describe('components/admin_console/system_user_detail', () => { }, mfaEnabled: true, }; - const wrapper = shallow(); + const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); }); }); diff --git a/webapp/channels/src/components/admin_console/system_user_detail/system_user_detail.tsx b/webapp/channels/src/components/admin_console/system_user_detail/system_user_detail.tsx index a518b5e8fa..bfe142ff51 100644 --- a/webapp/channels/src/components/admin_console/system_user_detail/system_user_detail.tsx +++ b/webapp/channels/src/components/admin_console/system_user_detail/system_user_detail.tsx @@ -3,7 +3,8 @@ import React from 'react'; import {Overlay} from 'react-bootstrap'; -import {FormattedMessage} from 'react-intl'; +import type {WrappedComponentProps} from 'react-intl'; +import {FormattedMessage, defineMessage, injectIntl} from 'react-intl'; import {Redirect} from 'react-router-dom'; import type {RouteComponentProps} from 'react-router-dom'; @@ -33,7 +34,6 @@ import EmailIcon from 'components/widgets/icons/email_icon'; import SheidOutlineIcon from 'components/widgets/icons/shield_outline_icon'; import {Constants} from 'utils/constants'; -import {t} from 'utils/i18n'; import * as Utils from 'utils/utils'; import './system_user_detail.scss'; @@ -47,7 +47,7 @@ export type Props = { setNavigationBlocked: (blocked: boolean) => void; addUserToTeam: (teamId: string, userId: string) => Promise; }; -} +} & WrappedComponentProps; export type State = { teams: TeamMembership[]; @@ -67,7 +67,7 @@ export type State = { error: ServerError | null; } -export default class SystemUserDetail extends React.PureComponent { +class SystemUserDetail extends React.PureComponent { errorMessageRef: React.RefObject; errorMessageRefCurrent: React.ReactInstance | undefined; @@ -289,7 +289,7 @@ export default class SystemUserDetail extends React.PureComponent - {Utils.localizeMessage('admin.user_item.makeActive', 'Activate')} + {this.props.intl.formatMessage({id: 'admin.user_item.makeActive', defaultMessage: 'Activate'})} ); } @@ -299,7 +299,7 @@ export default class SystemUserDetail extends React.PureComponent - {Utils.localizeMessage('admin.user_item.makeInactive', 'Deactivate')} + {this.props.intl.formatMessage({id: 'admin.user_item.makeInactive', defaultMessage: 'Deactivate'})} ); }; @@ -312,7 +312,7 @@ export default class SystemUserDetail extends React.PureComponent - {Utils.localizeMessage('admin.user_item.resetMfa', 'Remove MFA')} + {this.props.intl.formatMessage({id: 'admin.user_item.resetMfa', defaultMessage: 'Remove MFA'})} ); } @@ -332,12 +332,12 @@ export default class SystemUserDetail extends React.PureComponent {user.position} - {Utils.localizeMessage('admin.userManagement.userDetail.email', 'Email')} + {this.props.intl.formatMessage({id: 'admin.userManagement.userDetail.email', defaultMessage: 'Email'})}
- {Utils.localizeMessage('admin.userManagement.userDetail.username', 'Username')} + {this.props.intl.formatMessage({id: 'admin.userManagement.userDetail.username', defaultMessage: 'Username'})}
{user.username}
- {Utils.localizeMessage('admin.userManagement.userDetail.authenticationMethod', 'Authentication Method')} + {this.props.intl.formatMessage({id: 'admin.userManagement.userDetail.authenticationMethod', defaultMessage: 'Authentication Method'})}
{this.getAuthenticationText()} @@ -414,7 +414,7 @@ export default class SystemUserDetail extends React.PureComponent - {Utils.localizeMessage('admin.user_item.resetPwd', 'Reset Password')} + {this.props.intl.formatMessage({id: 'admin.user_item.resetPwd', defaultMessage: 'Reset Password'})} {this.renderActivateDeactivate()} {this.renderRemoveMFA()} @@ -422,10 +422,8 @@ export default class SystemUserDetail extends React.PureComponent
diff --git a/webapp/channels/src/components/admin_console/team_channel_settings/channel/list/channel_list.tsx b/webapp/channels/src/components/admin_console/team_channel_settings/channel/list/channel_list.tsx index 3e9ae83927..333c103200 100644 --- a/webapp/channels/src/components/admin_console/team_channel_settings/channel/list/channel_list.tsx +++ b/webapp/channels/src/components/admin_console/team_channel_settings/channel/list/channel_list.tsx @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import React from 'react'; -import {FormattedMessage} from 'react-intl'; +import {FormattedMessage, defineMessages} from 'react-intl'; import {Link} from 'react-router-dom'; import type {ChannelWithTeamData, ChannelSearchOpts} from '@mattermost/types/channels'; @@ -35,10 +35,6 @@ export interface ChannelListProps { }; data: ChannelWithTeamData[]; total: number; - removeGroup?: () => void; - emptyListTextId?: string; - emptyListTextDefaultMessage?: string; - isDisabled?: boolean; } interface ChannelListState { @@ -53,6 +49,17 @@ interface ChannelListState { const ROW_HEIGHT = 40; +const messages = defineMessages({ + group: { + id: 'admin.channel_settings.channel_row.managementMethod.group', + defaultMessage: 'Group Sync', + }, + manual: { + id: 'admin.channel_settings.channel_row.managementMethod.manual', + defaultMessage: 'Manual Invites', + }, +}); + export default class ChannelList extends React.PureComponent { constructor(props: ChannelListProps) { super(props); @@ -226,10 +233,7 @@ export default class ChannelList extends React.PureComponent - + ), edit: ( diff --git a/webapp/channels/src/components/admin_console/team_channel_settings/team/__snapshots__/team_settings.test.tsx.snap b/webapp/channels/src/components/admin_console/team_channel_settings/team/__snapshots__/team_settings.test.tsx.snap index ea945ccd47..626cbc01d3 100644 --- a/webapp/channels/src/components/admin_console/team_channel_settings/team/__snapshots__/team_settings.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/team_channel_settings/team/__snapshots__/team_settings.test.tsx.snap @@ -22,12 +22,19 @@ exports[`admin_console/team_channel_settings/team/TeamSettings should match snap className="admin-console__content" > diff --git a/webapp/channels/src/components/admin_console/team_channel_settings/team/details/__snapshots__/team_groups.test.tsx.snap b/webapp/channels/src/components/admin_console/team_channel_settings/team/details/__snapshots__/team_groups.test.tsx.snap index a110642832..96a4aaccf6 100644 --- a/webapp/channels/src/components/admin_console/team_channel_settings/team/details/__snapshots__/team_groups.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/team_channel_settings/team/details/__snapshots__/team_groups.test.tsx.snap @@ -63,12 +63,19 @@ exports[`admin_console/team_channel_settings/team/TeamGroups should match snapsh /> } - className="" id="team_groups" - subtitleDefault="Add and remove team members based on their group membership." - subtitleId="admin.team_settings.team_detail.syncedGroupsDescription" - titleDefault="Synced Groups" - titleId="admin.team_settings.team_detail.syncedGroupsTitle" + subtitle={ + Object { + "defaultMessage": "Add and remove team members based on their group membership.", + "id": "admin.team_settings.team_detail.syncedGroupsDescription", + } + } + title={ + Object { + "defaultMessage": "Synced Groups", + "id": "admin.team_settings.team_detail.syncedGroupsTitle", + } + } >
( } - className="" id="teamMembers" - subtitleDefault="A list of users who are currently in the team right now" - subtitleId="admin.team_settings.team_detail.membersDescription" - titleDefault="Members" - titleId="admin.team_settings.team_detail.membersTitle" + subtitle={ + Object { + "defaultMessage": "A list of users who are currently in the team right now", + "id": "admin.team_settings.team_detail.membersDescription", + } + } + title={ + Object { + "defaultMessage": "Members", + "id": "admin.team_settings.team_detail.membersTitle", + } + } > } - className="" id="teamMembers" - subtitleDefault="A list of users who are currently in the team right now" - subtitleId="admin.team_settings.team_detail.membersDescription" - titleDefault="Members" - titleId="admin.team_settings.team_detail.membersTitle" + subtitle={ + Object { + "defaultMessage": "A list of users who are currently in the team right now", + "id": "admin.team_settings.team_detail.membersDescription", + } + } + title={ + Object { + "defaultMessage": "Members", + "id": "admin.team_settings.team_detail.membersTitle", + } + } > { return ( (
diff --git a/webapp/channels/src/components/admin_console/team_channel_settings/team/details/team_profile.tsx b/webapp/channels/src/components/admin_console/team_channel_settings/team/details/team_profile.tsx index b6b9600bb5..70c7bdc0b5 100644 --- a/webapp/channels/src/components/admin_console/team_channel_settings/team/details/team_profile.tsx +++ b/webapp/channels/src/components/admin_console/team_channel_settings/team/details/team_profile.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames'; import {noop} from 'lodash'; import React, {useEffect, useState} from 'react'; -import {FormattedMessage} from 'react-intl'; +import {FormattedMessage, defineMessage, useIntl} from 'react-intl'; import {useDispatch, useSelector} from 'react-redux'; import type {Team} from '@mattermost/types/teams'; @@ -23,8 +23,7 @@ import AdminPanel from 'components/widgets/admin_console/admin_panel'; import TeamIcon from 'components/widgets/team_icon/team_icon'; import {ModalIdentifiers} from 'utils/constants'; -import {t} from 'utils/i18n'; -import {imageURLForTeam, localizeMessage} from 'utils/utils'; +import {imageURLForTeam} from 'utils/utils'; import './team_profile.scss'; @@ -42,6 +41,7 @@ export function TeamProfile({team, isArchived, onToggleArchive, isDisabled, save const dispatch = useDispatch(); const usage = useGetUsage(); const license = useSelector(getLicense); + const intl = useIntl(); const [overrideRestoreDisabled, setOverrideRestoreDisabled] = useState(false); const [restoreDisabled, setRestoreDisabled] = useState(usageDeltas.teams.teamsLoaded && usageDeltas.teams.active >= 0 && isArchived); @@ -55,15 +55,9 @@ export function TeamProfile({team, isArchived, onToggleArchive, isDisabled, save return null;// } - let archiveBtnID: string; - let archiveBtnDefault: string; - if (isArchived) { - archiveBtnID = t('admin.team_settings.team_details.unarchiveTeam'); - archiveBtnDefault = 'Unarchive Team'; - } else { - archiveBtnID = t('admin.team_settings.team_details.archiveTeam'); - archiveBtnDefault = 'Archive Team'; - } + const archiveBtn = isArchived ? + defineMessage({id: 'admin.team_settings.team_details.unarchiveTeam', defaultMessage: 'Unarchive Team'}) : + defineMessage({id: 'admin.team_settings.team_details.archiveTeam', defaultMessage: 'Archive Team'}); const toggleArchive = () => { setOverrideRestoreDisabled(true); @@ -119,10 +113,7 @@ export function TeamProfile({team, isArchived, onToggleArchive, isDisabled, save ) : ( )} - +
@@ -150,10 +141,7 @@ export function TeamProfile({team, isArchived, onToggleArchive, isDisabled, save ) : ( )} - + ); }; @@ -161,10 +149,8 @@ export function TeamProfile({team, isArchived, onToggleArchive, isDisabled, save return (
@@ -193,7 +179,7 @@ export function TeamProfile({team, isArchived, onToggleArchive, isDisabled, save defaultMessage='**Team Description**:' />
- {team.description || {localizeMessage('admin.team_settings.team_detail.profileNoDescription', 'No team description added.')}} + {team.description || {intl.formatMessage({id: 'admin.team_settings.team_detail.profileNoDescription', defaultMessage: 'No team description added.'})}}
diff --git a/webapp/channels/src/components/admin_console/team_channel_settings/team/team_settings.tsx b/webapp/channels/src/components/admin_console/team_channel_settings/team/team_settings.tsx index 52cffd5ac2..702c103ece 100644 --- a/webapp/channels/src/components/admin_console/team_channel_settings/team/team_settings.tsx +++ b/webapp/channels/src/components/admin_console/team_channel_settings/team/team_settings.tsx @@ -2,14 +2,12 @@ // See LICENSE.txt for license information. import React from 'react'; -import {FormattedMessage} from 'react-intl'; +import {FormattedMessage, defineMessage} from 'react-intl'; import TeamList from 'components/admin_console/team_channel_settings/team/list'; import AdminHeader from 'components/widgets/admin_console/admin_header'; import AdminPanel from 'components/widgets/admin_console/admin_panel'; -import {t} from 'utils/i18n'; - type Props = { siteName: string; }; @@ -29,10 +27,8 @@ export function TeamsSettings(props: Props) {
diff --git a/webapp/channels/src/components/admin_console/text_setting.tsx b/webapp/channels/src/components/admin_console/text_setting.tsx index 8da7671ba6..668ff5ab37 100644 --- a/webapp/channels/src/components/admin_console/text_setting.tsx +++ b/webapp/channels/src/components/admin_console/text_setting.tsx @@ -2,20 +2,33 @@ // See LICENSE.txt for license information. import React from 'react'; +import type {MessageDescriptor} from 'react-intl'; +import {useIntl} from 'react-intl'; import TextSetting from 'components/widgets/settings/text_setting'; import type {Props as WidgetTextSettingProps} from 'components/widgets/settings/text_setting'; import SetByEnv from './set_by_env'; -interface Props extends WidgetTextSettingProps { +interface Props extends Omit { setByEnv: boolean; disabled?: boolean; + placeholder?: string | MessageDescriptor; } const AdminTextSetting: React.FunctionComponent = (props: Props): JSX.Element => { - const {setByEnv, disabled, footer, ...sharedProps} = props; + const {setByEnv, disabled, footer, placeholder, ...sharedProps} = props; const isTextDisabled = disabled || setByEnv; + const intl = useIntl(); + + let placeholderToUse; + if (placeholder) { + if (typeof placeholder === 'string') { + placeholderToUse = placeholder; + } else { + placeholderToUse = intl.formatMessage(placeholder); + } + } return ( = (props: Props): JSX.Ele inputClassName='col-sm-8' disabled={isTextDisabled} footer={setByEnv ? : footer} + placeholder={placeholderToUse} /> ); }; diff --git a/webapp/channels/src/components/admin_console/types.ts b/webapp/channels/src/components/admin_console/types.ts index 0902260dfb..a1c20871eb 100644 --- a/webapp/channels/src/components/admin_console/types.ts +++ b/webapp/channels/src/components/admin_console/types.ts @@ -1,9 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import type {MessageDescriptor} from 'react-intl'; + 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 {JobType} from '@mattermost/types/jobs'; + +import type Constants from 'utils/constants'; import type ValidationResult from './validation'; @@ -18,72 +22,65 @@ export type ConsoleAccess = {read: {[key: string]: boolean}; write: {[key: strin type Validator = (value: any) => ValidationResult -type AdminDefinitionSettingCustom = { +type AdminDefinitionSettingCustom = Omit & { type: 'custom'; - label?: string; - label_default?: string; - component: Component; key: string; - isDisabled?: Check; - isHidden?: Check; + showTitle?: boolean; + component: Component; + label?: string; } type AdminDefinitionSettingBase = { key?: string; - label: string; - label_default: string; - onConfigLoad?: (configVal: any, config: any) => any; + label: string | MessageDescriptor; + label_values?: {[key: string]: any}; + help_text?: string | JSX.Element | MessageDescriptor; + help_text_markdown?: boolean; + help_text_values?: {[key: string]: any}; + disabled_help_text?: string | JSX.Element | MessageDescriptor; + disabled_help_text_markdown?: boolean; + disabled_help_text_values?: {[key: string]: any}; + onConfigLoad?: (configVal: any, config: Partial) => any; onConfigSave?: (displayVal: any, previousVal?: any) => any; isHidden?: Check; isDisabled?: Check; } -type AdminDefinitionSettingBanner = AdminDefinitionSettingBase & { +export type AdminDefinitionSettingBanner = AdminDefinitionSettingBase & { type: 'banner'; label_markdown?: boolean; - label_values?: {[key: string]: any}; banner_type: 'info' | 'warning'; } type AdminDefinitionSettingRole = AdminDefinitionSettingBase & { type: 'roles'; multiple?: boolean; - help_text?: string; - help_text_default?: string; - help_text_markdown?: boolean; + no_result?: string | MessageDescriptor; } 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; - multiple?: boolean; - placeholder_default?: string; + placeholder?: string | MessageDescriptor; + placeholder_values?: {[key: string]: any}; validate?: Validator; + multiple?: boolean; setFromMetadataField?: string; - dynamic_value?: (value: any, config: DeepPartial, state: any) => string; + dynamic_value?: (value: any, config: Partial, state: any) => string; max_length?: number; + default?: string; } type AdminDefinitionSettingGenerated = AdminDefinitionSettingBase & { type: 'generated'; - help_text: string | JSX.Element; - help_text_default: string | JSX.Element; - isDisabled: Check; + placeholder?: string | MessageDescriptor; + regenerate_help_text?: string; + default?: string; } -type AdminDefinitionSettingDropdownOption = { +export type AdminDefinitionSettingDropdownOption = { value: string; - display_name: string; - display_name_default: string; - help_text?: string; - help_text_default?: string; + display_name: string | MessageDescriptor; + help_text?: string | MessageDescriptor; help_text_markdown?: boolean; help_text_values?: {[key: string]: any}; isHidden?: Check; @@ -91,96 +88,90 @@ type AdminDefinitionSettingDropdownOption = { 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; + remove_help_text: string | MessageDescriptor; + remove_button_text: string | MessageDescriptor; + removing_text: string | MessageDescriptor; + uploading_text: string | MessageDescriptor; fileType: string; - upload_action: () => void; + upload_action: (file: File, success: (data: any) => void, error: (err: any) => void) => void; set_action?: () => void; setFromMetadataField?: string; - remove_action: () => void; + remove_action: (success: (data: any) => void, error: (err: any) => void) => 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}; + job_type: JobType; 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; + no_result?: string | MessageDescriptor; } 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; + action: (success: (data?: any) => void, error: (error: {message: string; detailed_error?: string}) => void, siteUrl: string) => void; + loading?: string | MessageDescriptor; + error_message: string | MessageDescriptor; + success_message?: string | MessageDescriptor; sourceUrlKey?: string; } +type AdminDefinitionSettingUsername = AdminDefinitionSettingBase & { + type: typeof Constants.SettingsTypes.TYPE_USERNAME; + placeholder: string; + default?: string; +} + +type MappingKeyTypes = 'enableTeamCreation' | 'editOthersPosts' | 'enableOnlyAdminIntegrations'; + +type AdminDefinitionSettingPermission = AdminDefinitionSettingBase & { + type: typeof Constants.SettingsTypes.TYPE_PERMISSION; + permissions_mapping_name: MappingKeyTypes; + key: string; +} + +type AdminDefinitionSettingRadio = AdminDefinitionSettingBase & { + type: typeof Constants.SettingsTypes.TYPE_RADIO; + options: AdminDefinitionSettingDropdownOption[]; + default?: string; +} + export type AdminDefinitionSetting = AdminDefinitionSettingCustom | AdminDefinitionSettingInput | AdminDefinitionSettingGenerated | AdminDefinitionSettingBanner | AdminDefinitionSettingDropdown | AdminDefinitionSettingButton | AdminDefinitionSettingFileUpload | -AdminDefinitionSettingJobsTable | AdminDefinitionSettingLanguage | AdminDefinitionSettingRole; +AdminDefinitionSettingJobsTable | AdminDefinitionSettingLanguage | +AdminDefinitionSettingUsername | AdminDefinitionSettingPermission | +AdminDefinitionSettingRadio | AdminDefinitionSettingRole; type AdminDefinitionConfigSchemaSettings = { id: string; - name: string; - name_default: string; + name: string | MessageDescriptor; isHidden?: Check; - onConfigLoad?: (configVal: any, config: any) => any; + onConfigLoad?: (config: Partial) => {[x: string]: string}; onConfigSave?: (displayVal: any) => any; settings?: AdminDefinitionSetting[]; sections?: AdminDefinitionConfigSchemaSection[]; + footer?: string | MessageDescriptor; + header?: string | MessageDescriptor; } type AdminDefinitionConfigSchemaSection = { title: string; subtitle?: string; settings: AdminDefinitionSetting[]; + header?: string | MessageDescriptor; + footer?: string | MessageDescriptor; } type RestrictedIndicatorType = { @@ -188,22 +179,22 @@ type RestrictedIndicatorType = { shouldDisplay: (license: ClientLicense, subscriptionProduct: Product|undefined) => boolean; } +export type AdminDefinitionSubSectionSchema = AdminDefinitionConfigSchemaComponent | AdminDefinitionConfigSchemaSettings; + export type AdminDefinitionSubSection = { url: string; - title?: string; - title_default?: string; - searchableStrings?: Array; + title?: string | MessageDescriptor; + searchableStrings?: Array; isHidden?: Check; isDiscovery?: boolean; isDisabled?: Check; - schema: AdminDefinitionConfigSchemaComponent | AdminDefinitionConfigSchemaSettings; + schema: AdminDefinitionSubSectionSchema; restrictedIndicator?: RestrictedIndicatorType; } export type AdminDefinitionSection = { icon: JSX.Element; - sectionTitle: string; - sectionTitleDefault: string; + sectionTitle: string | MessageDescriptor; isHidden: Check; id?: string; subsections: {[key: string]: AdminDefinitionSubSection}; @@ -211,4 +202,4 @@ export type AdminDefinitionSection = { export type AdminDefinition = {[key: string]: AdminDefinitionSection} -export type Check = boolean | ((config: DeepPartial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState, isSystemAdmin?: boolean) => boolean) +export type Check = boolean | ((config: Partial, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState, isSystemAdmin?: boolean) => boolean) diff --git a/webapp/channels/src/components/admin_console/user_autocomplete_setting/user_autocomplete_setting.tsx b/webapp/channels/src/components/admin_console/user_autocomplete_setting/user_autocomplete_setting.tsx index 67d114a468..66c3e5f434 100644 --- a/webapp/channels/src/components/admin_console/user_autocomplete_setting/user_autocomplete_setting.tsx +++ b/webapp/channels/src/components/admin_console/user_autocomplete_setting/user_autocomplete_setting.tsx @@ -15,7 +15,7 @@ export type Props = { id: string; label: string; placeholder: string; - helpText: string; + helpText: React.ReactNode; value: string; onChange: (id: string, value: string) => void; disabled: boolean; diff --git a/webapp/channels/src/components/admin_console/validation.tsx b/webapp/channels/src/components/admin_console/validation.tsx index 76daef6e97..708f80ca76 100644 --- a/webapp/channels/src/components/admin_console/validation.tsx +++ b/webapp/channels/src/components/admin_console/validation.tsx @@ -1,27 +1,28 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import * as Utils from 'utils/utils'; +import type {IntlShape, MessageDescriptor} from 'react-intl'; export default class ValidationResult { result: boolean; - text: string; - textDefault: string; + text: MessageDescriptor | string; - constructor(result: boolean, text: string, textDefault: string) { + constructor(result: boolean, text: MessageDescriptor | string) { this.result = result; this.text = text; - this.textDefault = textDefault; } public isValid(): boolean { return this.result; } - public error(): string|null { + public error(intl: IntlShape): string|null { if (this.result) { return null; } - return Utils.localizeMessage(this.text, this.textDefault); + if (typeof this.text === 'string') { + return this.text; + } + return intl.formatMessage(this.text); } } diff --git a/webapp/channels/src/components/analytics/activated_users_card/index.tsx b/webapp/channels/src/components/analytics/activated_users_card/index.tsx index 30dadb60e0..8e2f05a78d 100644 --- a/webapp/channels/src/components/analytics/activated_users_card/index.tsx +++ b/webapp/channels/src/components/analytics/activated_users_card/index.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import React from 'react'; -import {FormattedMessage} from 'react-intl'; +import {FormattedMessage, defineMessages} from 'react-intl'; import {AlertOutlineIcon} from '@mattermost/compass-icons/components'; @@ -17,6 +17,10 @@ type ActivatedUserCardProps = { isCloud: boolean; } +export const messages = defineMessages({ + totalUsers: {id: 'analytics.team.totalUsers', defaultMessage: 'Total Active Users'}, +}); + export const ActivatedUserCard = ({activatedUsers, seatsPurchased, isCloud}: ActivatedUserCardProps) => { const {isBetween5PercerntAnd10PercentPurchasedSeats, isOver10PercerntPurchasedSeats} = calculateOverageUserActivated({seatsPurchased, activeUsers: activatedUsers || 0}); const showOverageWarning = !isCloud && (isBetween5PercerntAnd10PercentPurchasedSeats || isOver10PercerntPurchasedSeats); @@ -34,8 +38,7 @@ export const ActivatedUserCard = ({activatedUsers, seatsPurchased, isCloud}: Act } icon='fa-users' diff --git a/webapp/channels/src/components/analytics/system_analytics/system_analytics.tsx b/webapp/channels/src/components/analytics/system_analytics/system_analytics.tsx index 57e728d4ea..40305c403f 100644 --- a/webapp/channels/src/components/analytics/system_analytics/system_analytics.tsx +++ b/webapp/channels/src/components/analytics/system_analytics/system_analytics.tsx @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import React from 'react'; -import {FormattedMessage} from 'react-intl'; +import {FormattedMessage, defineMessages} from 'react-intl'; import type {AnalyticsRow, PluginAnalyticsRow, IndexedPluginAnalyticsRow} from '@mattermost/types/admin'; import type {ClientLicense} from '@mattermost/types/config'; @@ -42,6 +42,44 @@ type State = { pluginSiteStats: Record; } +const messages = defineMessages({ + title: {id: 'analytics.system.title', defaultMessage: 'System Statistics'}, + totalPosts: {id: 'analytics.system.totalPosts', defaultMessage: 'Total Posts'}, + activeUsers: {id: 'analytics.system.activeUsers', defaultMessage: 'Active Users With Posts'}, + totalSessions: {id: 'analytics.system.totalSessions', defaultMessage: 'Total Sessions'}, + totalCommands: {id: 'analytics.system.totalCommands', defaultMessage: 'Total Commands'}, + totalIncomingWebhooks: {id: 'analytics.system.totalIncomingWebhooks', defaultMessage: 'Incoming Webhooks'}, + totalOutgoingWebhooks: {id: 'analytics.system.totalOutgoingWebhooks', defaultMessage: 'Outgoing Webhooks'}, + totalWebsockets: {id: 'analytics.system.totalWebsockets', defaultMessage: 'WebSocket Conns'}, + totalMasterDbConnections: {id: 'analytics.system.totalMasterDbConnections', defaultMessage: 'Master DB Conns'}, + totalReadDbConnections: {id: 'analytics.system.totalReadDbConnections', defaultMessage: 'Replica DB Conns'}, + postTypes: {id: 'analytics.system.postTypes', defaultMessage: 'Posts, Files and Hashtags'}, + channelTypes: {id: 'analytics.system.channelTypes', defaultMessage: 'Channel Types'}, + totalTeams: {id: 'analytics.system.totalTeams', defaultMessage: 'Total Teams'}, + totalChannels: {id: 'analytics.system.totalChannels', defaultMessage: 'Total Channels'}, + dailyActiveUsers: {id: 'analytics.system.dailyActiveUsers', defaultMessage: 'Daily Active Users'}, + monthlyActiveUsers: {id: 'analytics.system.monthlyActiveUsers', defaultMessage: 'Monthly Active Users'}, +}); + +export const searchableStrings = [ + messages.title, + messages.totalPosts, + messages.activeUsers, + messages.totalSessions, + messages.totalCommands, + messages.totalIncomingWebhooks, + messages.totalOutgoingWebhooks, + messages.totalWebsockets, + messages.totalMasterDbConnections, + messages.totalReadDbConnections, + messages.postTypes, + messages.channelTypes, + messages.totalTeams, + messages.totalChannels, + messages.dailyActiveUsers, + messages.monthlyActiveUsers, +]; + export default class SystemAnalytics extends React.PureComponent { state = { pluginSiteStats: {} as Record, @@ -131,12 +169,7 @@ export default class SystemAnalytics extends React.PureComponent { postCount = ( - } + title={} icon='fa-comment' count={this.getStatValue(stats[StatTypes.TOTAL_POSTS])} /> @@ -162,12 +195,7 @@ export default class SystemAnalytics extends React.PureComponent { postTotalGraph = (
- } + title={} id='totalPostsLineChart' data={postCountsDay} width={740} @@ -179,12 +207,7 @@ export default class SystemAnalytics extends React.PureComponent { activeUserGraph = (
- } + title={} id='activeUsersWithPostsLineChart' data={userCountsWithPostsDay} width={740} @@ -204,12 +227,7 @@ export default class SystemAnalytics extends React.PureComponent { sessionCount = ( - } + title={} icon='fa-signal' count={this.getStatValue(stats[StatTypes.TOTAL_SESSIONS])} /> @@ -218,12 +236,7 @@ export default class SystemAnalytics extends React.PureComponent { commandCount = ( - } + title={} icon='fa-terminal' count={this.getStatValue(stats[StatTypes.TOTAL_COMMANDS])} /> @@ -232,11 +245,7 @@ export default class SystemAnalytics extends React.PureComponent { incomingCount = ( + title={ } icon='fa-arrow-down' count={this.getStatValue(stats[StatTypes.TOTAL_IHOOKS])} @@ -246,11 +255,7 @@ export default class SystemAnalytics extends React.PureComponent { outgoingCount = ( + title={ } icon='fa-arrow-up' count={this.getStatValue(stats[StatTypes.TOTAL_OHOOKS])} @@ -261,33 +266,21 @@ export default class SystemAnalytics extends React.PureComponent { <> + title={ } icon='fa-user' count={this.getStatValue(stats[StatTypes.TOTAL_WEBSOCKET_CONNECTIONS])} /> + title={ } icon='fa-terminal' count={this.getStatValue(stats[StatTypes.TOTAL_MASTER_DB_CONNECTIONS])} /> + title={ } icon='fa-terminal' count={this.getStatValue(stats[StatTypes.TOTAL_READ_DB_CONNECTIONS])} @@ -302,11 +295,7 @@ export default class SystemAnalytics extends React.PureComponent { if (stats[StatTypes.TOTAL_POSTS] !== -1) { postTypeGraph = ( + title={ } data={postTypeData} width={300} @@ -318,11 +307,7 @@ export default class SystemAnalytics extends React.PureComponent { advancedGraphs = (
+ title={ } data={channelTypeData} width={300} @@ -359,11 +344,7 @@ export default class SystemAnalytics extends React.PureComponent { const teamCount = ( + title={ } icon='fa-users' count={this.getStatValue(stats[StatTypes.TOTAL_TEAMS])} @@ -384,11 +365,7 @@ export default class SystemAnalytics extends React.PureComponent { const channelCount = ( + title={ } icon='fa-globe' count={totalChannelCount()} @@ -398,11 +375,7 @@ export default class SystemAnalytics extends React.PureComponent { const dailyActiveUsers = ( + title={ } icon='fa-users' count={this.getStatValue(stats[StatTypes.DAILY_ACTIVE_USERS])} @@ -412,11 +385,7 @@ export default class SystemAnalytics extends React.PureComponent { const monthlyActiveUsers = ( + title={ } icon='fa-users' count={this.getStatValue(stats[StatTypes.MONTHLY_ACTIVE_USERS])} @@ -470,10 +439,7 @@ export default class SystemAnalytics extends React.PureComponent { return (
- +
diff --git a/webapp/channels/src/components/analytics/team_analytics/team_analytics.tsx b/webapp/channels/src/components/analytics/team_analytics/team_analytics.tsx index 6eccfc7719..967e1a127d 100644 --- a/webapp/channels/src/components/analytics/team_analytics/team_analytics.tsx +++ b/webapp/channels/src/components/analytics/team_analytics/team_analytics.tsx @@ -2,7 +2,8 @@ // See LICENSE.txt for license information. import React from 'react'; -import {FormattedDate, FormattedMessage} from 'react-intl'; +import type {MessageDescriptor} from 'react-intl'; +import {FormattedDate, FormattedMessage, defineMessages} from 'react-intl'; import type {AnalyticsRow} from '@mattermost/types/admin'; import type {ClientLicense} from '@mattermost/types/config'; @@ -15,13 +16,12 @@ import {General} from 'mattermost-redux/constants'; import * as AdminActions from 'actions/admin_actions'; import Banner from 'components/admin_console/banner'; -import {ActivatedUserCard} from 'components/analytics/activated_users_card'; +import {ActivatedUserCard, messages as activatedUsersCardsMessages} from 'components/analytics/activated_users_card'; import LineChart from 'components/analytics/line_chart'; import StatisticCount from 'components/analytics/statistic_count'; import TableChart from 'components/analytics/table_chart'; import TrueUpReview from 'components/analytics/true_up_review'; import ExternalLink from 'components/external_link'; -import FormattedMarkdownMessage from 'components/formatted_markdown_message'; import LoadingScreen from 'components/loading_screen'; import AdminHeader from 'components/widgets/admin_console/admin_header'; @@ -80,6 +80,29 @@ type State = { newUsers?: UserProfile[]; }; +const messages = defineMessages({ + title: {id: 'analytics.team.title', defaultMessage: 'Team Statistics for {team}'}, + info: {id: 'analytics.system.info', defaultMessage: 'Use data for only the chosen team. Exclude posts in direct message channels that are not tied to a team.'}, + totalPosts: {id: 'analytics.team.totalPosts', defaultMessage: 'Total Posts'}, + activeUsers: {id: 'analytics.team.activeUsers', defaultMessage: 'Active Users With Posts'}, + publicChannels: {id: 'analytics.team.publicChannels', defaultMessage: 'Public Channels'}, + privateGroups: {id: 'analytics.team.privateGroups', defaultMessage: 'Private Channels'}, + recentUsers: {id: 'analytics.team.recentUsers', defaultMessage: 'Recent Active Users'}, + newlyCreated: {id: 'analytics.team.newlyCreated', defaultMessage: 'Newly Created Users'}, +}); + +export const searchableStrings: Array = [ + [messages.title, {team: ''}], + messages.info, + messages.totalPosts, + messages.activeUsers, + activatedUsersCardsMessages.totalUsers, + messages.publicChannels, + messages.privateGroups, + messages.recentUsers, + messages.newlyCreated, +]; + export default class TeamAnalytics extends React.PureComponent { constructor(props: Props) { super(props); @@ -173,10 +196,7 @@ export default class TeamAnalytics extends React.PureComponent { let banner = (
- +
); @@ -213,12 +233,7 @@ export default class TeamAnalytics extends React.PureComponent { } else { totalPostsCount = ( - } + title={} icon='fa-comment' count={this.getStatValue(stats[StatTypes.TOTAL_POSTS])} /> @@ -228,12 +243,7 @@ export default class TeamAnalytics extends React.PureComponent {
- } + title={} id='totalPosts' data={postCountsDay} width={740} @@ -246,12 +256,7 @@ export default class TeamAnalytics extends React.PureComponent {
- } + title={} id='activeUsersWithPosts' data={userCountsWithPostsDay} width={740} @@ -289,9 +294,8 @@ export default class TeamAnalytics extends React.PureComponent {
- { isCloud={this.props.license.Cloud === 'true'} /> - } + title={} icon='fa-globe' count={this.getStatValue(stats[StatTypes.TOTAL_PUBLIC_CHANNELS])} /> - } + title={} icon='fa-lock' count={this.getStatValue(stats[StatTypes.TOTAL_PRIVATE_GROUPS])} /> @@ -345,21 +339,11 @@ export default class TeamAnalytics extends React.PureComponent { {userActiveGraph}
- } + title={} data={recentActiveUsers} /> - } + title={} data={newlyCreatedUsers} />
diff --git a/webapp/channels/src/components/channel_header/channel_header.tsx b/webapp/channels/src/components/channel_header/channel_header.tsx index e600f86447..a036f46825 100644 --- a/webapp/channels/src/components/channel_header/channel_header.tsx +++ b/webapp/channels/src/components/channel_header/channel_header.tsx @@ -21,6 +21,7 @@ import Markdown from 'components/markdown'; import OverlayTrigger from 'components/overlay_trigger'; import type {BaseOverlayTrigger} from 'components/overlay_trigger'; import ChannelPermissionGate from 'components/permissions_gates/channel_permission_gate'; +import {statusDropdownMessages} from 'components/status_dropdown/status_dropdown'; import StatusIcon from 'components/status_icon'; import Timestamp from 'components/timestamp'; import Tooltip from 'components/tooltip'; @@ -34,7 +35,7 @@ import { NotificationLevels, RHSStates, } from 'utils/constants'; -import {handleFormattedTextClick, localizeMessage, isEmptyObject, toTitleCase} from 'utils/utils'; +import {handleFormattedTextClick, isEmptyObject} from 'utils/utils'; import type {ModalData} from 'types/actions'; import type {RhsState} from 'types/store/rhs'; @@ -260,7 +261,7 @@ class ChannelHeader extends React.PureComponent { hideGuestTags, } = this.props; const {formatMessage} = this.props.intl; - const ariaLabelChannelHeader = localizeMessage('accessibility.sections.channelHeader', 'channel header region'); + const ariaLabelChannelHeader = this.props.intl.formatMessage({id: 'accessibility.sections.channelHeader', defaultMessage: 'channel header region'}); let hasGuestsText: ReactNode = ''; if (hasGuests && !hideGuestTags) { @@ -314,10 +315,7 @@ class ChannelHeader extends React.PureComponent { dmHeaderTextStatus = ( - + {this.renderCustomStatus()} ); diff --git a/webapp/channels/src/components/formatted_markdown_message.tsx b/webapp/channels/src/components/formatted_markdown_message.tsx index dfc4bff51f..b2b08d2c31 100644 --- a/webapp/channels/src/components/formatted_markdown_message.tsx +++ b/webapp/channels/src/components/formatted_markdown_message.tsx @@ -3,6 +3,7 @@ import marked from 'marked'; import React from 'react'; +import type {MessageDescriptor} from 'react-intl'; import {useIntl} from 'react-intl'; import {shouldOpenInNewTab, getSiteURL} from 'utils/url'; @@ -39,7 +40,7 @@ export class CustomRenderer extends marked.Renderer { } type Props = { - defaultMessage?: string; + defaultMessage?: MessageDescriptor['defaultMessage']; disableLinks?: boolean; id?: string; values?: Record; diff --git a/webapp/channels/src/components/quick_input/quick_input.tsx b/webapp/channels/src/components/quick_input/quick_input.tsx index 426bbca321..8bc94b6544 100644 --- a/webapp/channels/src/components/quick_input/quick_input.tsx +++ b/webapp/channels/src/components/quick_input/quick_input.tsx @@ -78,7 +78,7 @@ export type Props = { maxLength?: number; className?: string; - placeholder?: string | { id: string; defaultMessage: string }; + placeholder?: string; autoFocus?: boolean; type?: string; id?: string; diff --git a/webapp/channels/src/components/reset_status_modal/__snapshots__/reset_status_modal.test.tsx.snap b/webapp/channels/src/components/reset_status_modal/__snapshots__/reset_status_modal.test.tsx.snap index 1fe6312ef9..85ff2ebfff 100644 --- a/webapp/channels/src/components/reset_status_modal/__snapshots__/reset_status_modal.test.tsx.snap +++ b/webapp/channels/src/components/reset_status_modal/__snapshots__/reset_status_modal.test.tsx.snap @@ -2,17 +2,7 @@ exports[`components/ResetStatusModal should match snapshot 1`] = ` - } + cancelButtonText="" checkboxText={ } message={ } modalClass="" @@ -47,33 +27,13 @@ exports[`components/ResetStatusModal should match snapshot 1`] = ` onConfirm={[Function]} show={false} showCheckbox={true} - title={ - - } + title="" /> `; exports[`components/ResetStatusModal should match snapshot, render modal for OOF status 1`] = ` - } + cancelButtonText="" checkboxText={ } message={ } modalClass="" @@ -108,16 +58,6 @@ exports[`components/ResetStatusModal should match snapshot, render modal for OOF onConfirm={[Function]} show={false} showCheckbox={false} - title={ - - } + title="" /> `; diff --git a/webapp/channels/src/components/reset_status_modal/reset_status_modal.tsx b/webapp/channels/src/components/reset_status_modal/reset_status_modal.tsx index 662f1e4de6..b97b959de1 100644 --- a/webapp/channels/src/components/reset_status_modal/reset_status_modal.tsx +++ b/webapp/channels/src/components/reset_status_modal/reset_status_modal.tsx @@ -2,7 +2,8 @@ // See LICENSE.txt for license information. import React from 'react'; -import {FormattedMessage} from 'react-intl'; +import type {MessageDescriptor} from 'react-intl'; +import {FormattedMessage, defineMessages} from 'react-intl'; import type {PreferenceType} from '@mattermost/types/preferences'; import type {UserStatus} from '@mattermost/types/users'; @@ -12,34 +13,99 @@ import {Preferences} from 'mattermost-redux/constants'; import ConfirmModal from 'components/confirm_modal'; import {UserStatuses} from 'utils/constants'; -import {t} from 'utils/i18n'; -import {toTitleCase} from 'utils/utils'; -t('modal.manual_status.auto_responder.message_'); -t('modal.manual_status.auto_responder.message_away'); -t('modal.manual_status.auto_responder.message_dnd'); -t('modal.manual_status.auto_responder.message_offline'); -t('modal.manual_status.auto_responder.message_online'); -t('modal.manual_status.button_'); -t('modal.manual_status.button_away'); -t('modal.manual_status.button_dnd'); -t('modal.manual_status.button_offline'); -t('modal.manual_status.button_online'); -t('modal.manual_status.cancel_'); -t('modal.manual_status.cancel_away'); -t('modal.manual_status.cancel_dnd'); -t('modal.manual_status.cancel_offline'); -t('modal.manual_status.cancel_ooo'); -t('modal.manual_status.message_'); -t('modal.manual_status.message_away'); -t('modal.manual_status.message_dnd'); -t('modal.manual_status.message_offline'); -t('modal.manual_status.message_online'); -t('modal.manual_status.title_'); -t('modal.manual_status.title_away'); -t('modal.manual_status.title_dnd'); -t('modal.manual_status.title_offline'); -t('modal.manual_status.title_ooo'); +const messages: Record> = { + away: defineMessages({ + auto_responder_message: { + id: 'modal.manual_status.auto_responder.message_away', + defaultMessage: 'Would you like to switch your status to "Away" and disable Automatic Replies?', + }, + button: { + id: 'modal.manual_status.button_away', + defaultMessage: 'Yes, set my status to "Away"', + }, + cancel: { + id: 'modal.manual_status.cancel_away', + defaultMessage: 'No, keep it as "Away"', + }, + message: { + id: 'modal.manual_status.message_away', + defaultMessage: 'Would you like to switch your status to "Away"?', + }, + title: { + id: 'modal.manual_status.title_away', + defaultMessage: 'Your Status is Set to "Away"', + }, + }), + dnd: defineMessages({ + auto_responder_message: { + id: 'modal.manual_status.auto_responder.message_dnd', + defaultMessage: 'Would you like to switch your status to "Do Not Disturb" and disable Automatic Replies?', + }, + button: { + id: 'modal.manual_status.button_dnd', + defaultMessage: 'Yes, set my status to "Do Not Disturb"', + }, + cancel: { + id: 'modal.manual_status.cancel_dnd', + defaultMessage: 'No, keep it as "Do Not Disturb"', + }, + message: { + id: 'modal.manual_status.message_dnd', + defaultMessage: 'Would you like to switch your status to "Do Not Disturb"?', + }, + title: { + id: 'modal.manual_status.title_dnd', + defaultMessage: 'Your Status is Set to "Do Not Disturb"', + }, + }), + offline: defineMessages({ + auto_responder_message: { + id: 'modal.manual_status.auto_responder.message_offline', + defaultMessage: 'Would you like to switch your status to "Offline" and disable Automatic Replies?', + }, + button: { + id: 'modal.manual_status.button_offline', + defaultMessage: 'Yes, set my status to "Offline"', + }, + cancel: { + id: 'modal.manual_status.cancel_offline', + defaultMessage: 'No, keep it as "Offline"', + }, + message: { + id: 'modal.manual_status.message_offline', + defaultMessage: 'Would you like to switch your status to "Offline"?', + }, + title: { + id: 'modal.manual_status.title_offline', + defaultMessage: 'Your Status is Set to "Offline"', + }, + }), + online: defineMessages({ + auto_responder_message: { + id: 'modal.manual_status.auto_responder.message_online', + defaultMessage: 'Would you like to switch your status to "Online" and disable Automatic Replies?', + }, + button: { + id: 'modal.manual_status.button_online', + defaultMessage: 'Yes, set my status to "Online"', + }, + message: { + id: 'modal.manual_status.message_online', + defaultMessage: 'Would you like to switch your status to "Online"?', + }, + }), + ooo: defineMessages({ + cancel: { + id: 'modal.manual_status.cancel_ooo', + defaultMessage: 'No, keep it as "Out of Office"', + }, + title: { + id: 'modal.manual_status.title_ooo', + defaultMessage: 'Your Status is Set to "Out of Office"', + }, + }), +}; type Props = { @@ -146,62 +212,19 @@ export default class ResetStatusModal extends React.PureComponent private renderModalMessage = () => { if (this.props.currentUserStatus === UserStatuses.OUT_OF_OFFICE) { - return ( - - ); + return messages[this.state.newStatus] ? () : ''; } - return ( - - ); + return messages[this.state.newStatus] ? () : ''; }; public render(): JSX.Element { const userStatus = this.state.currentUserStatus.status || ''; - const userStatusId = 'modal.manual_status.title_' + userStatus; - const manualStatusTitle = ( - - ); + const manualStatusTitle = messages[userStatus] ? () : ''; const manualStatusMessage = this.renderModalMessage(); - - const manualStatusButton = ( - - ); - const manualStatusId = 'modal.manual_status.cancel_' + userStatus; - const manualStatusCancel = ( - - ); + const manualStatusButton = messages[this.state.newStatus] ? () : ''; + const manualStatusCancel = messages[userStatus] ? () : ''; const manualStatusCheckbox = ( > = { + ooo: defineMessages({ + name: { + id: 'status_dropdown.set_ooo', + defaultMessage: 'Out of office', + }, + extra: { + id: 'status_dropdown.set_ooo.extra', + defaultMessage: 'Automatic Replies are enabled', + }, + }), + online: defineMessages({ + name: { + id: 'status_dropdown.set_online', + defaultMessage: 'Online', + }, + }), + away: defineMessages({ + name: { + id: 'status_dropdown.set_away', + defaultMessage: 'Away', + }, + }), + dnd: defineMessages({ + name: { + id: 'status_dropdown.set_dnd', + defaultMessage: 'Do not disturb', + }, + extra: { + id: 'status_dropdown.set_dnd.extra', + defaultMessage: 'Disables all notifications', + }, + }), + offline: defineMessages({ + name: { + id: 'status_dropdown.set_offline', + defaultMessage: 'Offline', + }, + }), +}; + export class StatusDropdown extends React.PureComponent { dndTimes = [ - {id: 'thirty_minutes', label: t('status_dropdown.dnd_sub_menu_item.thirty_minutes'), labelDefault: '30 mins'}, - {id: 'one_hour', label: t('status_dropdown.dnd_sub_menu_item.one_hour'), labelDefault: '1 hour'}, - {id: 'two_hours', label: t('status_dropdown.dnd_sub_menu_item.two_hours'), labelDefault: '2 hours'}, - {id: 'tomorrow', label: t('status_dropdown.dnd_sub_menu_item.tomorrow'), labelDefault: 'Tomorrow'}, - {id: 'custom', label: t('status_dropdown.dnd_sub_menu_item.custom'), labelDefault: 'Custom'}, + {id: 'thirty_minutes', label: defineMessage({id: 'status_dropdown.dnd_sub_menu_item.thirty_minutes', defaultMessage: '30 mins'})}, + {id: 'one_hour', label: defineMessage({id: 'status_dropdown.dnd_sub_menu_item.one_hour', defaultMessage: '1 hour'})}, + {id: 'two_hours', label: defineMessage({id: 'status_dropdown.dnd_sub_menu_item.two_hours', defaultMessage: '2 hours'})}, + {id: 'tomorrow', label: defineMessage({id: 'status_dropdown.dnd_sub_menu_item.tomorrow', defaultMessage: 'Tomorrow'})}, + {id: 'custom', label: defineMessage({id: 'status_dropdown.dnd_sub_menu_item.custom', defaultMessage: 'Custom'})}, ]; static defaultProps = { userId: '', @@ -242,13 +282,13 @@ export class StatusDropdown extends React.PureComponent { customStatusText = customStatus?.text; break; case isStatusSet && !customStatus?.text && customStatus?.duration === CustomStatusDuration.DONT_CLEAR: - customStatusHelpText = localizeMessage('status_dropdown.set_custom_text', 'Set Custom Status Text...'); + customStatusHelpText = this.props.intl.formatMessage({id: 'status_dropdown.set_custom_text', defaultMessage: 'Set Custom Status Text...'}); break; case isStatusSet && !customStatus?.text && customStatus?.duration !== CustomStatusDuration.DONT_CLEAR: customStatusText = ''; break; case !isStatusSet: - customStatusHelpText = localizeMessage('status_dropdown.set_custom', 'Set a Custom Status'); + customStatusHelpText = this.props.intl.formatMessage({id: 'status_dropdown.set_custom', defaultMessage: 'Set a Custom Status'}); } const customStatusEmoji = isStatusSet ? ( @@ -361,16 +401,16 @@ export class StatusDropdown extends React.PureComponent { /> ); - const dndSubMenuItems = [ + const dndSubMenuItems = ([ { id: 'dndSubMenu-header', direction: 'right', - text: localizeMessage('status_dropdown.dnd_sub_menu_header', 'Disable notifications until:'), + text: this.props.intl.formatMessage({id: 'status_dropdown.dnd_sub_menu_header', defaultMessage: 'Disable notifications until:'}), isHeader: true, - } as any, - ].concat( - this.dndTimes.map(({id, label, labelDefault}, index) => { - let text: React.ReactNode = localizeMessage(label, labelDefault); + }, + ] as MenuType[])?.concat( + this.dndTimes.map(({id, label}, index) => { + let text: MenuType['text'] = this.props.intl.formatMessage(label); if (index === 3) { const tomorrow = getCurrentMomentForTimezone(this.props.timezone).add(1, 'day').set({hour: 9, minute: 0}).toDate(); text = ( @@ -399,7 +439,7 @@ export class StatusDropdown extends React.PureComponent { text, action: index === 4 ? () => setCustomTimedDnd() : () => setDnd(index), - } as any; + }; }), ); @@ -475,7 +515,7 @@ export class StatusDropdown extends React.PureComponent {
@@ -501,17 +541,17 @@ export class StatusDropdown extends React.PureComponent { null} - ariaLabel={localizeMessage('status_dropdown.set_ooo', 'Out of office').toLowerCase()} - text={localizeMessage('status_dropdown.set_ooo', 'Out of office')} - extraText={localizeMessage('status_dropdown.set_ooo.extra', 'Automatic Replies are enabled')} + ariaLabel={this.props.intl.formatMessage(statusDropdownMessages.ooo.name)} + text={this.props.intl.formatMessage(statusDropdownMessages.ooo.name)} + extraText={this.props.intl.formatMessage(statusDropdownMessages.ooo.extra)} /> {customStatusComponent} { /> { /> { /> { modalId={ModalIdentifiers.USER_SETTINGS} dialogType={UserSettingsModal} dialogProps={{isContentProductSettings: false}} - text={localizeMessage('navbar_dropdown.profileSettings', 'Profile')} + text={this.props.intl.formatMessage({id: 'navbar_dropdown.profileSettings', defaultMessage: 'Profile'})} icon={} > {this.props.showCompleteYourProfileTour && ( @@ -588,7 +628,7 @@ export class StatusDropdown extends React.PureComponent { } /> diff --git a/webapp/channels/src/components/widgets/admin_console/admin_panel.test.tsx b/webapp/channels/src/components/widgets/admin_console/admin_panel.test.tsx index 4dcd6b9831..a30ae0888e 100644 --- a/webapp/channels/src/components/widgets/admin_console/admin_panel.test.tsx +++ b/webapp/channels/src/components/widgets/admin_console/admin_panel.test.tsx @@ -10,10 +10,8 @@ describe('components/widgets/admin_console/AdminPanel', () => { const defaultProps = { className: 'test-class-name', id: 'test-id', - titleId: 'test-title-id', - titleDefault: 'test-title-default', - subtitleId: 'test-subtitle-id', - subtitleDefault: 'test-subtitle-default', + title: {id: 'test-title-id', defaultMessage: 'test-title-default'}, + subtitle: {id: 'test-subtitle-id', defaultMessage: 'test-subtitle-default'}, subtitleValues: {foo: 'bar'}, }; diff --git a/webapp/channels/src/components/widgets/admin_console/admin_panel.tsx b/webapp/channels/src/components/widgets/admin_console/admin_panel.tsx index a88e03b2a5..18b9fe96bb 100644 --- a/webapp/channels/src/components/widgets/admin_console/admin_panel.tsx +++ b/webapp/channels/src/components/widgets/admin_console/admin_panel.tsx @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import React from 'react'; +import type {MessageDescriptor} from 'react-intl'; import {FormattedMessage} from 'react-intl'; import './admin_panel.scss'; @@ -10,51 +11,52 @@ type Props = { id?: string; className?: string; onHeaderClick?: React.EventHandler; - titleId: string; - titleDefault: string; - subtitleId: string; - subtitleDefault: string; + title: MessageDescriptor; + subtitle: MessageDescriptor; subtitleValues?: any; button?: React.ReactNode; children?: React.ReactNode; }; -const AdminPanel: React.FC = (props: Props) => ( +const AdminPanel: React.FC = ({ + subtitle, + title, + button, + children, + className = '', + id, + onHeaderClick, + subtitleValues, +}: Props) => (

- {props.button && + {button &&
- {props.button} + {button}
}
- {props.children} + {children}
); -AdminPanel.defaultProps = { - className: '', -}; - export default AdminPanel; diff --git a/webapp/channels/src/components/widgets/admin_console/admin_panel_togglable.test.tsx b/webapp/channels/src/components/widgets/admin_console/admin_panel_togglable.test.tsx index 27f4d3640b..31a5bc19ef 100644 --- a/webapp/channels/src/components/widgets/admin_console/admin_panel_togglable.test.tsx +++ b/webapp/channels/src/components/widgets/admin_console/admin_panel_togglable.test.tsx @@ -10,31 +10,43 @@ describe('components/widgets/admin_console/AdminPanelTogglable', () => { const defaultProps = { className: 'test-class-name', id: 'test-id', - titleId: 'test-title-id', - titleDefault: 'test-title-default', - subtitleId: 'test-subtitle-id', - subtitleDefault: 'test-subtitle-default', + title: {id: 'test-title-id', defaultMessage: 'test-title-default'}, + subtitle: { + id: 'test-subtitle-id', + defaultMessage: 'test-subtitle-default', + }, open: true, onToggle: jest.fn(), }; test('should match snapshot', () => { - const wrapper = shallow({'Test'}); - expect(wrapper).toMatchInlineSnapshot(` -} - className="AdminPanelTogglable test-class-name" - id="test-id" - onHeaderClick={[MockFunction]} - subtitleDefault="test-subtitle-default" - subtitleId="test-subtitle-id" - titleDefault="test-title-default" - titleId="test-title-id" -> - Test - -`, + const wrapper = shallow( + + {'Test'} + , ); + expect(wrapper).toMatchInlineSnapshot(` + } + className="AdminPanelTogglable test-class-name" + id="test-id" + onHeaderClick={[MockFunction]} + subtitle={ + Object { + "defaultMessage": "test-subtitle-default", + "id": "test-subtitle-id", + } + } + title={ + Object { + "defaultMessage": "test-title-default", + "id": "test-title-id", + } + } + > + Test + + `); }); test('should match snapshot closed', () => { @@ -44,21 +56,29 @@ describe('components/widgets/admin_console/AdminPanelTogglable', () => { open={false} > {'Test'} - ); - expect(wrapper).toMatchInlineSnapshot(` -} - className="AdminPanelTogglable test-class-name closed" - id="test-id" - onHeaderClick={[MockFunction]} - subtitleDefault="test-subtitle-default" - subtitleId="test-subtitle-id" - titleDefault="test-title-default" - titleId="test-title-id" -> - Test - -`, + , ); + expect(wrapper).toMatchInlineSnapshot(` + } + className="AdminPanelTogglable test-class-name closed" + id="test-id" + onHeaderClick={[MockFunction]} + subtitle={ + Object { + "defaultMessage": "test-subtitle-default", + "id": "test-subtitle-id", + } + } + title={ + Object { + "defaultMessage": "test-title-default", + "id": "test-title-id", + } + } + > + Test + + `); }); }); diff --git a/webapp/channels/src/components/widgets/admin_console/admin_panel_togglable.tsx b/webapp/channels/src/components/widgets/admin_console/admin_panel_togglable.tsx index 9e48afd5a6..a7a9ca8028 100644 --- a/webapp/channels/src/components/widgets/admin_console/admin_panel_togglable.tsx +++ b/webapp/channels/src/components/widgets/admin_console/admin_panel_togglable.tsx @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import React from 'react'; +import type {MessageDescriptor} from 'react-intl'; import AccordionToggleIcon from 'components/widgets/icons/accordion_toggle_icon'; @@ -12,27 +13,30 @@ type Props = { className: string; id?: string; open: boolean; - titleId: string; - titleDefault: string; - subtitleId: string; - subtitleDefault: string; + title: MessageDescriptor; + subtitle: MessageDescriptor; onToggle?: React.EventHandler; - isDisabled?: boolean; }; -const AdminPanelTogglable: React.FC = (props: Props) => { +const AdminPanelTogglable = ({ + className, + open, + subtitle, + title, + children, + id, + onToggle, +}: Props) => { return ( } > - {props.children} + {children} ); }; diff --git a/webapp/channels/src/components/widgets/admin_console/admin_panel_with_button.test.tsx b/webapp/channels/src/components/widgets/admin_console/admin_panel_with_button.test.tsx index f56105994d..7f904345f0 100644 --- a/webapp/channels/src/components/widgets/admin_console/admin_panel_with_button.test.tsx +++ b/webapp/channels/src/components/widgets/admin_console/admin_panel_with_button.test.tsx @@ -10,13 +10,16 @@ describe('components/widgets/admin_console/AdminPanelWithButton', () => { const defaultProps = { className: 'test-class-name', id: 'test-id', - titleId: 'test-title-id', - titleDefault: 'test-title-default', - subtitleId: 'test-subtitle-id', - subtitleDefault: 'test-subtitle-default', + title: {id: 'test-title-id', defaultMessage: 'test-title-default'}, + subtitle: { + id: 'test-subtitle-id', + defaultMessage: 'test-subtitle-default', + }, onButtonClick: jest.fn(), - buttonTextId: 'test-button-text-id', - buttonTextDefault: 'test-button-text-default', + buttonText: { + id: 'test-button-text-id', + defaultMessage: 'test-button-text-default', + }, disabled: false, }; @@ -42,10 +45,18 @@ describe('components/widgets/admin_console/AdminPanelWithButton', () => { } className="AdminPanelWithButton test-class-name" id="test-id" - subtitleDefault="test-subtitle-default" - subtitleId="test-subtitle-id" - titleDefault="test-title-default" - titleId="test-title-id" + subtitle={ + Object { + "defaultMessage": "test-subtitle-default", + "id": "test-subtitle-id", + } + } + title={ + Object { + "defaultMessage": "test-title-default", + "id": "test-title-id", + } + } > Test @@ -77,10 +88,18 @@ describe('components/widgets/admin_console/AdminPanelWithButton', () => { } className="AdminPanelWithButton test-class-name" id="test-id" - subtitleDefault="test-subtitle-default" - subtitleId="test-subtitle-id" - titleDefault="test-title-default" - titleId="test-title-id" + subtitle={ + Object { + "defaultMessage": "test-subtitle-default", + "id": "test-subtitle-id", + } + } + title={ + Object { + "defaultMessage": "test-title-default", + "id": "test-title-id", + } + } > Test diff --git a/webapp/channels/src/components/widgets/admin_console/admin_panel_with_button.tsx b/webapp/channels/src/components/widgets/admin_console/admin_panel_with_button.tsx index 85232b6570..057c1cc147 100644 --- a/webapp/channels/src/components/widgets/admin_console/admin_panel_with_button.tsx +++ b/webapp/channels/src/components/widgets/admin_console/admin_panel_with_button.tsx @@ -3,6 +3,7 @@ import classNames from 'classnames'; import React from 'react'; +import type {MessageDescriptor} from 'react-intl'; import {FormattedMessage} from 'react-intl'; import AdminPanel from './admin_panel'; @@ -11,29 +12,34 @@ type Props = { children?: React.ReactNode; className: string; id?: string; - titleId: string; - titleDefault: string; - subtitleId: string; - subtitleDefault: string; + title: MessageDescriptor; + subtitle: MessageDescriptor; onButtonClick?: React.EventHandler; disabled?: boolean; - buttonTextId?: string; - buttonTextDefault?: string; + buttonText?: MessageDescriptor; } -const AdminPanelWithButton: React.FC = (props: Props) => { +const AdminPanelWithButton = ({ + className, + subtitle, + title, + buttonText, + children, + disabled, + id, + onButtonClick, +}: Props) => { let button; - if (props.onButtonClick && props.buttonTextId) { - const buttonId = (props.buttonTextDefault || '').split(' ').join('-').toLowerCase(); + if (onButtonClick && buttonText) { + const buttonId = (buttonText.defaultMessage as string || '').split(' ').join('-').toLowerCase(); button = ( e.preventDefault() : props.onButtonClick} + className={classNames('btn', 'btn-primary', {disabled})} + onClick={disabled ? (e) => e.preventDefault() : onButtonClick} data-testid={buttonId} > ); @@ -41,15 +47,13 @@ const AdminPanelWithButton: React.FC = (props: Props) => { return ( - {props.children} + {children} ); }; diff --git a/webapp/channels/src/components/widgets/admin_console/admin_panel_with_link.test.tsx b/webapp/channels/src/components/widgets/admin_console/admin_panel_with_link.test.tsx index 64b187fe96..e0329f3879 100644 --- a/webapp/channels/src/components/widgets/admin_console/admin_panel_with_link.test.tsx +++ b/webapp/channels/src/components/widgets/admin_console/admin_panel_with_link.test.tsx @@ -10,13 +10,16 @@ describe('components/widgets/admin_console/AdminPanelWithLink', () => { const defaultProps = { className: 'test-class-name', id: 'test-id', - titleId: 'test-title-id', - titleDefault: 'test-title-default', - subtitleId: 'test-subtitle-id', - subtitleDefault: 'test-subtitle-default', + title: {id: 'test-title-id', defaultMessage: 'test-title-default'}, + subtitle: { + id: 'test-subtitle-id', + defaultMessage: 'test-subtitle-default', + }, url: '/path', - linkTextId: 'test-button-text-id', - linkTextDefault: 'test-button-text-default', + linkText: { + id: 'test-button-text-id', + defaultMessage: 'test-button-text-default', + }, disabled: false, }; @@ -42,10 +45,18 @@ describe('components/widgets/admin_console/AdminPanelWithLink', () => { className="AdminPanelWithLink test-class-name" data-testid="test-id" id="test-id" - subtitleDefault="test-subtitle-default" - subtitleId="test-subtitle-id" - titleDefault="test-title-default" - titleId="test-title-id" + subtitle={ + Object { + "defaultMessage": "test-subtitle-default", + "id": "test-subtitle-id", + } + } + title={ + Object { + "defaultMessage": "test-title-default", + "id": "test-title-id", + } + } > Test @@ -79,10 +90,18 @@ describe('components/widgets/admin_console/AdminPanelWithLink', () => { className="AdminPanelWithLink test-class-name" data-testid="test-id" id="test-id" - subtitleDefault="test-subtitle-default" - subtitleId="test-subtitle-id" - titleDefault="test-title-default" - titleId="test-title-id" + subtitle={ + Object { + "defaultMessage": "test-subtitle-default", + "id": "test-subtitle-id", + } + } + title={ + Object { + "defaultMessage": "test-title-default", + "id": "test-title-id", + } + } > Test diff --git a/webapp/channels/src/components/widgets/admin_console/admin_panel_with_link.tsx b/webapp/channels/src/components/widgets/admin_console/admin_panel_with_link.tsx index 71c6dee704..65aca576b5 100644 --- a/webapp/channels/src/components/widgets/admin_console/admin_panel_with_link.tsx +++ b/webapp/channels/src/components/widgets/admin_console/admin_panel_with_link.tsx @@ -3,6 +3,7 @@ import classNames from 'classnames'; import React from 'react'; +import type {MessageDescriptor} from 'react-intl'; import {FormattedMessage} from 'react-intl'; import {Link} from 'react-router-dom'; @@ -10,53 +11,53 @@ import AdminPanel from './admin_panel'; type Props = { children?: React.ReactNode; - className: string; + className?: string; id?: string; - titleId: string; - titleDefault: string; - subtitleId: string; - subtitleDefault: string; + title: MessageDescriptor; + subtitle: MessageDescriptor; subtitleValues?: any; url: string; disabled?: boolean; - linkTextId: string; - linkTextDefault: string; + linkText: MessageDescriptor; } -const AdminPanelWithLink = (props: Props) => { +const AdminPanelWithLink = ({ + className = '', + linkText, + subtitle, + title, + url, + children, + disabled, + id, + subtitleValues, +}: Props) => { const button = ( e.preventDefault() : () => null} + data-testid={`${id}-link`} + className={classNames(['btn', 'btn-primary', {disabled}])} + to={url} + onClick={disabled ? (e) => e.preventDefault() : () => null} > ); return ( - {props.children} + {children} ); }; -AdminPanelWithLink.defaultProps = { - className: '', -}; - export default AdminPanelWithLink; diff --git a/webapp/channels/src/i18n/en.json b/webapp/channels/src/i18n/en.json index 779580fda5..e6a4a3ea51 100644 --- a/webapp/channels/src/i18n/en.json +++ b/webapp/channels/src/i18n/en.json @@ -439,7 +439,6 @@ "admin.bleve.enableAutocompleteDescription": "When true, Bleve will be used for all autocompletion queries on users and channels using the latest index. Autocompletion results may be incomplete until a bulk index of the existing users and channels database is finished. When false, database autocomplete is used.", "admin.bleve.enableAutocompleteTitle": "Enable Bleve for autocomplete queries:", "admin.bleve.enableIndexingDescription": "When true, indexing of new posts occurs automatically. Search queries will use database search until \"Enable Bleve for search queries\" is enabled. {documentationLink}", - "admin.bleve.enableIndexingDescription.documentationLinkText": "Learn more about Bleve in our documentation.", "admin.bleve.enableIndexingTitle": "Enable Bleve Indexing:", "admin.bleve.enableSearchingDescription": "When true, Bleve will be used for all search queries using the latest index. Search results may be incomplete until a bulk index of the existing post database is finished. When false, database search is used.", "admin.bleve.enableSearchingTitle": "Enable Bleve for search queries:", @@ -522,6 +521,8 @@ "admin.channel_settings.channel_moderation.subtitleMembers": "Manage the actions available to channel members.", "admin.channel_settings.channel_moderation.title": "Channel Moderation", "admin.channel_settings.channel_row.configure": "Edit", + "admin.channel_settings.channel_row.managementMethod.group": "Group Sync", + "admin.channel_settings.channel_row.managementMethod.manual": "Manual Invites", "admin.channel_settings.description": "Manage channel settings.", "admin.channel_settings.groupsPageTitle": "{siteName} Channels", "admin.channel_settings.title": "Channels", @@ -762,7 +763,6 @@ "admin.elasticsearch.clientKeyTitle": "Client Certificate Key path:", "admin.elasticsearch.connectionUrlDescription": "The address of the Elasticsearch server. {documentationLink}", "admin.elasticsearch.connectionUrlExample": "E.g.: \"https://elasticsearch.example.org:9200\"", - "admin.elasticsearch.connectionUrlExample.documentationLinkText": "Please see documentation with server setup instructions.", "admin.elasticsearch.connectionUrlTitle": "Server Connection Address:", "admin.elasticsearch.createJob.help": "All users, channels and posts in the database will be indexed from oldest to newest. Elasticsearch is available during indexing but search results may be incomplete until the indexing job is complete.", "admin.elasticsearch.createJob.title": "Index Now", @@ -770,12 +770,12 @@ "admin.elasticsearch.enableAutocompleteDescription": "Requires a successful connection to the Elasticsearch server. When true, Elasticsearch will be used for all autocompletion queries on users and channels using the latest index. Autocompletion results may be incomplete until a bulk index of the existing users and channels database is finished. When false, database autocomplete is used.", "admin.elasticsearch.enableAutocompleteTitle": "Enable Elasticsearch for autocomplete queries:", "admin.elasticsearch.enableIndexingDescription": "When true, indexing of new posts occurs automatically. Search queries will use database search until \"Enable Elasticsearch for search queries\" is enabled. {documentationLink}", - "admin.elasticsearch.enableIndexingDescription.documentationLinkText": "Learn more about Elasticsearch in our documentation.", "admin.elasticsearch.enableIndexingTitle": "Enable Elasticsearch Indexing:", "admin.elasticsearch.enableSearchingDescription": "Requires a successful connection to the Elasticsearch server. When true, Elasticsearch will be used for all search queries using the latest index. Search results may be incomplete until a bulk index of the existing post database is finished. When false, database search is used.", "admin.elasticsearch.enableSearchingTitle": "Enable Elasticsearch for search queries:", "admin.elasticsearch.ignoredPurgeIndexes": "Indexes to skip while purging:", "admin.elasticsearch.ignoredPurgeIndexesDescription": "When filled in, these indexes will be ignored during the purge, separated by commas.", + "admin.elasticsearch.ignoredPurgeIndexesDescription.example": "E.g.: .opendistro*,.security*", "admin.elasticsearch.password": "E.g.: \"yourpassword\"", "admin.elasticsearch.passwordDescription": "(Optional) The password to authenticate to the Elasticsearch server.", "admin.elasticsearch.passwordTitle": "Server Password:", @@ -893,6 +893,8 @@ "admin.experimental.allowedEmailDomain.desc": "(Optional) When set, users must have an email ending in this domain to move threads. Multiple domains can be specified by separating them with commas.", "admin.experimental.allowedEmailDomain.title": "Allowed Email Domain", "admin.experimental.clientSideCertCheck.desc": "When **primary**, after the client side certificate is verified, user’s email is retrieved from the certificate and is used to log in without a password. When **secondary**, after the client side certificate is verified, user’s email is retrieved from the certificate and matched against the one supplied by the user. If they match, the user logs in with regular email/password credentials.", + "admin.experimental.clientSideCertCheck.options.primary": "primary", + "admin.experimental.clientSideCertCheck.options.secondary": "secondary", "admin.experimental.clientSideCertCheck.title": "Client-Side Certification Login Method:", "admin.experimental.clientSideCertEnable.desc": "Enables client-side certification for your Mattermost server. See documentation to learn more.", "admin.experimental.clientSideCertEnable.title": "Enable Client-Side Certification:", @@ -903,6 +905,11 @@ "admin.experimental.collapsedThreads.off": "Disabled", "admin.experimental.collapsedThreads.title": "Collapsed Reply Threads", "admin.experimental.defaultTheme.desc": "Set a default theme that applies to all new users on the system.", + "admin.experimental.defaultTheme.options.denim": "Denim", + "admin.experimental.defaultTheme.options.indigo": "Indigo", + "admin.experimental.defaultTheme.options.onyx": "Onyx", + "admin.experimental.defaultTheme.options.quartz": "Quartz", + "admin.experimental.defaultTheme.options.sapphire": "Sapphire", "admin.experimental.defaultTheme.title": "Default Theme:", "admin.experimental.delayChannelAutocomplete.desc": "When true, the autocomplete for channel links (such as ~town-square) will only trigger after typing a tilde followed by a couple letters. When false, the autocomplete will appear as soon as the user types a tilde.", "admin.experimental.delayChannelAutocomplete.title": "Delay Channel Autocomplete:", @@ -997,27 +1004,6 @@ "admin.feature_flags.title": "Feature Flags", "admin.featureDiscovery.WarningDescription": "Your License is being updated to give you full access to all the Enterprise Features. This page will automatically refresh once the license update is complete. Please wait ", "admin.featureDiscovery.WarningTitle": "Your trial has started and updates are being made to your license.", - "admin.field_names.allowBannerDismissal": "Allow banner dismissal", - "admin.field_names.bannerColor": "Banner color", - "admin.field_names.bannerText": "Banner text", - "admin.field_names.bannerTextColor": "Banner text color", - "admin.field_names.enableBanner": "Enable Announcement banner", - "admin.field_names.enableCommands": "Enable Custom Slash Commands", - "admin.field_names.enableConfirmNotificationsToChannel": "Show @channel, @all, @here confirmation dialog", - "admin.field_names.enableIncomingWebhooks": "Enable Incoming Webhooks", - "admin.field_names.enableOAuthServiceProvider": "Enable OAuth 2.0 Service Provider", - "admin.field_names.enableOutgoingWebhooks": "Enable Outgoing Webhooks", - "admin.field_names.enablePostIconOverride": "Enable integrations to override profile picture icons", - "admin.field_names.enablePostUsernameOverride": "Enable integrations to override usernames", - "admin.field_names.enableUserAccessTokens": "Enable Personal Access Tokens", - "admin.field_names.enableUserCreation": "Enable Account Creation", - "admin.field_names.maxChannelsPerTeam": "Max Channels Per Team", - "admin.field_names.maxNotificationsPerChannel": "Max Notifications Per Channel", - "admin.field_names.maxUsersPerTeam": "Max Users Per Team", - "admin.field_names.postEditTimeLimit": "Edit post time limit", - "admin.field_names.restrictCreationToDomains": "Restrict account creation to specified email domains", - "admin.field_names.restrictDirectMessage": "Enable users to open Direct Message channels with", - "admin.field_names.teammateNameDisplay": "Teammate Name Display", "admin.file_upload.chooseFile": "Choose File", "admin.file_upload.noFile": "No file uploaded", "admin.file_upload.uploadFile": "Upload", @@ -1033,7 +1019,6 @@ "admin.filter.title": "Filter by", "admin.general.localization.availableLocalesDescription": "Set which languages are available for users in Settings > Display > Language (leave this field blank to have all supported languages available). If you're manually adding new languages, the Default Client Language must be added before saving this setting.\n \nWould like to help with translations? Join the Mattermost Translation Server to contribute.", "admin.general.localization.availableLocalesNoResults": "No results found", - "admin.general.localization.availableLocalesNotPresent": "The default client language must be included in the available list", "admin.general.localization.availableLocalesTitle": "Available Languages:", "admin.general.localization.clientLocaleDescription": "Default language for newly created users and pages where the user hasn't logged in.", "admin.general.localization.clientLocaleTitle": "Default Client Language:", @@ -1455,6 +1440,9 @@ "admin.log.jsonDescription": "When true, logged events are written in a machine readable JSON format. Otherwise they are printed as plain text. Changing this setting requires a server restart before taking effect.", "admin.log.Level": "Level", "admin.log.levelDescription": "This setting determines the level of detail at which log events are written to the console. ERROR: Outputs only error messages. INFO: Outputs error messages and information around startup and initialization. DEBUG: Prints high detail for developers working on debugging issues.", + "admin.log.levelOptions.DEBUG": "DEBUG", + "admin.log.levelOptions.ERROR": "ERROR", + "admin.log.levelOptions.INFO": "INFO", "admin.log.levelTitle": "Console Log Level:", "admin.log.locationDescription": "The location of the log files. If blank, they are stored in the ./logs directory. The path that you set must exist and Mattermost must have write permissions in it. Changing this setting requires a server restart before taking effect.", "admin.log.locationPlaceholder": "Enter your file location", @@ -1571,7 +1559,6 @@ "admin.password.preview": "Error message preview", "admin.password.symbol": "At least one symbol (e.g. \"~!@#$%^&*()\")", "admin.password.uppercase": "At least one uppercase letter", - "admin.permissions.documentationLinkText": "documentation", "admin.permissions.group.convert_public_channel_to_private.description": "Convert public channels to private", "admin.permissions.group.convert_public_channel_to_private.name": "Convert Channels", "admin.permissions.group.custom_groups.description": "Create, edit, delete and manage the members of custom groups.", @@ -4097,27 +4084,22 @@ "mobile.set_status.offline.icon": "Offline Icon", "mobile.set_status.online.icon": "Online Icon", "modal.manual_status.ask": "Do not ask me again", - "modal.manual_status.auto_responder.message_": "Would you like to switch your status to \"{status}\" and disable Automatic Replies?", "modal.manual_status.auto_responder.message_away": "Would you like to switch your status to \"Away\" and disable Automatic Replies?", "modal.manual_status.auto_responder.message_dnd": "Would you like to switch your status to \"Do Not Disturb\" and disable Automatic Replies?", "modal.manual_status.auto_responder.message_offline": "Would you like to switch your status to \"Offline\" and disable Automatic Replies?", "modal.manual_status.auto_responder.message_online": "Would you like to switch your status to \"Online\" and disable Automatic Replies?", - "modal.manual_status.button_": "Yes, set my status to \"{status}\"", "modal.manual_status.button_away": "Yes, set my status to \"Away\"", "modal.manual_status.button_dnd": "Yes, set my status to \"Do Not Disturb\"", "modal.manual_status.button_offline": "Yes, set my status to \"Offline\"", "modal.manual_status.button_online": "Yes, set my status to \"Online\"", - "modal.manual_status.cancel_": "No, keep it as \"{status}\"", "modal.manual_status.cancel_away": "No, keep it as \"Away\"", "modal.manual_status.cancel_dnd": "No, keep it as \"Do Not Disturb\"", "modal.manual_status.cancel_offline": "No, keep it as \"Offline\"", "modal.manual_status.cancel_ooo": "No, keep it as \"Out of Office\"", - "modal.manual_status.message_": "Would you like to switch your status to \"{status}\"?", "modal.manual_status.message_away": "Would you like to switch your status to \"Away\"?", "modal.manual_status.message_dnd": "Would you like to switch your status to \"Do Not Disturb\"?", "modal.manual_status.message_offline": "Would you like to switch your status to \"Offline\"?", "modal.manual_status.message_online": "Would you like to switch your status to \"Online\"?", - "modal.manual_status.title_": "Your Status is Set to \"{status}\"", "modal.manual_status.title_away": "Your Status is Set to \"Away\"", "modal.manual_status.title_dnd": "Your Status is Set to \"Do Not Disturb\"", "modal.manual_status.title_offline": "Your Status is Set to \"Offline\"", diff --git a/webapp/channels/src/utils/__snapshots__/admin_console_plugin_index.test.ts.snap b/webapp/channels/src/utils/__snapshots__/admin_console_plugin_index.test.ts.snap index 2969e47500..523d03a19c 100644 --- a/webapp/channels/src/utils/__snapshots__/admin_console_plugin_index.test.ts.snap +++ b/webapp/channels/src/utils/__snapshots__/admin_console_plugin_index.test.ts.snap @@ -3,8 +3,8 @@ exports[`AdminConsolePluginsIndex.getPluginEntries should return map with the text extracted from plugins 1`] = ` Object { "plugin_mattermost-autolink": Array [ - "admin.plugin.enable_plugin", - "admin.plugin.enable_plugin.help", + "Enable Plugin: ", + "When true, this plugin is enabled.", "PluginSettings.PluginStates.mattermost-autolink.Enable", "Autolink", "mattermost-autolink", @@ -18,8 +18,8 @@ Object { exports[`AdminConsolePluginsIndex.getPluginEntries should return map with the text extracted from plugins 2`] = ` Object { "plugin_Some-random-plugin": Array [ - "admin.plugin.enable_plugin", - "admin.plugin.enable_plugin.help", + "Enable Plugin: ", + "When true, this plugin is enabled.", "PluginSettings.PluginStates.Some-random-plugin.Enable", "Random", "Some-random-plugin", @@ -32,8 +32,8 @@ Object { "setRange", ], "plugin_mattermost-autolink": Array [ - "admin.plugin.enable_plugin", - "admin.plugin.enable_plugin.help", + "Enable Plugin: ", + "When true, this plugin is enabled.", "PluginSettings.PluginStates.mattermost-autolink.Enable", "Autolink", "mattermost-autolink", diff --git a/webapp/channels/src/utils/admin_console_index.tsx b/webapp/channels/src/utils/admin_console_index.tsx index 55cad29dc6..4779aa00d5 100644 --- a/webapp/channels/src/utils/admin_console_index.tsx +++ b/webapp/channels/src/utils/admin_console_index.tsx @@ -2,11 +2,12 @@ // See LICENSE.txt for license information. import FlexSearch from 'flexsearch/dist/flexsearch.es5'; -import type {IntlShape} from 'react-intl'; +import type {IntlShape, MessageDescriptor} from 'react-intl'; import type {PluginRedux} from '@mattermost/types/plugins'; import type AdminDefinition from 'components/admin_console/admin_definition'; +import type {AdminDefinitionSetting, AdminDefinitionSubSection} from 'components/admin_console/types'; import {getPluginEntries} from './admin_console_plugin_index'; @@ -28,29 +29,37 @@ export type Index = { search(query: string): string[]; } -function extractTextsFromSection(section: Record, intl: IntlShape) { - const texts: Array = []; - if (section.title) { - texts.push(intl.formatMessage({id: section.title, defaultMessage: section.title_default})); +function pushText(texts: string[], value: string | MessageDescriptor | JSX.Element, intl: IntlShape, values?: Record) { + if (typeof value === 'string') { + texts.push(value); + } else if ('id' in value) { + texts.push(intl.formatMessage(value, values)); } - if (section.schema && section.schema.name) { - texts.push(section.schema.name); +} + +function extractTextsFromSection(section: AdminDefinitionSubSection, intl: IntlShape) { + const texts: string[] = []; + if (section.title) { + pushText(texts, section.title, intl); + } + if ('name' in section.schema && section.schema.name) { + pushText(texts, section.schema.name, intl); } if (section.searchableStrings) { for (const searchableString of section.searchableStrings) { - if (typeof searchableString === 'string') { - texts.push(intl.formatMessage({id: searchableString, defaultMessage: searchableString})); + if (Array.isArray(searchableString)) { + texts.push(intl.formatMessage(searchableString[0], searchableString[1])); } else { - texts.push(intl.formatMessage({id: searchableString[0], defaultMessage: ''}, searchableString[1])); + pushText(texts, searchableString, intl); } } } if (section.schema) { - if (section.schema.settings) { - texts.push(extractTextFromSettings(section.schema.settings, intl)); - } else if (section.schema.sections) { - section.schema.sections.forEach((schemaSection: any) => { + if ('settings' in section.schema && section.schema.settings) { + texts.push(...extractTextFromSettings(section.schema.settings, intl)); + } else if ('sections' in section.schema && section.schema.sections) { + section.schema.sections.forEach((schemaSection) => { texts.push(...extractTextFromSettings(schemaSection.settings, intl)); }); } @@ -59,21 +68,21 @@ function extractTextsFromSection(section: Record, intl: IntlShape) return texts; } -function extractTextFromSettings(settings: Array>, intl: IntlShape) { - const texts = []; +function extractTextFromSettings(settings: AdminDefinitionSetting[], intl: IntlShape) { + const texts: string[] = []; for (const setting of Object.values(settings)) { if (setting.label) { - texts.push(intl.formatMessage({id: setting.label, defaultMessage: setting.label_default}, setting.label_values)); + pushText(texts, setting.label, intl, setting.label_values); } - if (setting.help_text && typeof setting.help_text === 'string') { - texts.push(intl.formatMessage({id: setting.help_text, defaultMessage: setting.help_text_default}, setting.help_text_values)); + if (setting.help_text) { + pushText(texts, setting.help_text, intl, setting.help_text_values); } - if (setting.remove_help_text) { - texts.push(intl.formatMessage({id: setting.remove_help_text, defaultMessage: setting.remove_help_text_default})); + if ('remove_help_text' in setting && setting.remove_help_text) { + pushText(texts, setting.remove_help_text, intl); } - if (setting.remove_button_text) { - texts.push(intl.formatMessage({id: setting.remove_button_text, defaultMessage: setting.remove_button_text_default})); + if ('remove_button_text' in setting && setting.remove_button_text) { + pushText(texts, setting.remove_button_text, intl); } } @@ -110,7 +119,7 @@ export function generateIndex(adminDefinition: typeof AdminDefinition, intl: Int addToIndex(adminDefinitionsToUrlsAndTexts(adminDefinition, intl), idx); - addToIndex(getPluginEntries(plugins), idx); + addToIndex(getPluginEntries(plugins, intl), idx); return idx; } diff --git a/webapp/channels/src/utils/admin_console_plugin_index.test.ts b/webapp/channels/src/utils/admin_console_plugin_index.test.ts index b76ba5f007..85af5addfd 100644 --- a/webapp/channels/src/utils/admin_console_plugin_index.test.ts +++ b/webapp/channels/src/utils/admin_console_plugin_index.test.ts @@ -1,41 +1,47 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import type {IntlShape, MessageDescriptor} from 'react-intl'; + import {samplePlugin1, samplePlugin2, samplePlugin3, samplePlugin4} from 'tests/helpers/admin_console_plugin_index_sample_pluings'; import {getPluginEntries} from './admin_console_plugin_index'; +const intl = { + formatMessage: (descriptor: MessageDescriptor) => descriptor.defaultMessage, +} as IntlShape; + describe('AdminConsolePluginsIndex.getPluginEntries', () => { it('should return an empty map in case of plugins is undefined', () => { - const entries = getPluginEntries(); + const entries = getPluginEntries(undefined, intl); expect(entries).toEqual({}); }); it('should return an empty map in case of plugins is undefined', () => { - const entries = getPluginEntries(undefined); + const entries = getPluginEntries(undefined, intl); expect(entries).toEqual({}); }); it('should return an empty map in case of plugins is empty', () => { - const entries = getPluginEntries({}); + const entries = getPluginEntries({}, intl); expect(entries).toEqual({}); }); it('should return map with the text extracted from plugins', () => { - const entries = getPluginEntries({[samplePlugin1.id]: samplePlugin1}); + const entries = getPluginEntries({[samplePlugin1.id]: samplePlugin1}, intl); expect(entries).toMatchSnapshot(); expect(entries).toHaveProperty('plugin_mattermost-autolink'); }); it('should return map with the text extracted from plugins', () => { - const entries = getPluginEntries({[samplePlugin1.id]: samplePlugin1, [samplePlugin2.id]: samplePlugin2}); + const entries = getPluginEntries({[samplePlugin1.id]: samplePlugin1, [samplePlugin2.id]: samplePlugin2}, intl); expect(entries).toMatchSnapshot(); expect(entries).toHaveProperty('plugin_mattermost-autolink'); expect(entries).toHaveProperty('plugin_Some-random-plugin'); }); it('should not return the markdown link texts', () => { - const entries = getPluginEntries({[samplePlugin3.id]: samplePlugin3}); + const entries = getPluginEntries({[samplePlugin3.id]: samplePlugin3}, intl); expect(entries).toHaveProperty('plugin_plugin-with-markdown'); expect(entries['plugin_plugin-with-markdown']).toContain('click here'); expect(entries['plugin_plugin-with-markdown']).toContain('Markdown plugin label'); @@ -43,22 +49,22 @@ describe('AdminConsolePluginsIndex.getPluginEntries', () => { }); it('should extract the text from label field', () => { - const entries = getPluginEntries({[samplePlugin3.id]: samplePlugin3}); + const entries = getPluginEntries({[samplePlugin3.id]: samplePlugin3}, intl); expect(entries).toHaveProperty('plugin_plugin-with-markdown'); expect(entries['plugin_plugin-with-markdown']).toContain('Markdown plugin label'); }); it('should index the enable plugin setting', () => { - const entries = getPluginEntries({[samplePlugin3.id]: samplePlugin3}); + const entries = getPluginEntries({[samplePlugin3.id]: samplePlugin3}, intl); expect(entries).toHaveProperty('plugin_plugin-with-markdown'); - expect(entries['plugin_plugin-with-markdown']).toContain('admin.plugin.enable_plugin'); + expect(entries['plugin_plugin-with-markdown']).toContain('Enable Plugin: '); expect(entries['plugin_plugin-with-markdown']).toContain('PluginSettings.PluginStates.plugin-with-markdown.Enable'); }); it('should index the enable plugin setting even if other settings are not present', () => { - const entries = getPluginEntries({[samplePlugin4.id]: samplePlugin4}); + const entries = getPluginEntries({[samplePlugin4.id]: samplePlugin4}, intl); expect(entries).toHaveProperty('plugin_plugin-without-settings'); - expect(entries['plugin_plugin-without-settings']).toContain('admin.plugin.enable_plugin'); + expect(entries['plugin_plugin-without-settings']).toContain('Enable Plugin: '); expect(entries['plugin_plugin-without-settings']).toContain('PluginSettings.PluginStates.plugin-without-settings.Enable'); }); }); diff --git a/webapp/channels/src/utils/admin_console_plugin_index.ts b/webapp/channels/src/utils/admin_console_plugin_index.ts index 1a45375490..d7f77fd359 100644 --- a/webapp/channels/src/utils/admin_console_plugin_index.ts +++ b/webapp/channels/src/utils/admin_console_plugin_index.ts @@ -1,6 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import type {IntlShape, MessageDescriptor} from 'react-intl'; + import type {PluginRedux, PluginSetting} from '@mattermost/types/plugins'; import getEnablePluginSetting from 'components/admin_console/custom_plugin_settings/enable_plugin_setting'; @@ -8,8 +10,8 @@ import type {AdminDefinitionSetting} from 'components/admin_console/types'; import {stripMarkdown} from 'utils/markdown'; -function extractTextsFromPlugin(plugin: PluginRedux) { - const texts = extractTextFromSetting(getEnablePluginSetting(plugin)); +function extractTextsFromPlugin(plugin: PluginRedux, intl: IntlShape) { + const texts = extractTextFromSetting(getEnablePluginSetting(plugin), intl); if (plugin.name) { texts.push(plugin.name); } @@ -28,7 +30,7 @@ function extractTextsFromPlugin(plugin: PluginRedux) { const settings = Object.values(plugin.settings_schema.settings); for (const setting of settings) { - const settingsTexts = extractTextFromSetting(setting as Partial); + const settingsTexts = extractTextFromSetting(setting as Partial, intl); texts.push(...settingsTexts); } } @@ -36,29 +38,40 @@ function extractTextsFromPlugin(plugin: PluginRedux) { return texts; } -function extractTextFromSetting(setting: Partial) { - const texts = []; - if (setting.label) { - texts.push(setting.label); +function pushString(texts: string[], value: string | MessageDescriptor | undefined, intl: IntlShape, shouldStripMarkdown?: boolean) { + let newValue; + if (value) { + if (typeof value === 'string') { + newValue = value; + } else { + newValue = intl.formatMessage(value); + } } - if (setting.display_name) { - texts.push(setting.display_name); + + if (newValue && shouldStripMarkdown) { + newValue = stripMarkdown(newValue); } - if (setting.help_text) { - texts.push(stripMarkdown(setting.help_text)); - } - if (setting.key) { - texts.push(setting.key); + + if (newValue) { + texts.push(newValue); } +} + +function extractTextFromSetting(setting: Partial, intl: IntlShape) { + const texts: string[] = []; + pushString(texts, setting.label, intl); + pushString(texts, setting.display_name, intl); + pushString(texts, setting.help_text, intl, true); + pushString(texts, setting.key, intl); return texts; } -export function getPluginEntries(pluginsObj?: Record) { +export function getPluginEntries(pluginsObj: Record | undefined, intl: IntlShape) { const entries: Record = {}; const plugins = pluginsObj || {}; for (const pluginId of Object.keys(plugins)) { const url = `plugin_${pluginId}`; - entries[url] = extractTextsFromPlugin(plugins[pluginId]); + entries[url] = extractTextsFromPlugin(plugins[pluginId], intl); } return entries; } diff --git a/webapp/channels/src/utils/constants.tsx b/webapp/channels/src/utils/constants.tsx index a7eb36fbd7..c99223d608 100644 --- a/webapp/channels/src/utils/constants.tsx +++ b/webapp/channels/src/utils/constants.tsx @@ -31,23 +31,23 @@ import logoWebhook from 'images/webhook_icon.jpg'; import {t} from 'utils/i18n'; export const SettingsTypes = { - TYPE_TEXT: 'text', - TYPE_LONG_TEXT: 'longtext', - TYPE_NUMBER: 'number', - TYPE_COLOR: 'color', - TYPE_BOOL: 'bool', - TYPE_PERMISSION: 'permission', - TYPE_RADIO: 'radio', - TYPE_BANNER: 'banner', - TYPE_DROPDOWN: 'dropdown', - TYPE_GENERATED: 'generated', - TYPE_USERNAME: 'username', - TYPE_BUTTON: 'button', - TYPE_LANGUAGE: 'language', - TYPE_JOBSTABLE: 'jobstable', - TYPE_FILE_UPLOAD: 'fileupload', - TYPE_CUSTOM: 'custom', - TYPE_ROLES: 'roles', + TYPE_TEXT: 'text' as const, + TYPE_LONG_TEXT: 'longtext' as const, + TYPE_NUMBER: 'number' as const, + TYPE_COLOR: 'color' as const, + TYPE_BOOL: 'bool' as const, + TYPE_PERMISSION: 'permission' as const, + TYPE_RADIO: 'radio' as const, + TYPE_BANNER: 'banner' as const, + TYPE_DROPDOWN: 'dropdown' as const, + TYPE_GENERATED: 'generated' as const, + TYPE_USERNAME: 'username' as const, + TYPE_BUTTON: 'button' as const, + TYPE_LANGUAGE: 'language' as const, + TYPE_JOBSTABLE: 'jobstable' as const, + TYPE_FILE_UPLOAD: 'fileupload' as const, + TYPE_CUSTOM: 'custom' as const, + TYPE_ROLES: 'roles' as const, }; export const InviteTypes = { diff --git a/webapp/channels/src/utils/policy_roles_adapter.test.ts b/webapp/channels/src/utils/policy_roles_adapter.test.ts index e1e5894caa..a1b299cb36 100644 --- a/webapp/channels/src/utils/policy_roles_adapter.test.ts +++ b/webapp/channels/src/utils/policy_roles_adapter.test.ts @@ -9,7 +9,7 @@ import {rolesFromMapping, mappingValueFromRoles} from 'utils/policy_roles_adapte describe('PolicyRolesAdapter', () => { let roles: Record = {}; - let policies: Record = {}; + let policies: Record = {}; beforeEach(() => { roles = { diff --git a/webapp/channels/src/utils/policy_roles_adapter.ts b/webapp/channels/src/utils/policy_roles_adapter.ts index ee059cd0ab..bfe538ccac 100644 --- a/webapp/channels/src/utils/policy_roles_adapter.ts +++ b/webapp/channels/src/utils/policy_roles_adapter.ts @@ -53,7 +53,7 @@ type MappingValueTypes = {roleName: string; * @param {object} roles same structure as returned by mattermost-redux `getRoles`. * @return {object} the updated roles (only) in the same structure as returned by mattermost-redux `getRoles`. */ -export function rolesFromMapping(mappingValues: Record, roles: Record): Record { +export function rolesFromMapping(mappingValues: Record, roles: Record): Record { const rolesClone: Record = JSON.parse(JSON.stringify(roles)); // Purge roles that aren't present in MAPPING, we don't care about them. @@ -62,7 +62,7 @@ export function rolesFromMapping(mappingValues: Record, roles: Reco Object.keys(MAPPING).forEach((mappingKey) => { const value = mappingValues[mappingKey]; if (value) { - mutateRolesBasedOnMapping(mappingKey as MappingKeyTypes, value, rolesClone); + mutateRolesBasedOnMapping(mappingKey as MappingKeyTypes, value as any, rolesClone); } }); diff --git a/webapp/platform/types/src/plugins.ts b/webapp/platform/types/src/plugins.ts index db8a98cb31..8bc524cbca 100644 --- a/webapp/platform/types/src/plugins.ts +++ b/webapp/platform/types/src/plugins.ts @@ -1,6 +1,12 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +// Equivalent to MessageDescriptor from react-intl +type MessageDescriptor = { + id: string; + defaultMessage: string; +} + export type PluginManifest = { id: string; name: string; @@ -44,7 +50,7 @@ export type PluginSetting = { key: string; display_name: string; type: string; - help_text: string; + help_text: string | MessageDescriptor; regenerate_help_text?: string; placeholder: string; default: any;