mirror of
https://github.com/mattermost/mattermost.git
synced 2026-09-21 14:20:41 +08:00
[MM-69647] Remove CloudDedicatedExportUI feature flag and dead code (#37836)
The CloudDedicatedExportUI flag (default false, shipped v9.7.0) has been off everywhere since it shipped. It gated the Cloud Enterprise System Console "Export Storage" admin UI and the accompanying Test Connection resolution that picked the ExportDriverName/Export* credential fields over the primary file-store fields. Per the decision recorded on MM-69647, remove the feature. Because App.UseExportFileStore() has always returned false (the flag never flipped on), simply dropping the flag term would have left IsCloud() && DedicatedExportStore -- silently activating the never- exercised Export* resolution for cloud servers. Instead revert the MM-68854 dispatch entirely and lock in the long-standing primary-field behavior: - Server: remove UseExportFileStore()/ResolvedFileStoreDriverName(), collapse CheckMandatoryS3Fields/CheckMandatoryAzureFields and TestFileStoreConnectionWithConfig to the primary file-store fields, and restore testFileStore's primary DriverName dispatch. Drop the obsolete TestFileStoreTestConnection test. - Webapp: remove the "Export Storage" admin console section, its en.json strings, and the admin_sidebar fixtures/snapshot entry. - Remove the struct field, its SetDefaults entry, and the e2e mirror. The underlying FileSettings.DedicatedExportStore config and Export* fields are kept -- they remain a live config-file/env feature used by bulk and compliance exports, independent of the removed UI.
This commit is contained in:
@@ -804,7 +804,6 @@ const defaultServerConfig: AdminConfig = {
|
||||
WysiwygEditor: false,
|
||||
EnableExportDirectDownload: false,
|
||||
MoveThreadsEnabled: false,
|
||||
CloudDedicatedExportUI: false,
|
||||
NotificationMonitoring: true,
|
||||
CustomProfileAttributes: true,
|
||||
AttributeValueMasking: false,
|
||||
|
||||
@@ -591,12 +591,10 @@ func testFileStore(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
// Validate mandatory fields per driver. TestFileStoreConnectionWithConfig
|
||||
// will catch missing fields by failing to construct the backend, but a
|
||||
// dedicated validation step lets us surface a clearer error.
|
||||
//
|
||||
// When the dedicated export filestore is active the backend that is
|
||||
// actually built and tested is the export backend, so we have to dispatch
|
||||
// on ExportDriverName -- otherwise a primary=S3 / export=Azure deployment
|
||||
// would run the S3 field check while testing the Azure backend.
|
||||
driver := c.App.ResolvedFileStoreDriverName(&cfg.FileSettings)
|
||||
driver := ""
|
||||
if cfg.FileSettings.DriverName != nil {
|
||||
driver = *cfg.FileSettings.DriverName
|
||||
}
|
||||
switch driver {
|
||||
case model.ImageDriverLocal:
|
||||
// Local driver has no mandatory fields beyond the directory, which has a default.
|
||||
|
||||
@@ -826,87 +826,6 @@ func TestS3TestConnection(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestFileStoreTestConnection(t *testing.T) {
|
||||
mainHelper.Parallel(t)
|
||||
th := SetupConfig(t, func(cfg *model.Config) {
|
||||
cfg.FeatureFlags.CloudDedicatedExportUI = true
|
||||
cfg.FileSettings.DedicatedExportStore = model.NewPointer(true)
|
||||
})
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
||||
t.Cleanup(func() { th.App.Srv().SetLicense(nil) })
|
||||
|
||||
baseSettings := func(primary, export string) model.FileSettings {
|
||||
fs := model.FileSettings{}
|
||||
fs.SetDefaults(false)
|
||||
fs.DriverName = model.NewPointer(primary)
|
||||
fs.ExportDriverName = model.NewPointer(export)
|
||||
return fs
|
||||
}
|
||||
|
||||
t.Run("primary=local, export=azure: missing ExportAzureContainer surfaces azure error", func(t *testing.T) {
|
||||
fs := baseSettings(model.ImageDriverLocal, model.ImageDriverAzure)
|
||||
fs.ExportAzureStorageAccount = model.NewPointer("acmemattermost")
|
||||
fs.ExportAzureAccessKey = model.NewPointer("secret")
|
||||
fs.ExportAzureContainer = model.NewPointer("")
|
||||
|
||||
resp, err := th.SystemAdminClient.TestS3Connection(context.Background(), &model.Config{FileSettings: fs})
|
||||
require.Error(t, err)
|
||||
CheckErrorID(t, err, "api.admin.test_azure.missing_azure_field")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
})
|
||||
|
||||
t.Run("primary=s3, export=azure: missing ExportAzureStorageAccount surfaces azure error", func(t *testing.T) {
|
||||
fs := baseSettings(model.ImageDriverS3, model.ImageDriverAzure)
|
||||
fs.ExportAzureStorageAccount = model.NewPointer("")
|
||||
fs.ExportAzureContainer = model.NewPointer("mattermost")
|
||||
fs.ExportAzureAccessKey = model.NewPointer("secret")
|
||||
|
||||
resp, err := th.SystemAdminClient.TestS3Connection(context.Background(), &model.Config{FileSettings: fs})
|
||||
require.Error(t, err)
|
||||
CheckErrorID(t, err, "api.admin.test_azure.missing_azure_field")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
})
|
||||
|
||||
t.Run("primary=local, export=s3: missing ExportAmazonS3Bucket surfaces s3 error", func(t *testing.T) {
|
||||
fs := baseSettings(model.ImageDriverLocal, model.ImageDriverS3)
|
||||
fs.ExportAmazonS3Bucket = model.NewPointer("")
|
||||
|
||||
resp, err := th.SystemAdminClient.TestS3Connection(context.Background(), &model.Config{FileSettings: fs})
|
||||
require.Error(t, err)
|
||||
CheckErrorID(t, err, "api.admin.test_s3.missing_s3_bucket")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
})
|
||||
|
||||
t.Run("unsupported export driver", func(t *testing.T) {
|
||||
fs := baseSettings(model.ImageDriverS3, "bogus")
|
||||
|
||||
resp, err := th.SystemAdminClient.TestS3Connection(context.Background(), &model.Config{FileSettings: fs})
|
||||
require.Error(t, err)
|
||||
CheckErrorID(t, err, "api.file.test_connection_unsupported_driver.app_error")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
})
|
||||
|
||||
t.Run("no license - primary dispatch", func(t *testing.T) {
|
||||
// No license is set, so UseExportFileStore() must return false. Set an
|
||||
// ExportDriverName that, if it were honored, would change the dispatch
|
||||
// outcome -- this asserts that the primary path is taken.
|
||||
appErr := th.App.Srv().RemoveLicense()
|
||||
require.Nil(t, appErr)
|
||||
fs := baseSettings(model.ImageDriverAzure, model.ImageDriverS3)
|
||||
|
||||
// fs.AzureStorageAccount = model.NewPointer("")
|
||||
// fs.AzureContainer = model.NewPointer("mattermost")
|
||||
// fs.AzureAccessKey = model.NewPointer("secret")
|
||||
// fs.ExportAmazonS3Bucket = model.NewPointer("export-bucket")
|
||||
|
||||
resp, err := th.SystemAdminClient.TestS3Connection(context.Background(), &model.Config{FileSettings: fs})
|
||||
require.Error(t, err)
|
||||
CheckErrorID(t, err, "api.admin.test_azure.missing_azure_field")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSupportedTimezones(t *testing.T) {
|
||||
mainHelper.Parallel(t)
|
||||
th := Setup(t)
|
||||
|
||||
@@ -59,43 +59,8 @@ func (a *App) ExportFileBackend() filestore.FileBackend {
|
||||
return a.ch.exportFilestore
|
||||
}
|
||||
|
||||
// UseExportFileStore reports whether the dedicated export filestore is
|
||||
// active. When true, callers reading the filestore configuration should
|
||||
// resolve the export-side fields (ExportDriverName, ExportAmazonS3*,
|
||||
// ExportAzure*, ...) rather than the primary fields.
|
||||
func (a *App) UseExportFileStore() bool {
|
||||
if !a.License().IsCloud() {
|
||||
return false
|
||||
}
|
||||
if !a.Config().FeatureFlags.CloudDedicatedExportUI {
|
||||
return false
|
||||
}
|
||||
dedicated := a.Config().FileSettings.DedicatedExportStore
|
||||
return dedicated != nil && *dedicated
|
||||
}
|
||||
|
||||
// ResolvedFileStoreDriverName returns the driver name that callers should
|
||||
// read for the active filestore -- ExportDriverName when the dedicated
|
||||
// export filestore is enabled, otherwise the primary DriverName. The empty
|
||||
// string is returned when neither pointer is set so callers can produce a
|
||||
// dedicated "unsupported driver" error.
|
||||
func (a *App) ResolvedFileStoreDriverName(settings *model.FileSettings) string {
|
||||
name := settings.DriverName
|
||||
if a.UseExportFileStore() {
|
||||
name = settings.ExportDriverName
|
||||
}
|
||||
|
||||
if name == nil {
|
||||
return ""
|
||||
}
|
||||
return *name
|
||||
}
|
||||
|
||||
func (a *App) CheckMandatoryS3Fields(settings *model.FileSettings) *model.AppError {
|
||||
bucket := settings.AmazonS3Bucket
|
||||
if a.UseExportFileStore() {
|
||||
bucket = settings.ExportAmazonS3Bucket
|
||||
}
|
||||
if bucket == nil || *bucket == "" {
|
||||
return model.NewAppError("CheckMandatoryS3Fields", "api.admin.test_s3.missing_s3_bucket", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
@@ -107,12 +72,6 @@ func (a *App) CheckMandatoryAzureFields(settings *model.FileSettings) *model.App
|
||||
authMode := settings.AzureAuthMode
|
||||
accessKey := settings.AzureAccessKey
|
||||
container := settings.AzureContainer
|
||||
if a.UseExportFileStore() {
|
||||
storageAccount = settings.ExportAzureStorageAccount
|
||||
authMode = settings.ExportAzureAuthMode
|
||||
accessKey = settings.ExportAzureAccessKey
|
||||
container = settings.ExportAzureContainer
|
||||
}
|
||||
if storageAccount == nil || *storageAccount == "" {
|
||||
return model.NewAppError("CheckMandatoryAzureFields", "api.admin.test_azure.missing_azure_field", nil, "missing azure storage account setting", http.StatusBadRequest)
|
||||
}
|
||||
@@ -162,11 +121,7 @@ func (a *App) TestFileStoreConnectionWithConfig(cfg *model.FileSettings) *model.
|
||||
complianceEnabled := license != nil && *license.Features.Compliance
|
||||
allowInsecure := insecure != nil && *insecure
|
||||
allowedUntrustedInternalConnections := model.SafeDereference(a.Config().ServiceSettings.AllowedUntrustedInternalConnections)
|
||||
if a.UseExportFileStore() {
|
||||
backend, err = filestore.NewFileBackend(filestore.NewExportFileBackendSettingsFromConfig(cfg, complianceEnabled && license.IsCloud(), allowInsecure, allowedUntrustedInternalConnections))
|
||||
} else {
|
||||
backend, err = filestore.NewFileBackend(filestore.NewFileBackendSettingsFromConfig(cfg, complianceEnabled, allowInsecure, allowedUntrustedInternalConnections))
|
||||
}
|
||||
backend, err = filestore.NewFileBackend(filestore.NewFileBackendSettingsFromConfig(cfg, complianceEnabled, allowInsecure, allowedUntrustedInternalConnections))
|
||||
if err != nil {
|
||||
return model.NewAppError("FileAttachmentBackend", "api.file.no_driver.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
@@ -34,8 +34,6 @@ type FeatureFlags struct {
|
||||
|
||||
MoveThreadsEnabled bool
|
||||
|
||||
CloudDedicatedExportUI bool
|
||||
|
||||
NotificationMonitoring bool
|
||||
|
||||
CustomProfileAttributes bool
|
||||
@@ -165,7 +163,6 @@ func (f *FeatureFlags) SetDefaults() {
|
||||
f.WysiwygEditor = false
|
||||
f.EnableExportDirectDownload = false
|
||||
f.MoveThreadsEnabled = false
|
||||
f.CloudDedicatedExportUI = false
|
||||
f.NotificationMonitoring = true
|
||||
f.CustomProfileAttributes = true
|
||||
f.AttributeValueMasking = true
|
||||
|
||||
@@ -1592,352 +1592,6 @@ const AdminDefinition: AdminDefinitionType = {
|
||||
],
|
||||
},
|
||||
},
|
||||
export_storage: {
|
||||
url: 'environment/export_storage',
|
||||
title: defineMessage({id: 'admin.sidebar.exportStorage', defaultMessage: 'Export Storage'}),
|
||||
isHidden: it.any(
|
||||
it.not(it.licensedForFeature('Cloud')),
|
||||
it.not(it.minLicenseTier(LicenseSkus.Enterprise)),
|
||||
it.configIsFalse('FeatureFlags', 'CloudDedicatedExportUI'),
|
||||
),
|
||||
schema: {
|
||||
id: 'ExportFileSettings',
|
||||
name: defineMessage({id: 'admin.sidebar.exportStorage', defaultMessage: 'Export Storage'}),
|
||||
settings: [
|
||||
{
|
||||
type: 'bool',
|
||||
key: 'FileSettings.DedicatedExportStore',
|
||||
label: defineMessage({id: 'admin.exportStorage.dedicatedExportStore', defaultMessage: 'Enable Dedicated Export Store:'}),
|
||||
help_text: defineMessage({id: 'admin.exportStorage.dedicatedExportStoreDescription', defaultMessage: 'When enabled, Mattermost will use a dedicated export storage bucket for all export operations. This is required for Mattermost Cloud deployments.'}),
|
||||
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
},
|
||||
{
|
||||
type: 'dropdown',
|
||||
key: 'FileSettings.ExportDriverName',
|
||||
label: defineMessage({id: 'admin.exportStorage.exportDriverName', defaultMessage: 'Export Storage Driver:'}),
|
||||
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
isHidden: it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
options: [
|
||||
{
|
||||
value: FILE_STORAGE_DRIVER_S3,
|
||||
display_name: defineMessage({id: 'admin.image.storeAmazonS3', defaultMessage: 'Amazon S3'}),
|
||||
},
|
||||
{
|
||||
value: FILE_STORAGE_DRIVER_AZURE,
|
||||
display_name: defineMessage({id: 'admin.image.storeAzureBlob', defaultMessage: 'Azure Blob Storage'}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'FileSettings.ExportDirectory',
|
||||
label: defineMessage({id: 'admin.exportStorage.exportDirectory', defaultMessage: 'Export Directory'}),
|
||||
help_text: defineMessage({id: 'admin.image.exportDirectoryDescription', defaultMessage: 'Directory to which files 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.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'FileSettings.ExportAmazonS3AccessKeyId',
|
||||
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 <link>IAM role</link>. Enter the Access Key ID provided by your Amazon EC2 administrator.'}), // eslint-disable-line formatjs/enforce-placeholders -- link provided via help_text_values
|
||||
help_text_values: {
|
||||
link: (msg: string) => (
|
||||
<ExternalLink
|
||||
location='admin_console'
|
||||
href='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2_instance-profiles.html'
|
||||
>
|
||||
{msg}
|
||||
</ExternalLink>
|
||||
),
|
||||
},
|
||||
help_text_markdown: false,
|
||||
placeholder: defineMessage({id: 'admin.image.amazonS3IdExample', defaultMessage: 'E.g.: "AKIADTOVBGERKLCBV"'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'FileSettings.ExportAmazonS3SecretAccessKey',
|
||||
label: defineMessage({id: 'admin.image.amazonS3SecretTitle', defaultMessage: 'Amazon S3 Secret Access Key:'}),
|
||||
help_text: defineMessage({id: 'admin.image.amazonS3SecretDescription', defaultMessage: '(Optional) The secret access key associated with your Amazon S3 Access Key ID.'}),
|
||||
placeholder: defineMessage({id: 'admin.image.amazonS3SecretExample', defaultMessage: 'E.g.: "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'FileSettings.ExportAmazonS3Bucket',
|
||||
label: defineMessage({id: 'admin.image.amazonS3BucketTitle', defaultMessage: 'Amazon S3 Bucket:'}),
|
||||
help_text: defineMessage({id: 'admin.image.amazonS3BucketDescription', defaultMessage: 'Name you selected for your S3 bucket in AWS.'}),
|
||||
placeholder: defineMessage({id: 'admin.image.amazonS3BucketExampleExport', defaultMessage: 'E.g.: "mattermost-export"'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'FileSettings.ExportAmazonS3PathPrefix',
|
||||
label: defineMessage({id: 'admin.image.amazonS3PathPrefixTitle', defaultMessage: 'Amazon S3 Path Prefix:'}),
|
||||
help_text: defineMessage({id: 'admin.image.amazonS3PathPrefixDescription', defaultMessage: 'Prefix you selected for your S3 bucket in AWS.'}),
|
||||
placeholder: defineMessage({id: 'admin.image.amazonS3PathPrefixExample', defaultMessage: 'E.g.: "subdir1" or you can leave it empty.'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'FileSettings.ExportAmazonS3Region',
|
||||
label: defineMessage({id: 'admin.image.amazonS3RegionTitle', defaultMessage: 'Amazon S3 Region:'}),
|
||||
help_text: defineMessage({id: 'admin.image.amazonS3RegionDescription', defaultMessage: 'AWS region you selected when creating your S3 bucket. If no region is set, Mattermost attempts to get the appropriate region from AWS, or sets it to "us-east-1" if none found.'}),
|
||||
placeholder: defineMessage({id: 'admin.image.amazonS3RegionExample', defaultMessage: 'E.g.: "us-east-1"'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'FileSettings.ExportAmazonS3Endpoint',
|
||||
label: defineMessage({id: 'admin.image.amazonS3EndpointTitle', defaultMessage: 'Amazon S3 Endpoint:'}),
|
||||
help_text: defineMessage({id: 'admin.image.amazonS3EndpointDescription', defaultMessage: 'Hostname of your S3 Compatible Storage provider. Defaults to "s3.amazonaws.com".'}),
|
||||
placeholder: defineMessage({id: 'admin.image.amazonS3EndpointExample', defaultMessage: 'E.g.: "s3.amazonaws.com"'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
},
|
||||
{
|
||||
type: 'bool',
|
||||
key: 'FileSettings.ExportAmazonS3SSL',
|
||||
label: defineMessage({id: 'admin.image.amazonS3SSLTitle', defaultMessage: 'Enable Secure Amazon S3 Connections:'}),
|
||||
help_text: defineMessage({id: 'admin.image.amazonS3SSLDescription', defaultMessage: 'When false, allow insecure connections to Amazon S3. Defaults to secure connections only.'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
},
|
||||
{
|
||||
type: 'bool',
|
||||
key: 'FileSettings.ExportAmazonSignV2',
|
||||
label: defineMessage({id: 'admin.image.amazonS3SignV2', defaultMessage: 'Enable Sign V2'}),
|
||||
help_text: defineMessage({id: 'admin.image.amazonS3SignV2Description', defaultMessage: 'When true, use Sign V2 for Amazon S3 connections'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
},
|
||||
{
|
||||
type: 'bool',
|
||||
key: 'FileSettings.ExportAmazonS3SSE',
|
||||
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 <link>documentation</link> to learn more.'}), // eslint-disable-line formatjs/enforce-placeholders -- link provided via help_text_values
|
||||
help_text_values: {
|
||||
link: (msg: string) => (
|
||||
<ExternalLink
|
||||
location='admin_console'
|
||||
href={DocLinks.SESSION_LENGTHS}
|
||||
>
|
||||
{msg}
|
||||
</ExternalLink>
|
||||
),
|
||||
},
|
||||
help_text_markdown: false,
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'FileSettings.ExportAmazonS3StorageClass',
|
||||
label: defineMessage({id: 'admin.image.amazonS3StorageClassTitle', defaultMessage: 'Amazon S3 Storage Class:'}),
|
||||
help_text: defineMessage({id: 'admin.image.amazonS3StorageClassDescription', defaultMessage: 'Storage class for your S3 Compatible Storage provider. Defaults to empty.'}),
|
||||
placeholder: defineMessage({id: 'admin.image.amazonS3StorageClassExample', defaultMessage: 'E.g.: "STANDARD" or "STANDARD_IA"'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
},
|
||||
{
|
||||
type: 'dropdown',
|
||||
key: 'FileSettings.ExportAzureCloud',
|
||||
label: defineMessage({id: 'admin.image.azureCloudTitle', defaultMessage: 'Azure Cloud:'}),
|
||||
help_text: defineMessage({id: 'admin.image.azureCloudDescription', defaultMessage: 'The Azure cloud to connect to. Choose "Azure Commercial" or "Azure Government" to use the well-known endpoint for that cloud; only the storage account name is required. Choose "Custom Endpoint" to point at an arbitrary host such as Azurite, a reverse proxy, or any other Azure cloud (for example Azure China).'}),
|
||||
options: [
|
||||
{
|
||||
value: AZURE_CLOUD_COMMERCIAL,
|
||||
display_name: defineMessage({id: 'admin.image.azureCloudCommercial', defaultMessage: 'Azure Commercial'}),
|
||||
},
|
||||
{
|
||||
value: AZURE_CLOUD_GOVERNMENT,
|
||||
display_name: defineMessage({id: 'admin.image.azureCloudGovernment', defaultMessage: 'Azure Government'}),
|
||||
},
|
||||
{
|
||||
value: AZURE_CLOUD_CUSTOM,
|
||||
display_name: defineMessage({id: 'admin.image.azureCloudCustom', defaultMessage: 'Custom Endpoint'}),
|
||||
},
|
||||
],
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'FileSettings.ExportAzureStorageAccount',
|
||||
label: defineMessage({id: 'admin.image.azureStorageAccountTitle', defaultMessage: 'Azure Storage Account:'}),
|
||||
help_text: defineMessage({id: 'admin.image.azureStorageAccountDescription', defaultMessage: 'The name of your Azure Storage account.'}),
|
||||
placeholder: defineMessage({id: 'admin.image.azureStorageAccountExample', defaultMessage: 'E.g.: "mattermoststorage"'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'FileSettings.ExportAzureContainer',
|
||||
label: defineMessage({id: 'admin.image.azureContainerTitle', defaultMessage: 'Azure Container:'}),
|
||||
help_text: defineMessage({id: 'admin.image.azureContainerExportDescription', defaultMessage: 'Name of the container in your Azure Storage account.'}),
|
||||
placeholder: defineMessage({id: 'admin.image.azureContainerExportExample', defaultMessage: 'E.g.: "mattermost-export"'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'FileSettings.ExportAzurePathPrefix',
|
||||
label: defineMessage({id: 'admin.image.azurePathPrefixTitle', defaultMessage: 'Azure Path Prefix:'}),
|
||||
help_text: defineMessage({id: 'admin.image.azurePathPrefixDescription', defaultMessage: 'Optional path prefix to use for blobs in your Azure container.'}),
|
||||
placeholder: defineMessage({id: 'admin.image.azurePathPrefixExample', defaultMessage: 'E.g.: "files/" or leave empty'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
},
|
||||
{
|
||||
type: 'dropdown',
|
||||
key: 'FileSettings.ExportAzureAuthMode',
|
||||
label: defineMessage({id: 'admin.image.azureAuthModeTitle', defaultMessage: 'Azure Authentication:'}),
|
||||
help_text: defineMessage({id: 'admin.image.azureAuthModeDescription', defaultMessage: '"Shared key" signs requests with the Storage Account access key.\n \n"Default credential (Microsoft Entra ID)" reads the identity from the host environment - managed identity on Azure-hosted deployments, workload identity, service principal env vars, or "az login" for local development. No access key required.'}), // eslint-disable-line formatjs/no-multiple-whitespaces
|
||||
help_text_markdown: true,
|
||||
options: [
|
||||
{
|
||||
value: AZURE_AUTH_MODE_SHARED_KEY,
|
||||
display_name: defineMessage({id: 'admin.image.azureAuthModeSharedKey', defaultMessage: 'Shared key'}),
|
||||
},
|
||||
{
|
||||
value: AZURE_AUTH_MODE_DEFAULT_CREDENTIAL,
|
||||
display_name: defineMessage({id: 'admin.image.azureAuthModeDefaultCredential', defaultMessage: 'Default credential (Microsoft Entra ID)'}),
|
||||
},
|
||||
],
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'FileSettings.ExportAzureAccessKey',
|
||||
label: defineMessage({id: 'admin.image.azureAccessKeyTitle', defaultMessage: 'Azure Storage Account Key:'}),
|
||||
help_text: defineMessage({id: 'admin.image.azureAccessKeyDescription', defaultMessage: 'The shared key for your Azure Storage account.'}),
|
||||
placeholder: defineMessage({id: 'admin.image.azureAccessKeyExample', defaultMessage: 'E.g.: "9MZbtYgfq18PJ8PbRaJ5u91IH8izHvReTbcuQzMl+So="'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
it.not(it.stateEquals('FileSettings.ExportAzureAuthMode', AZURE_AUTH_MODE_SHARED_KEY)),
|
||||
),
|
||||
isHidden: it.any(
|
||||
it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
it.not(it.stateEquals('FileSettings.ExportAzureAuthMode', AZURE_AUTH_MODE_SHARED_KEY)),
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'FileSettings.ExportAzureEndpoint',
|
||||
label: defineMessage({id: 'admin.image.azureEndpointTitle', defaultMessage: 'Custom Azure Endpoint:'}),
|
||||
help_text: defineMessage({id: 'admin.image.azureEndpointDescription', defaultMessage: 'Full Blob service URL, including scheme and storage account. Mattermost does not modify this URL, so the storage account must already be embedded in the hostname (vhost-style, e.g. "https://acmemattermost.blob.core.chinacloudapi.cn/") or in the path (path-style, e.g. "http://localhost:10000/devstoreaccount1/"). Shared-key auth signs against the host this URL points at, so make sure it actually serves the storage account named above.'}),
|
||||
placeholder: defineMessage({id: 'admin.image.azureEndpointExample', defaultMessage: 'E.g.: "http://localhost:10000/devstoreaccount1/"'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
it.not(it.stateEquals('FileSettings.ExportAzureCloud', AZURE_CLOUD_CUSTOM)),
|
||||
),
|
||||
isHidden: it.any(
|
||||
it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
it.not(it.stateEquals('FileSettings.ExportAzureCloud', AZURE_CLOUD_CUSTOM)),
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'bool',
|
||||
key: 'FileSettings.ExportAzureSSL',
|
||||
label: defineMessage({id: 'admin.image.azureSSLTitle', defaultMessage: 'Enable Secure Azure Blob Storage Connections:'}),
|
||||
help_text: defineMessage({id: 'admin.image.azureSSLDescription', defaultMessage: 'When false, allow insecure connections to Azure Blob Storage. Defaults to secure connections only. Ignored for the Custom Endpoint cloud (the scheme is part of the endpoint URL).'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
it.stateEquals('FileSettings.ExportAzureCloud', AZURE_CLOUD_CUSTOM),
|
||||
),
|
||||
isHidden: it.any(
|
||||
it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
it.stateEquals('FileSettings.ExportAzureCloud', AZURE_CLOUD_CUSTOM),
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
key: 'FileSettings.ExportAzureRequestTimeoutMilliseconds',
|
||||
label: defineMessage({id: 'admin.image.azureRequestTimeoutTitle', defaultMessage: 'Azure Request Timeout (Milliseconds):'}),
|
||||
help_text: defineMessage({id: 'admin.image.azureRequestTimeoutDescription', defaultMessage: 'Number of milliseconds to wait for a response from Azure Blob Storage before timing out.'}),
|
||||
placeholder: defineMessage({id: 'admin.image.azureRequestTimeoutExample', defaultMessage: 'E.g.: "30000"'}),
|
||||
isDisabled: it.any(
|
||||
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
),
|
||||
isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)), it.stateEquals('FileSettings.DedicatedExportStore', false)),
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
action: testFileStoreConnection,
|
||||
key: 'TestFileStoreConnection',
|
||||
label: defineMessage({id: 'admin.filestore.connectionTest', defaultMessage: 'Test Connection'}),
|
||||
loading: defineMessage({id: 'admin.filestore.testing', defaultMessage: 'Testing...'}),
|
||||
error_message: defineMessage({id: 'admin.filestore.testFail', defaultMessage: 'Connection unsuccessful: {error}'}), // eslint-disable-line formatjs/enforce-placeholders -- error provided at runtime
|
||||
success_message: defineMessage({id: 'admin.filestore.testSuccess', defaultMessage: 'Connection was successful'}),
|
||||
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)),
|
||||
isHidden: it.stateEquals('FileSettings.DedicatedExportStore', false),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
image_proxy: {
|
||||
url: 'environment/image_proxy',
|
||||
title: defineMessage({id: 'admin.sidebar.imageProxy', defaultMessage: 'Image Proxy'}),
|
||||
|
||||
-38
@@ -2814,25 +2814,6 @@ c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4z M20,4v5H4V4H20z M22,15v5c0,1.1-0.9,2-2,2H
|
||||
class="nav nav__sub-menu subsections"
|
||||
/>
|
||||
</li>
|
||||
<li
|
||||
class="sidebar-section"
|
||||
data-testid="environment.export_storage"
|
||||
>
|
||||
<a
|
||||
class="sidebar-section-title"
|
||||
href="/admin_console/environment/export_storage"
|
||||
id="environment/export_storage"
|
||||
>
|
||||
<span
|
||||
class="sidebar-section-title__text"
|
||||
>
|
||||
Export Storage
|
||||
</span>
|
||||
</a>
|
||||
<ul
|
||||
class="nav nav__sub-menu subsections"
|
||||
/>
|
||||
</li>
|
||||
<li
|
||||
class="sidebar-section"
|
||||
data-testid="environment.image_proxy"
|
||||
@@ -4698,25 +4679,6 @@ c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4z M20,4v5H4V4H20z M22,15v5c0,1.1-0.9,2-2,2H
|
||||
class="nav nav__sub-menu subsections"
|
||||
/>
|
||||
</li>
|
||||
<li
|
||||
class="sidebar-section"
|
||||
data-testid="environment.export_storage"
|
||||
>
|
||||
<a
|
||||
class="sidebar-section-title"
|
||||
href="/admin_console/environment/export_storage"
|
||||
id="environment/export_storage"
|
||||
>
|
||||
<span
|
||||
class="sidebar-section-title__text"
|
||||
>
|
||||
Export Storage
|
||||
</span>
|
||||
</a>
|
||||
<ul
|
||||
class="nav nav__sub-menu subsections"
|
||||
/>
|
||||
</li>
|
||||
<li
|
||||
class="sidebar-section"
|
||||
data-testid="environment.image_proxy"
|
||||
|
||||
@@ -399,7 +399,6 @@ describe('components/AdminSidebar', () => {
|
||||
} as Office365Settings,
|
||||
FeatureFlags: {
|
||||
CustomProfileAttributes: true,
|
||||
CloudDedicatedExportUI: true,
|
||||
},
|
||||
},
|
||||
adminDefinition: AdminDefinition,
|
||||
@@ -532,7 +531,6 @@ describe('components/AdminSidebar', () => {
|
||||
} as Office365Settings,
|
||||
FeatureFlags: {
|
||||
CustomProfileAttributes: true,
|
||||
CloudDedicatedExportUI: true,
|
||||
},
|
||||
},
|
||||
adminDefinition: AdminDefinition,
|
||||
|
||||
@@ -1380,10 +1380,6 @@
|
||||
"admin.experimental.userStatusAwayTimeout.title": "User Status Away Timeout:",
|
||||
"admin.experimental.youtubeReferrerPolicy.desc": "When true, the referrer policy for embedded YouTube videos will be set to \"strict-origin-when-cross-origin\" which resolves issues where YouTube video previews display as unavailable, while balancing the need to protect user privacy with some degree of referral data to support web functionalities, like analytics, logging, and third-party integrations. When false, the referrer policy will be set to \"no-referrer\" which enhances user privacy by not disclosing the source URL, but limits the ability to track user engagement and traffic sources in analytics tools.",
|
||||
"admin.experimental.youtubeReferrerPolicy.title": "YouTube Referrer Policy:",
|
||||
"admin.exportStorage.dedicatedExportStore": "Enable Dedicated Export Store:",
|
||||
"admin.exportStorage.dedicatedExportStoreDescription": "When enabled, Mattermost will use a dedicated export storage bucket for all export operations. This is required for Mattermost Cloud deployments.",
|
||||
"admin.exportStorage.exportDirectory": "Export Directory",
|
||||
"admin.exportStorage.exportDriverName": "Export Storage Driver:",
|
||||
"admin.false": "False",
|
||||
"admin.feature_discovery.contact_sales": "Contact sales",
|
||||
"admin.feature_discovery.learn_more": "Learn more",
|
||||
@@ -1561,7 +1557,6 @@
|
||||
"admin.guest_access.whitelistedDomainsTitle": "Whitelisted Guest Domains:",
|
||||
"admin.image.amazonS3BucketDescription": "Name you selected for your S3 bucket in AWS.",
|
||||
"admin.image.amazonS3BucketExample": "E.g.: \"mattermost-media\"",
|
||||
"admin.image.amazonS3BucketExampleExport": "E.g.: \"mattermost-export\"",
|
||||
"admin.image.amazonS3BucketTitle": "Amazon S3 Bucket:",
|
||||
"admin.image.amazonS3EndpointDescription": "Hostname of your S3 Compatible Storage provider. Defaults to \"s3.amazonaws.com\".",
|
||||
"admin.image.amazonS3EndpointExample": "E.g.: \"s3.amazonaws.com\"",
|
||||
@@ -1578,8 +1573,6 @@
|
||||
"admin.image.amazonS3SecretDescription": "(Optional) The secret access key associated with your Amazon S3 Access Key ID.",
|
||||
"admin.image.amazonS3SecretExample": "E.g.: \"jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY\"",
|
||||
"admin.image.amazonS3SecretTitle": "Amazon S3 Secret Access Key:",
|
||||
"admin.image.amazonS3SignV2": "Enable Sign V2",
|
||||
"admin.image.amazonS3SignV2Description": "When true, use Sign V2 for Amazon S3 connections",
|
||||
"admin.image.amazonS3SSEDescription": "When true, encrypt files in Amazon S3 using server-side encryption with Amazon S3-managed keys. See <link>documentation</link> to learn more.",
|
||||
"admin.image.amazonS3SSETitle": "Enable Server-Side Encryption for Amazon S3:",
|
||||
"admin.image.amazonS3SSLDescription": "When false, allow insecure connections to Amazon S3. Defaults to secure connections only.",
|
||||
@@ -1605,8 +1598,6 @@
|
||||
"admin.image.azureCloudTitle": "Azure Cloud:",
|
||||
"admin.image.azureContainerDescription": "Name of the container in your Azure Storage account.",
|
||||
"admin.image.azureContainerExample": "E.g.: \"mattermost-media\"",
|
||||
"admin.image.azureContainerExportDescription": "Name of the container in your Azure Storage account.",
|
||||
"admin.image.azureContainerExportExample": "E.g.: \"mattermost-export\"",
|
||||
"admin.image.azureContainerTitle": "Azure Container:",
|
||||
"admin.image.azureEndpointDescription": "Full Blob service URL, including scheme and storage account. Mattermost does not modify this URL, so the storage account must already be embedded in the hostname (vhost-style, e.g. \"https://acmemattermost.blob.core.chinacloudapi.cn/\") or in the path (path-style, e.g. \"http://localhost:10000/devstoreaccount1/\"). Shared-key auth signs against the host this URL points at, so make sure it actually serves the storage account named above.",
|
||||
"admin.image.azureEndpointExample": "E.g.: \"http://localhost:10000/devstoreaccount1/\"",
|
||||
@@ -1624,7 +1615,6 @@
|
||||
"admin.image.azureStorageAccountTitle": "Azure Storage Account:",
|
||||
"admin.image.enableProxy": "Enable Image Proxy:",
|
||||
"admin.image.enableProxyDescription": "When true, enables an image proxy for loading all Markdown images.",
|
||||
"admin.image.exportDirectoryDescription": "Directory to which files are written. If blank, defaults to ./data/.",
|
||||
"admin.image.extractContentDescription": "When enabled, supported document types are searchable by their content. Search results for existing documents may be incomplete <link>until a data migration is executed</link>.",
|
||||
"admin.image.extractContentTimeout.minValue": "Timeout must be 0 or greater.",
|
||||
"admin.image.extractContentTimeoutDescription": "Maximum number of seconds spent extracting the searchable content of a single uploaded document. Extractions that exceed this limit are aborted to protect server performance. Set to 0 to disable the timeout.",
|
||||
@@ -3307,7 +3297,6 @@
|
||||
"admin.sidebar.environment": "Environment",
|
||||
"admin.sidebar.experimental": "Experimental",
|
||||
"admin.sidebar.experimentalFeatures": "Features",
|
||||
"admin.sidebar.exportStorage": "Export Storage",
|
||||
"admin.sidebar.fileSharingDownloads": "File Sharing and Downloads",
|
||||
"admin.sidebar.fileStorage": "File Storage",
|
||||
"admin.sidebar.filter": "Find settings",
|
||||
|
||||
Reference in New Issue
Block a user