* MM-67387 Prevent useExternalLink from erroring on certain invalid links
* Make check for domain in useExternalLink stricter
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
* Channel sharing operations (invite, uninvite, list shared channel remotes)
now require ManageSharedChannels instead of ManageSecureConnections, allowing
customers to delegate channel sharing without granting full connection management access.
Endpoints serving both roles (getRemoteClusters, getSharedChannelRemotesByRemoteCluster) accept either permission.
Also adds RequirePermission helpers on Context to reduce boilerplate across all remote cluster and shared channel handlers, and fixes a bug where invite/uninvite checked ManageSecureConnections but reported ManageSharedChannels in the error.
Allows plugins to programmatically trigger their RHS panel to pop out
into a separate window by calling window.WebappUtils.popouts.popoutRhsPlugin().
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add single-channel guest tracking and reporting
- Add AnalyticsGetSingleChannelGuestCount store method to count guests in exactly one channel
- Exclude single-channel guests from active user seat count in GetServerLimits
- Add single-channel guest count to standard analytics response
- Add Single-channel Guests card to System Statistics page with overage warning
- Add Single-channel guests row to Edition and License page with overage styling
- Add dismissible admin-only banner when single-channel guest limit is exceeded
- Gate feature behind non-Entry SKU and guest accounts enabled checks
- Re-fetch server limits on config changes for reactive UI updates
- Fix label alignment in license details panel
Made-with: Cursor
* Refine single-channel guest tracking
- Remove license GuestAccounts feature check from shouldTrackSingleChannelGuests (only config matters)
- Re-add getServerLimits calls on page mount for fresh data
- Remove config-change reactivity code (componentDidUpdate, useEffect)
- Add server i18n translations for error strings
- Sync webapp i18n via extract
- Add inline comments for business logic
- Restore struct field comments in ServerLimits model
- Add Playwright E2E tests for single-channel guest feature
- Fix label alignment in license details panel
Made-with: Cursor
* Guests over limit fixes and PR feedback
* Fix linter issues and code quality improvements
- Use max() builtin to clamp adjusted user count instead of if-statement (modernize linter)
- Change banner type from ADVISOR to CRITICAL for proper red color styling
Made-with: Cursor
* Fix overage warnings incorrectly counting single-channel guests
Single-channel guests are free and should not trigger license seat
overage warnings. Update all overage checks to use
serverLimits.activeUserCount (seat-adjusted, excluding SCG) instead
of the raw total_users_count or TOTAL_USERS analytics stat.
- UserSeatAlertBanner on License page: use serverLimits.activeUserCount
- UserSeatAlertBanner on Site Statistics page: use serverLimits.activeUserCount
- ActivatedUserCard display and overage check: use serverLimits.activeUserCount
- OverageUsersBanner: use serverLimits.activeUserCount
Made-with: Cursor
* Use license.Users as fallback for singleChannelGuestLimit before limits load
This prevents the SingleChannelGuestsCard from showing a false overage
state before serverLimits has been fetched, while still rendering the
card immediately on page load.
Made-with: Cursor
* Fix invite modal overage banner incorrectly counting single-channel guests
Made-with: Cursor
* Fix invitation modal tests missing limits entity in mock state
Made-with: Cursor
* Fix tests
* Add E2E test for single-channel guest exceeded limit scenario
Made-with: Cursor
* Fix TypeScript errors in single channel guests E2E test
Made-with: Cursor
* Fix channel name validation error caused by unawaited async getRandomId()
Made-with: Cursor
* Add contextual tooltips to stat cards when guest accounts are enabled
Made-with: Cursor
* Code review feedback: query builder, readability, tooltips, and alignment fixes
Made-with: Cursor
* Fix license page tooltip alignment, width, and SaveLicense SCG exclusion
Made-with: Cursor
* Fix banner dismiss, license spacing, and add dismiss test
Made-with: Cursor
* Exclude DM/GM channels from single-channel guest count and fix E2E tests
Filter the AnalyticsGetSingleChannelGuestCount query to only count
memberships in public/private channels, excluding DMs and GMs. Update
store tests with DM-only, GM-only, and mixed membership cases. Fix E2E
overage test to mock the server limits API instead of skipping, and
correct banner locator to use data-testid.
Made-with: Cursor
* feat: pass unicode emojis from emoji picker to textare
* fi test
* PR feedback
* fix unit tests
* fix linter
* fix emoji test
* fix e2e test
* e2e test
* Fix MM-T155 emoji test to use flexible recently used assertions
Made-with: Cursor
---------
Co-authored-by: Nevyana Angelova <nevyangelova@Nevy-Macbook-16-2025.local>
* UpdateByQuery methods for channel_type; rewrite reindexChannelPosts
log pre-fetch error in channel Update and upgrade reindexChannelPosts to error level
* add backfill orchestration, config listener, webapp toggle changes
fix misleading backfill complete log when SaveOrUpdate fails
* add integration & unit tests for public channel search and backfill
* fix nil pointer dereference on UpdateByQuery response and log partial failures
* add tests for compliance mode override and P channel post leakage
* update system console snapshots
* add instructions to error message
* improve compliance-mode test
* getAllChannels doesn't filter by O/S, need to do ourselves
* in search, load channel info for channels we're not a member of
* blank commit -- something is wrong with github, maybe this will help
* improve channelType passing; error msg; simplify settings naming
* debug logging for timing backfill and channel change; TO BE REVERTED
* fix getMissingChannelsFromFiles in search as well
* blank commit
When a multiselect dialog field uses data_source: "dynamic", a
comma-separated default value (e.g. "Product1,Product2") was rendered
as a single chip instead of two separate pre-selected chips.
---------
Co-authored-by: Scott Bishel <scott.bishel@mattermost.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* Port scripts/update-versions to a shell script
* Update the scripts to change the version to use jq
* Add the shared package
* Update webapp/scripts/CLAUDE.OPTIONAL.md
* Update webapp/scripts/CLAUDE.OPTIONAL.md
* docs(auth): Add clarification note about Google SSO username/email sync behavior
* i18n: Add translation for SSO username sync note
* Fix linting errors in User Settings Security
Fixed react/jsx-tag-spacing and other linting issues in user_settings_security.tsx.
Also updated webapp/package.json to enforce npm engines.
* Fix: Revert whitespace changes in package.json and en.json
* Fix: Reorder en.json keys alphabetically to satisfy check-i18n
* Fix: Scope Google SSO sync note to Google service and use block element
* Rename Content Flagging to Data Spillage Handling
Update all user-facing text to use "Data Spillage Handling" and
"Quarantine for Review" terminology. Rename i18n keys that referenced
content flagging. Auto-patch bot display name on pre-existing servers.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fixed searchable stringgs
* Revert unintended package-lock.json changes
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix i18n extract check: correct typo and key ordering
Fix "posed" -> "posted" typo in keep/remove quarantine modal
defaultMessages. Move admin.contentFlagging.title to correct
alphabetical position in en.json.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix webapp tests for Data Spillage Handling rename
Update test assertions to match renamed i18n strings:
notification settings, content reviewers, and additional
settings tests now expect the new quarantine terminology.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Use translatable i18n strings for notification messages
Replace hardcoded "flagged for review" notification templates with
i18n.T() calls using "quarantined for review" terminology. Add six
new server i18n keys for author, reporter, and reviewer notifications.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix server i18n key mismatches and update test assertions
Rename remaining app.content_flagging.* keys to app.data_spillage.*
in server/i18n/en.json to match Go code references. Fix the
quarantine_post_confirmation key name. Update test assertions to
match new "quarantined for review" terminology.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix gofmt formatting in content_flagging.go
Co-authored-by: Cursor <cursoragent@cursor.com>
* Prevent nil bot on PatchBot failure in getContentReviewBot
Use a separate variable for PatchBot result so the original bot
is preserved if the display name update fails.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Reorder server i18n keys after extract
Run mmgotool i18n extract to sort entries into correct
alphabetical order.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Replace i18n.T() with fmt.Sprintf for notification messages and fix test assertions
Use direct string formatting for bot notification messages instead of
i18n translation keys, which were being removed by mmgotool i18n extract
due to indirect key references. Also update test expectations for renamed
error keys (content_flagging -> data_spillage).
Co-authored-by: Cursor <cursoragent@cursor.com>
* Update default quarantine reasons to DISC-aligned terminology
Replace generic content moderation reasons with defense/intelligence
sector terminology: Classification mismatch, Need-to-know violation,
PII exposure, OPSEC concern, CUI violation, Unauthorized disclosure,
and Other. Updated across model, API tests, webapp tests, and e2e tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Adding a string missing from bad merge
* Update remaining flagged terminology and icon for data spillage rename
- Change post menu icon from flag-outline to alert-outline
- Update reviewer notification: "quarantined" -> "submitted" a message
- Update action notifications: "flagged message" -> "quarantined message"
- Update modal errors: "flagging" -> "quarantining" this message
- Update report title: "flagged" -> "submitted" a message for review
- Update e2e page object locator for renamed menu item
Made-with: Cursor
* Fix tests
* Fix quarantine icon alignment in post dot menu
Use AlertOutlineIcon React component with size={18} instead of raw
<i> tag to match the sizing of all other menu item icons.
Made-with: Cursor
* Fixed E2E tests
* Missing test fix
* Fix E2E tests
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* Add shared_channel_manager and secure_connection_manager built-in roles
Introduce two new delegated admin roles for granular Shared Channels
permission management, allowing admins to assign shared channel and
secure connection management to specific non-admin users without
granting full System Admin or System Manager access.
- shared_channel_manager: grants manage_shared_channels permission
- secure_connection_manager: grants manage_secure_connections permission
Includes server role definitions, app migrations, permissions migrations,
System Console UI support, and API permission tests.
* MM-45293 Index all Slack attachment content fields in Elasticsearch
Previously only the "text" field from Slack attachments was indexed.
This adds title, pretext, fallback, and field titles/values, making
posts from integrations (JIRA, GitHub, CI bots) fully searchable.
* Includes deleted remote cluster infos to correctly show shared user information
* Addressing review comments
---------
Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es>
Co-authored-by: Mattermost Build <build@mattermost.com>
* MM-67659 Fix handleThreadReadChanged not working when unread_mentions or unread_replies is 0
* Update type definition for ThreadReadChanged
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
* Increase minimum version of NPM to 11
When we updated the Node.js version, I asked Saturn to make the Node/NPM
version checks looser because I didn't know about anything that would cause
the package-lock.json or package.json formats to change. It seems like NPM 10
is causing a few fields to be removed now, so we should require 11 to avoid
thrashing on that.
* And update the Node version
The "Channels" header was flashing and then disappearing when typing in
the Find Channels dialog (Ctrl/Cmd+K). This was caused by the
fetchUsersAndChannels method returning results without the groups
structure that was introduced in MM-62990.
* fix: ensuring that webapp and mobile notifications decode special characters
* fix: linter error
* Replacing anonymous function with existing utility to escape regex
* Added missing characters to webapp handling, excluded markdown renderer from being affected
- Added tests that explicitly check for script injection
Race condition caused mousemove events to fire after reset() but before
cleanup, resulting in negative width calculations that CSS clamped to
min-width.
* MM-66442 - Add empty state message for ABAC channel modal
* fix translation and ts issue
* Remove empty filter controls border in channel modal
* extract and memoize message
* adjust modal header title style and text color
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
* MM-65975 - migrate team modal to generic modal
* add e2e tests to team settings
* apply self review fixes, organize test pom and clean code
* fix cypress tests
* Remove jest-junit and unignore build folder in web app packages
We don't actually use the file output by jest-junit, and I don't think we
have since we moved off of Jenkins for CI
* Move parcel-namer-shared into build folder
* MM-67323 Add loadSharedDependency API and script for plugins to use it
* Fix client and mattermost-redux packages missing const enums
* Change interface for webAppExternals
* Rename "Self-Deleting Messages" to "Burn-on-Read Messages"
Updated the Burn on Read feature naming in the System Console for consistency with the product terminology and made all Posts subsection titles translatable for better internationalization support.
- Renamed section title from "Self-Deleting Messages" to "Burn-on-Read Messages"
- Updated section description to clarify that messages delete after being read (removed "or sent")
- Added translation keys for all 6 Posts subsection titles
- Updated type definitions and UI components to support translatable subsection titles
Co-authored-by: Cursor <cursoragent@cursor.com>
* Linting
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* Respect user display preferences for date and time formatting
User Preference Support:
- Read isUseMilitaryTime preference from user settings
- Apply 24-hour format when enabled (14:00 instead of 2:00 PM)
- Apply 12-hour format when disabled (2:00 PM instead of 14:00)
- Pass useTime prop to Timestamp component with correct hourCycle/hour12
Date Formatting Consistency:
- Create formatDateForDisplay() utility in date_utils.ts
- Centralize date formatting logic (month: 'short', day/year: 'numeric')
- Use consistent "Jan 15, 2025" format across all date/datetime fields
- Replace DateTime.fromJSDate().toLocaleString() which varies by browser
Components Updated:
- DateTimeInput: Use isMilitaryTime for dropdown and selected time display
- DateTimeInput: Use formatDateForDisplay for date display
- AppsFormDateField: Use formatDateForDisplay instead of inline Intl code
Tests Added:
- 4 tests for user preference handling (military time, locale)
- 5 tests for formatDateForDisplay utility
- Updated snapshot for Timestamp changes
Benefits:
- Single source of truth for date formatting
- Easy to change format globally by updating one function
- Respects user preferences consistently
- Fixes inconsistency where datetime showed "1/1/2026" vs date showing "Jan 1, 2026"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
* Add timezone and manual time entry support for datetime fields
Object Model:
- Create DialogDateTimeConfig with TimeInterval, LocationTimezone, AllowManualTimeEntry
- Add DateTimeConfig field to DialogElement
- Keep legacy MinDate, MaxDate, TimeInterval for fallback
Timezone Support (location_timezone):
- Display datetime in specific IANA timezone (e.g., "Europe/London", "Asia/Tokyo")
- Show timezone indicator: "🌍 Times in GMT"
- Preserve timezone through all operations
- Fix momentToString to clone before converting to UTC (prevents mutation)
- Use moment.tz array syntax for timezone-safe moment creation
- Generate time intervals starting at midnight in display timezone
Manual Time Entry (allow_manual_time_entry):
- Add parseTimeString() function supporting multiple formats:
- 12-hour: 12a, 12:30p, 3:45pm
- 24-hour: 14:30, 9:15
- Add TimeInputManual component with text input
- Conditional rendering: manual input OR dropdown
- No rounding for manual entry (exact minutes preserved)
- No auto-advance (validation only, show error for invalid format)
- Respects user's 12h/24h preference for placeholder
Critical Bug Fixes:
- Fix getTimeInIntervals to return Moment[] instead of Date[] (preserves timezone)
- Fix momentToString mutation: use .clone() before .utc()
- Use .clone() when calling .startOf('day') to preserve timezone
- Use moment.tz([...], timezone) array syntax instead of .tz().hour() mutation
- Display selected time using .format() instead of Timestamp component
- Fix null handling: optional fields start empty, show '--:--'
- Manual entry gets exact current time, dropdown gets rounded time
Component Updates:
- DateTimeInput: Add TimeInputManual component, parseTimeString, timezone handling
- AppsFormDateTimeField: Extract config, timezone indicator, pass timezone to child
- Modal components: Handle Moment | null signatures
- CSS: Add manual entry input styles with error states
Features:
- Timezone-aware time generation (dropdown starts at midnight in display TZ)
- Manual entry works with timezones (creates moments in correct TZ)
- Optional fields start empty (null value, no display default)
- Required datetime fields get rounded default from apps_form_component
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
* Fix momentToString mutation - clone before converting to UTC
Prevents .utc() from mutating the original moment object.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
* Add E2E test for 12h/24h time preference support
Test MM-T2530H verifies that datetime fields respect user's display preference:
- Sets preference to 24-hour format
- Verifies dropdown shows times as 14:00, 15:00, etc.
- Verifies selected time displays in 24-hour format
- Changes preference to 12-hour format
- Verifies dropdown shows times as 2:00 PM, 3:00 PM, etc.
Uses cy.apiSaveClockDisplayModeTo24HourPreference() to set user preference.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
* Auto-round time to interval boundaries in DateTimeInput
Automatically rounds displayed time to timePickerInterval to ensure
consistent behavior across all callers.
Problem:
- DND modal and Custom Status modal showed unrounded times (e.g., 13:47)
- Should show rounded times (e.g., 14:00) to match dropdown intervals
- Some callers pre-rounded, others didn't (inconsistent)
Solution:
- Add useEffect in DateTimeInput that auto-rounds on mount
- Only calls handleChange if time needs rounding
- Uses timePickerInterval prop or 30-minute default
- Harmless for callers that already pre-round (no change triggered)
Behavior:
- DND modal: Now shows 14:00 instead of 13:47
- Custom Status: Still works (already pre-rounded, so no-op)
- Post Reminder: Still works (already pre-rounded, so no-op)
- Interactive Dialog: Still works (uses custom intervals)
Added 3 unit tests for auto-rounding behavior.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
* lint fix
* Add deferred login cleanup to post_test.go 'not logged in' test
Ensures the test helper is logged back in after the logout test completes, preventing test state issues for subsequent tests.
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
* Add unit tests for parseTimeString and timezone handling
parseTimeString tests (9 test cases):
- 12-hour format with AM/PM (12a, 3:30pm, etc.)
- 24-hour format (14:30, 23:59, etc.)
- Time without minutes (defaults to :00)
- Invalid hours, minutes, and formats
- Edge cases (midnight 12:00am, noon 12:00pm)
Timezone handling tests (3 test cases):
- Preserve timezone in getTimeInIntervals
- Generate intervals starting at midnight in timezone
- Timezone conversion pattern verification
Total: 12 new tests added (32 total in file)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
* Add E2E tests and webhook support for timezone/manual entry
E2E Tests Added (MM-T2530O through MM-T2530S):
- MM-T2530O: Manual time entry basic functionality
- MM-T2530P: Manual time entry multiple formats (12a, 14:30, 9pm)
- MM-T2530Q: Manual time entry invalid format handling
- MM-T2530R: Timezone support dropdown (London GMT)
- MM-T2530S: Timezone support manual entry (London GMT)
Webhook Server Support:
- Added getTimezoneManualDialog() to webhook_utils.js
- Added 'timezone-manual' case to webhook_serve.js
- Dialog with 3 fields: local manual, London dropdown, London manual
Bug Fixes:
- Skip auto-rounding for allowManualTimeEntry fields (preserve exact minutes)
- Generate dropdown options even when displayTime is null (use currentTime fallback)
- Scope Cypress selectors with .within() to avoid duplicate ID issues
All tests passing (13 total datetime tests).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
* Fix ESLint no-multi-spaces in apps.ts
Remove extra spacing before comments to comply with ESLint rules.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
* Fix gofmt formatting in integration_action.go
Align Options, MultiSelect, and Refresh field spacing to match Go formatting standards.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
* more lint fixes
* css lint fix
* i18n-extract
* lint fixes
* update snapshot
* Fix modal scroll containment for datetime fields
The .modal-overflow class was applying overflow: visible to .modal-body,
which broke scroll containment when datetime fields were present. This
caused the entire form to scroll instead of scrolling within the modal-body
viewport.
Changes:
- Remove .modal-body overflow override from .modal-overflow class to
preserve scroll containment while still allowing date/time popups to
display correctly via z-index
- Remove italic styling from timezone indicator for cleaner appearance
- Remove redundant "Time" label from manual time entry input (aria-label
is sufficient for accessibility)
- Add CSS rule to ensure "(optional)" label text is not bold
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
* fixes for cypress tests
* fix for using timezone crossing dates
* fix dateonly strings parse failures
* regex fix
* linter fix
---------
Co-authored-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>