+
+ }
+ text={formatMessage({id: 'admin.classification_markings.notice.body', defaultMessage: 'Markings are not tied to access control decisions at this time and are for display purposes only.'})}
+ />
+
);
diff --git a/webapp/channels/src/i18n/en.json b/webapp/channels/src/i18n/en.json
index 65f3402ac17..a8450a2e1f4 100644
--- a/webapp/channels/src/i18n/en.json
+++ b/webapp/channels/src/i18n/en.json
@@ -1870,11 +1870,30 @@
"admin.mobileSecurity.allowPdfLinkNavigationTitle": "Allow Link Navigation in Secure PDFs:",
"admin.mobileSecurity.biometricsDescription": "Enforces biometric authentication (with PIN/passcode fallback) before accessing the app. Users will be prompted based on session activity and server switching rules.",
"admin.mobileSecurity.biometricsTitle": "Enable Biometric Authentication:",
+ "admin.mobileSecurity.ephemeralMode_feature_discovery.description": "With Mattermost Enterprise Advanced, you can enable Mobile Ephemeral Mode to enforce data persistence policies on mobile devices. Configure disconnection timeouts, offline data retention, and automatic cache cleanup.",
+ "admin.mobileSecurity.ephemeralMode_feature_discovery.title": "Control mobile data persistence with Mobile Ephemeral Mode",
+ "admin.mobileSecurity.ephemeralMode.autoCacheCleanup.placeholder": "E.g.: 7",
+ "admin.mobileSecurity.ephemeralMode.autoCacheCleanup.range": "Must be a number between 0 and 60 days.",
+ "admin.mobileSecurity.ephemeralMode.autoCacheCleanupDescription": "Controls the maximum age of any content cached on the device, regardless of connection status. Prevents unbounded accumulation of sensitive data. Set to 0 for zero-persistence mode where content is never persisted to disk.",
+ "admin.mobileSecurity.ephemeralMode.autoCacheCleanupTitle": "Auto Cache Cleanup (days):",
+ "admin.mobileSecurity.ephemeralMode.banner": "Changes to these settings are delivered to connected devices in real time. Offline devices will continue operating under their last-known settings until they re-establish a server connection. Timer state persists across app and device restarts.",
+ "admin.mobileSecurity.ephemeralMode.disconnectionTimeout.placeholder": "E.g.: 60",
+ "admin.mobileSecurity.ephemeralMode.disconnectionTimeout.range": "Must be a number between 0 and 600 seconds (10 minutes).",
+ "admin.mobileSecurity.ephemeralMode.disconnectionTimeoutDescription": "Grace period after losing server connection before the device is considered offline. Helps avoid false triggers from brief network interruptions. Values below 5 are not recommended.",
+ "admin.mobileSecurity.ephemeralMode.disconnectionTimeoutTitle": "Disconnection Timeout (seconds):",
+ "admin.mobileSecurity.ephemeralMode.enableDescription": "When enabled, mobile clients will follow the server-configured ephemeral data policies. Disconnected devices will clean up cached data based on the configured timers.",
+ "admin.mobileSecurity.ephemeralMode.enableTitle": "Enable Mobile Ephemeral Mode:",
+ "admin.mobileSecurity.ephemeralMode.offlinePersistence.disabled": "How long cached content is kept after the device goes offline. When the timer expires, cached content is deleted but session credentials are preserved. Set to 0 for immediate cleanup. Requires Mobile Ephemeral Mode to be enabled and Auto Cache Cleanup to be greater than 0.",
+ "admin.mobileSecurity.ephemeralMode.offlinePersistence.placeholder": "E.g.: 24",
+ "admin.mobileSecurity.ephemeralMode.offlinePersistence.range": "Must be a number between 0 and 72 hours (3 days).",
+ "admin.mobileSecurity.ephemeralMode.offlinePersistenceDescription": "How long cached content is kept after the device goes offline. When the timer expires, cached content is deleted but session credentials are preserved. Set to 0 for immediate cleanup.",
+ "admin.mobileSecurity.ephemeralMode.offlinePersistenceTitle": "Offline Persistence Timer (hours):",
"admin.mobileSecurity.jailbreakDescription": "Prevents access to the app on devices detected as jailbroken or rooted. If a device fails the security check, users will be denied access or prompted to switch to a compliant server.",
"admin.mobileSecurity.jailbreakTitle": "Enable Jailbreak/Root Protection:",
"admin.mobileSecurity.mobileAllowDownloads": "Site Configuration > File Sharing and Downloads > Allow File Downloads on Mobile",
"admin.mobileSecurity.screenCaptureDescription": "Blocks screenshots and screen recordings when using the mobile app. Screenshots will appear blank, and screen recordings will blur (iOS) or show a black screen (Android). Also applies when switching apps.",
"admin.mobileSecurity.screenCaptureTitle": "Prevent Screen Capture:",
+ "admin.mobileSecurity.sections.ephemeralMode.description": "Configure data persistence and cache management policies for mobile devices.",
"admin.mobileSecurity.sections.general.description": "Configure device security features for the mobile app.",
"admin.mobileSecurity.sections.intune.description": "Configure Microsoft Intune Mobile Application Management (MAM) for App Protection Policies.",
"admin.mobileSecurity.secureFilePreviewDescription": "Prevents file downloads, previews, and sharing for most file types, even if {mobileAllowDownloads} is enabled. Allows in-app previews for PDFs, videos, and images only. Files are stored temporarily in the app's cache and cannot be exported or shared.",
@@ -4052,6 +4071,11 @@
"channel_menu.bookmarks.addLink": "Add a link",
"channel_modal.alreadyExist": "A channel with that URL already exists",
"channel_modal.cancel": "Cancel",
+ "channel_modal.classification.banner_label": "Banner text",
+ "channel_modal.classification.banner_placeholder": "Banner text",
+ "channel_modal.classification.level_label": "Classification level",
+ "channel_modal.classification.toggle_description": "When enabled, classification markings will appear for this channel. Individual channels cannot have a classification level lower than the global classification level.",
+ "channel_modal.classification.toggle_label": "Channel classification",
"channel_modal.create_board.tooltip_description": "Use any of our templates to manage your tasks or start from scratch with your own!",
"channel_modal.create_board.tooltip_title": "Manage your task with a board",
"channel_modal.createNew": "Create channel",
@@ -4140,6 +4164,9 @@
"channel_settings.archive.button": "Archive this channel",
"channel_settings.archive.warning": "Archiving a channel removes it from the user interface, but doesn't permanently delete the channel. New messages can't be posted to archived channels.",
"channel_settings.channel_info_tab.name": "Channel Info",
+ "channel_settings.classification.description": "When enabled, a classification level can be set for the channel with configurable indicators.",
+ "channel_settings.classification.level_label": "Classification level",
+ "channel_settings.classification.title": "Classification",
"channel_settings.error_banner_color_required": "Banner color is required",
"channel_settings.error_banner_text_required": "Banner text is required",
"channel_settings.error_display_name_required": "Channel name is required",
diff --git a/webapp/channels/src/sass/components/_color-input.scss b/webapp/channels/src/sass/components/_color-input.scss
index 1d65c4015cb..b403e60625e 100644
--- a/webapp/channels/src/sass/components/_color-input.scss
+++ b/webapp/channels/src/sass/components/_color-input.scss
@@ -1,4 +1,5 @@
@use "utils/functions";
+@use "utils/variables";
.color-input {
position: relative;
@@ -30,11 +31,20 @@
border-radius: 0 4px 4px 0;
background: functions.v(center-channel-bg) !important;
line-height: 0;
+
+ &--disabled {
+ background: rgba(var(--center-channel-color-rgb), 0.1) !important;
+ cursor: not-allowed;
+
+ .color-icon {
+ cursor: not-allowed;
+ }
+ }
}
.color-popover {
position: absolute;
- z-index: 12;
+ z-index: variables.$z-index-popover;
top: 100%;
right: 0;
padding-top: 8px;
diff --git a/webapp/channels/src/utils/admin_console_index.test.tsx b/webapp/channels/src/utils/admin_console_index.test.tsx
index 2da36d664e8..3c83b111f77 100644
--- a/webapp/channels/src/utils/admin_console_index.test.tsx
+++ b/webapp/channels/src/utils/admin_console_index.test.tsx
@@ -29,8 +29,8 @@ describe('AdminConsoleIndex.generateIndex', () => {
expect(idx.search('saml')).toEqual([
'authentication/saml',
'environment/session_lengths',
- 'authentication/email',
'environment/mobile_security',
+ 'authentication/email',
'experimental/features',
]);
expect(idx.search('nginx')).toEqual([
diff --git a/webapp/platform/client/src/client4.ts b/webapp/platform/client/src/client4.ts
index 1c7d16a424a..d464978ddaa 100644
--- a/webapp/platform/client/src/client4.ts
+++ b/webapp/platform/client/src/client4.ts
@@ -2210,6 +2210,13 @@ export default class Client4 {
);
};
+ patchPropertyValues = (groupName: string, objectType: string, targetId: string, items: Array<{field_id: string; value: T}>) => {
+ return this.doFetch>>(
+ `${this.getBaseRoute()}/properties/groups/${groupName}/${objectType}/values/${targetId}`,
+ {method: 'PATCH', body: JSON.stringify(items)},
+ );
+ };
+
// Remote Clusters Routes
getRemoteClusters = (options: {
diff --git a/webapp/platform/types/src/config.ts b/webapp/platform/types/src/config.ts
index b77216eb20a..c5e6fc8899e 100644
--- a/webapp/platform/types/src/config.ts
+++ b/webapp/platform/types/src/config.ts
@@ -837,6 +837,13 @@ export type IntuneSettings = {
AuthService?: string;
};
+export type MobileEphemeralModeSettings = {
+ Enable: boolean;
+ DisconnectionTimeoutSeconds: number;
+ OfflinePersistenceTimerHours: number;
+ AutoCacheCleanupDays: number;
+};
+
export type ClusterSettings = {
Enable: boolean;
ClusterName: string;
@@ -1105,6 +1112,7 @@ export type AdminConfig = {
AccessControlSettings: AccessControlSettings;
ContentFlaggingSettings: ContentFlaggingSettings;
AutoTranslationSettings: AutoTranslationSettings;
+ MobileEphemeralModeSettings: MobileEphemeralModeSettings;
};
export type ReplicaLagSetting = {