diff --git a/changelog.d/2-features/WPB-25919 b/changelog.d/2-features/WPB-25919 new file mode 100644 index 00000000000..871658adf82 --- /dev/null +++ b/changelog.d/2-features/WPB-25919 @@ -0,0 +1 @@ +Added a team feature flag for background effects. \ No newline at end of file diff --git a/docs/src/developer/reference/config-options.md b/docs/src/developer/reference/config-options.md index 4a269a7fe45..9df6dee0773 100644 --- a/docs/src/developer/reference/config-options.md +++ b/docs/src/developer/reference/config-options.md @@ -264,6 +264,20 @@ The lock status for individual teams can be changed via the internal API (`PUT / The feature status for individual teams can be changed via the public API (if the feature is unlocked). +### Background Effects + +The `backgroundEffects` feature flag controls whether background effects are available in meetings. It is enabled and unlocked by default. If you want a different configuration, use the following syntax: +```yaml +backgroundEffects: + defaults: + status: disabled|enabled + lockStatus: locked|unlocked +``` + +The lock status for individual teams can be changed via the internal API (`PUT /i/teams/:tid/features/backgroundEffects/(un)?locked`). + +The feature status for individual teams can be changed via the public API (if the feature is unlocked). + ### File Sharing File sharing is enabled and unlocked by default. If you want a different configuration, use the following syntax: diff --git a/integration/integration.cabal b/integration/integration.cabal index b388fbd0bca..fbb67af4e2a 100644 --- a/integration/integration.cabal +++ b/integration/integration.cabal @@ -139,6 +139,7 @@ library Test.FeatureFlags.AppLock Test.FeatureFlags.Apps Test.FeatureFlags.AssetAuditLog + Test.FeatureFlags.BackgroundEffects Test.FeatureFlags.Cells Test.FeatureFlags.CellsInternal Test.FeatureFlags.Channels diff --git a/integration/test/Test/FeatureFlags/BackgroundEffects.hs b/integration/test/Test/FeatureFlags/BackgroundEffects.hs new file mode 100644 index 00000000000..c2a568a0b84 --- /dev/null +++ b/integration/test/Test/FeatureFlags/BackgroundEffects.hs @@ -0,0 +1,30 @@ +-- This file is part of the Wire Server implementation. +-- +-- Copyright (C) 2026 Wire Swiss GmbH +-- +-- This program is free software: you can redistribute it and/or modify it under +-- the terms of the GNU Affero General Public License as published by the Free +-- Software Foundation, either version 3 of the License, or (at your option) any +-- later version. +-- +-- This program is distributed in the hope that it will be useful, but WITHOUT +-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +-- details. +-- +-- You should have received a copy of the GNU Affero General Public License along +-- with this program. If not, see . + +module Test.FeatureFlags.BackgroundEffects where + +import Test.FeatureFlags.Util +import Testlib.Prelude + +testPatchBackgroundEffects :: (HasCallStack) => App () +testPatchBackgroundEffects = checkPatch OwnDomain "backgroundEffects" enabled + +testBackgroundEffects :: (HasCallStack) => APIAccess -> App () +testBackgroundEffects access = + mkFeatureTests "backgroundEffects" + & addUpdate enabled + & runFeatureTests OwnDomain access diff --git a/integration/test/Test/FeatureFlags/Util.hs b/integration/test/Test/FeatureFlags/Util.hs index 295869f48e2..88b10c965ab 100644 --- a/integration/test/Test/FeatureFlags/Util.hs +++ b/integration/test/Test/FeatureFlags/Util.hs @@ -243,6 +243,7 @@ defAllFeatures = ], "meetings" .= enabled, "meetingsPremium" .= disabledLocked, + "backgroundEffects" .= enabled, "preventAdminlessGroups" .= object [ "lockStatus" .= "locked", @@ -268,6 +269,7 @@ hasExplicitLockStatus "enforceFileDownloadLocation" = True hasExplicitLockStatus "domainRegistration" = True hasExplicitLockStatus "meetings" = True hasExplicitLockStatus "meetingsPremium" = True +hasExplicitLockStatus "backgroundEffects" = True hasExplicitLockStatus _ = False checkFeature :: (HasCallStack, MakesValue user, MakesValue tid) => String -> user -> tid -> Value -> App () @@ -442,7 +444,8 @@ defAllConfiguredFeatures = ] ), "meetings" .= defaults enabled, - "meetingsPremium" .= defaults disabledLocked + "meetingsPremium" .= defaults disabledLocked, + "backgroundEffects" .= defaults enabled ] where defaults x = object ["defaults" .= x] diff --git a/integration/test/Test/Migration/TeamFeatures.hs b/integration/test/Test/Migration/TeamFeatures.hs index b25e09bb174..31adaae39b6 100644 --- a/integration/test/Test/Migration/TeamFeatures.hs +++ b/integration/test/Test/Migration/TeamFeatures.hs @@ -84,7 +84,8 @@ testTeamFeaturesMigration = do "simplifiedUserConnectionRequestQRCode", "stealthUsers", "meetings", - "meetingsPremium" + "meetingsPremium", + "backgroundEffects" ] assertModifiedFeatures :: String -> [(Value, String, [Value])] -> App () diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs index 01e0f4f1051..2c2f3f951a8 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs @@ -100,6 +100,7 @@ type IFeatureAPI = :<|> IFeatureStatusLockStatusPut StealthUsersConfig :<|> IFeatureStatusLockStatusPut MeetingsConfig :<|> IFeatureStatusLockStatusPut MeetingsPremiumConfig + :<|> IFeatureStatusLockStatusPut BackgroundEffectsConfig -- all feature configs :<|> Named "feature-configs-internal" diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Galley/Feature.hs b/libs/wire-api/src/Wire/API/Routes/Public/Galley/Feature.hs index fee909d1351..72815073ac4 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Galley/Feature.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Galley/Feature.hs @@ -81,6 +81,7 @@ type FeatureAPI = :<|> FeatureAPIGet CellsInternalConfig :<|> FeatureAPIGetPut MeetingsConfig :<|> FeatureAPIGetPut MeetingsPremiumConfig + :<|> FeatureAPIGetPut BackgroundEffectsConfig type VersionedFeatureAPIPut named reqBodyVersion cfg = Named diff --git a/libs/wire-api/src/Wire/API/Team/Feature.hs b/libs/wire-api/src/Wire/API/Team/Feature.hs index a1463bb7ae1..ac3dff03ccf 100644 --- a/libs/wire-api/src/Wire/API/Team/Feature.hs +++ b/libs/wire-api/src/Wire/API/Team/Feature.hs @@ -119,6 +119,7 @@ module Wire.API.Team.Feature StealthUsersConfig (..), MeetingsConfig (..), MeetingsPremiumConfig (..), + BackgroundEffectsConfig (..), Features, AllFeatures, NpProject (..), @@ -289,6 +290,7 @@ data FeatureSingleton cfg where FeatureSingletonCellsInternalConfig :: FeatureSingleton CellsInternalConfig FeatureSingletonMeetingsConfig :: FeatureSingleton MeetingsConfig FeatureSingletonMeetingsPremiumConfig :: FeatureSingleton MeetingsPremiumConfig + FeatureSingletonBackgroundEffectsConfig :: FeatureSingleton BackgroundEffectsConfig type family DeprecatedFeatureName (v :: Version) (cfg :: Type) :: Symbol @@ -2205,6 +2207,30 @@ instance IsFeatureConfig MeetingsPremiumConfig where instance ToObjectSchema MeetingsPremiumConfig where objectSchema = pure MeetingsPremiumConfig +-------------------------------------------------------------------------------- +-- BackgroundEffects Feature +-- +-- Controls whether background effects are available in meetings. + +data BackgroundEffectsConfig = BackgroundEffectsConfig + deriving (Eq, Show, Generic, GSOP.Generic) + deriving (Arbitrary) via (GenericUniform BackgroundEffectsConfig) + deriving (RenderableSymbol) via (RenderableTypeName BackgroundEffectsConfig) + deriving (ParseDbFeature, Default) via TrivialFeature BackgroundEffectsConfig + +instance ToSchema BackgroundEffectsConfig where + schema = object objectSchema + +instance Default (LockableFeature BackgroundEffectsConfig) where + def = defUnlockedFeature + +instance IsFeatureConfig BackgroundEffectsConfig where + type FeatureSymbol BackgroundEffectsConfig = "backgroundEffects" + featureSingleton = FeatureSingletonBackgroundEffectsConfig + +instance ToObjectSchema BackgroundEffectsConfig where + objectSchema = pure BackgroundEffectsConfig + --------------------------------------------------------------------------------- -- FeatureStatus @@ -2310,7 +2336,8 @@ type Features = StealthUsersConfig, CellsInternalConfig, MeetingsConfig, - MeetingsPremiumConfig + MeetingsPremiumConfig, + BackgroundEffectsConfig ] -- | list of available features as a record diff --git a/libs/wire-api/src/Wire/API/Team/FeatureFlags.hs b/libs/wire-api/src/Wire/API/Team/FeatureFlags.hs index 275371eafa6..07e9ea6def4 100644 --- a/libs/wire-api/src/Wire/API/Team/FeatureFlags.hs +++ b/libs/wire-api/src/Wire/API/Team/FeatureFlags.hs @@ -428,6 +428,13 @@ newtype instance FeatureDefaults MeetingsPremiumConfig deriving (FromJSON, ToJSON) via Defaults (LockableFeature MeetingsPremiumConfig) deriving (ParseFeatureDefaults) via OptionalField MeetingsPremiumConfig +newtype instance FeatureDefaults BackgroundEffectsConfig + = BackgroundEffectsDefaults (LockableFeature BackgroundEffectsConfig) + deriving stock (Eq, Show) + deriving newtype (Default, GetFeatureDefaults) + deriving (FromJSON, ToJSON) via Defaults (LockableFeature BackgroundEffectsConfig) + deriving (ParseFeatureDefaults) via OptionalField BackgroundEffectsConfig + featureKey :: forall cfg. (IsFeatureConfig cfg) => Key.Key featureKey = Key.fromText $ featureName @cfg diff --git a/libs/wire-subsystems/src/Wire/FeaturesConfigSubsystem/Types.hs b/libs/wire-subsystems/src/Wire/FeaturesConfigSubsystem/Types.hs index 4747dc7faea..023035e8c11 100644 --- a/libs/wire-subsystems/src/Wire/FeaturesConfigSubsystem/Types.hs +++ b/libs/wire-subsystems/src/Wire/FeaturesConfigSubsystem/Types.hs @@ -150,4 +150,6 @@ instance GetFeatureConfig MeetingsConfig instance GetFeatureConfig MeetingsPremiumConfig +instance GetFeatureConfig BackgroundEffectsConfig + instance GetFeatureConfig PreventAdminlessGroupsConfig diff --git a/services/galley/src/Galley/API/Internal.hs b/services/galley/src/Galley/API/Internal.hs index 32cc9ac9cb2..c64f65bba64 100644 --- a/services/galley/src/Galley/API/Internal.hs +++ b/services/galley/src/Galley/API/Internal.hs @@ -300,6 +300,7 @@ allFeaturesAPI = <@> featureAPI1Full <@> featureAPI1Full <@> featureAPI1Full + <@> featureAPI1Full featureAPI :: API IFeatureAPI GalleyEffects featureAPI = @@ -326,6 +327,7 @@ featureAPI = <@> mkNamedAPI @'("ilock", StealthUsersConfig) (updateLockStatus @StealthUsersConfig) <@> mkNamedAPI @'("ilock", MeetingsConfig) (updateLockStatus @MeetingsConfig) <@> mkNamedAPI @'("ilock", MeetingsPremiumConfig) (updateLockStatus @MeetingsPremiumConfig) + <@> mkNamedAPI @'("ilock", BackgroundEffectsConfig) (updateLockStatus @BackgroundEffectsConfig) -- all features <@> mkNamedAPI @"feature-configs-internal" (maybe getAllTeamFeaturesForServer getAllTeamFeaturesForUser) <@> mkNamedAPI @"get-configured-feature-flags" getConfiguredFeatureFlags diff --git a/services/galley/src/Galley/API/Public/Feature.hs b/services/galley/src/Galley/API/Public/Feature.hs index 6d95420f5d8..b246e37c254 100644 --- a/services/galley/src/Galley/API/Public/Feature.hs +++ b/services/galley/src/Galley/API/Public/Feature.hs @@ -82,6 +82,7 @@ featureAPI = <@> mkNamedAPI @'("get", CellsInternalConfig) getFeature <@> featureAPIGetPut @MeetingsConfig <@> featureAPIGetPut @MeetingsPremiumConfig + <@> featureAPIGetPut @BackgroundEffectsConfig deprecatedFeatureConfigAPI :: API DeprecatedFeatureAPI GalleyEffects deprecatedFeatureConfigAPI = diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index f04363a6966..c87f0651ae1 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -515,3 +515,5 @@ instance SetFeatureConfig StealthUsersConfig instance SetFeatureConfig MeetingsConfig instance SetFeatureConfig MeetingsPremiumConfig + +instance SetFeatureConfig BackgroundEffectsConfig diff --git a/tools/stern/src/Stern/API.hs b/tools/stern/src/Stern/API.hs index d53bbbfcd72..45066bbc5ff 100644 --- a/tools/stern/src/Stern/API.hs +++ b/tools/stern/src/Stern/API.hs @@ -197,6 +197,8 @@ sitemap' = :<|> Named @"put-route-meetings-config" (mkFeatureStatusPutRoute @MeetingsConfig) :<|> Named @"get-route-meetings-premium-config" (mkFeatureGetRoute @MeetingsPremiumConfig) :<|> Named @"put-route-meetings-premium-config" (mkFeatureStatusPutRoute @MeetingsPremiumConfig) + :<|> Named @"get-route-background-effects-config" (mkFeatureGetRoute @BackgroundEffectsConfig) + :<|> Named @"put-route-background-effects-config" (mkFeatureStatusPutRoute @BackgroundEffectsConfig) :<|> Named @"get-team-invoice" getTeamInvoice :<|> Named @"get-team-billing-info" getTeamBillingInfo :<|> Named @"put-team-billing-info" updateTeamBillingInfo @@ -236,6 +238,7 @@ sitemap' = :<|> Named @"lock-unlock-route-apps-config" (mkFeatureLockUnlockRoute @AppsConfig) :<|> Named @"lock-unlock-route-meetings-config" (mkFeatureLockUnlockRoute @MeetingsConfig) :<|> Named @"lock-unlock-route-meetings-premium-config" (mkFeatureLockUnlockRoute @MeetingsPremiumConfig) + :<|> Named @"lock-unlock-route-background-effects-config" (mkFeatureLockUnlockRoute @BackgroundEffectsConfig) sitemapInternal :: Servant.Server SternAPIInternal sitemapInternal = diff --git a/tools/stern/src/Stern/API/Routes.hs b/tools/stern/src/Stern/API/Routes.hs index 668f6755360..a216a2bb671 100644 --- a/tools/stern/src/Stern/API/Routes.hs +++ b/tools/stern/src/Stern/API/Routes.hs @@ -332,6 +332,8 @@ type SternAPI = :<|> Named "put-route-meetings-config" (MkFeatureStatusPutRoute MeetingsConfig) :<|> Named "get-route-meetings-premium-config" (MkFeatureGetRoute MeetingsPremiumConfig) :<|> Named "put-route-meetings-premium-config" (MkFeatureStatusPutRoute MeetingsPremiumConfig) + :<|> Named "get-route-background-effects-config" (MkFeatureGetRoute BackgroundEffectsConfig) + :<|> Named "put-route-background-effects-config" (MkFeatureStatusPutRoute BackgroundEffectsConfig) :<|> Named "get-team-invoice" ( Summary "Get a specific invoice by Number" @@ -488,6 +490,7 @@ type SternAPI = :<|> Named "lock-unlock-route-apps-config" (MkFeatureLockUnlockRoute AppsConfig) :<|> Named "lock-unlock-route-meetings-config" (MkFeatureLockUnlockRoute MeetingsConfig) :<|> Named "lock-unlock-route-meetings-premium-config" (MkFeatureLockUnlockRoute MeetingsPremiumConfig) + :<|> Named "lock-unlock-route-background-effects-config" (MkFeatureLockUnlockRoute BackgroundEffectsConfig) ------------------------------------------------------------------------------- -- Swagger diff --git a/tools/stern/test/integration/API.hs b/tools/stern/test/integration/API.hs index b1c6d1fc0d8..62af047dc32 100644 --- a/tools/stern/test/integration/API.hs +++ b/tools/stern/test/integration/API.hs @@ -129,7 +129,9 @@ tests s = test s "PUT /teams/:tid/features/meetings{,'?lockOrUnlock'}" $ testLockStatus @MeetingsConfig, test s "/teams/:tid/features/meetings" $ testFeatureStatus @MeetingsConfig, test s "PUT /teams/:tid/features/meetingsPremium{,'?lockOrUnlock'}" $ testLockStatus @MeetingsPremiumConfig, - test s "/teams/:tid/features/meetingsPremium" $ testFeatureStatus @MeetingsPremiumConfig + test s "/teams/:tid/features/meetingsPremium" $ testFeatureStatus @MeetingsPremiumConfig, + test s "PUT /teams/:tid/features/backgroundEffects{,'?lockOrUnlock'}" $ testLockStatus @BackgroundEffectsConfig, + test s "/teams/:tid/features/backgroundEffects" $ testFeatureStatus @BackgroundEffectsConfig -- The following endpoints can not be tested here because they require ibis: -- - `GET /teams/:tid/billing` -- - `GET /teams/:tid/invoice/:inr`