* MM-64977: Fix channel switcher row overlap with long names
In the Find Channels modal, very long channel names overflowed
their row and visually overlapped the team name shown on the right
because the team label was absolutely positioned and the channel
column did not reserve horizontal space.
Restructure the SwitchChannelSuggestion row to use a real flex
layout: a primary column wrapper holds the channel name and inline
metadata with `flex: 1 1 auto; min-width: 0;` so the name truncates
with an ellipsis, and the team-name span becomes a flex sibling
with `flex: 0 0 auto; max-width: 40%;` so it remains visible. The
channel name is wrapped in WithTooltip whose disabled prop is
driven by a useLayoutEffect-based scrollWidth > clientWidth check,
so the full name is shown on hover only when truncation occurs.
Made-with: Cursor
* MM-64977: Show tooltip on truncated team name as well
Mirror the channel-name tooltip behavior on the team-name span in
the channel switcher row: track its truncation state via the same
useLayoutEffect + ref pattern, and wrap the team name in WithTooltip
whose disabled prop is driven by scrollWidth > clientWidth. Hovering
the team label now reveals the full team display name when (and only
when) it is actually truncated.
Extend existing tooltip tests to assert the team-name tooltip
disabled flag mirrors the truncation state in both branches; loosen
the layout test to permit the WithTooltip wrapper around the team
span while still asserting the team name does not live inside the
primary column.
Made-with: Cursor
Every test binary that uses TestPool builds 16 stores in parallel, each
running the full migration set. Without DisableMorphLogging() the morph
debug stream from each store flows through to the test logger (which is
configured at LvlTrace), producing tens of thousands of "migrating (up)"
lines per shard — amplified further on shards that re-run flaky tests,
since every re-run spawns a fresh TestMain and a fresh pool.
Migration failures are still surfaced: engine.ApplyAll returns the
error, sqlstore.New wraps it as "failed to apply database migrations",
and both NewTestPool callers panic on a non-nil result.
Co-authored-by: Mattermost Build <build@mattermost.com>
The Invite Guest modal's 'Add to channels' section was rendering with
hard-coded or dimmed text colors that did not respect the active theme.
This was hardest to read on dark themes where the typed-input text and
the parenthetical channel name in the suggestion dropdown nearly blended
into the background.
Changes:
- Set the shared invite section title to var(--center-channel-color) so
it follows the active theme.
- Override the react-select default emotion color on
.channels-input__input-container so typed text in the channel picker
uses the themed color (the previous attempt only fixed the outer
control and the inner input, missing the input container that
react-select uses to set the inherited color).
- Use the themed color (with 0.75 alpha) for the .channel-name option
text and remove the 0.5 opacity layers so dark themes stay readable.
Fixes: https://mattermost.atlassian.net/browse/MM-68238
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
* Avoid setting an empty value on slash command IconURL
When `PostEnablePostIconOverride` is enabled and no icon URL is
provided, the override icon URL was being set to empty and triggering
a warning. This change updates the behavior not to set the icon at
all, avoiding the triggering of the warn message while keeping the
behavior.
* Adds an additional check to the test
---------
Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es>
* MM-68499 - auto run sync jobs on team admin abac policy creation
* Use child-policy flow for access-control sync ownership test
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
* fix: detect ADFS when IdpDescriptorURL has no trailing slash
The ADFS detection in detectSAMLProviderType was checking for "/adfs/"
(with trailing slash) but standard ADFS IdpDescriptorURL values often
end with just "/adfs" (e.g. https://adfs.company.com/adfs), causing the
provider type to show as "unknown" in support packets.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: lowercase FederationMetadata pattern for case-insensitive matching
The normalizedURL is already lowercased, so comparing against the mixed-case
literal "/FederationMetadata/" made that branch unreachable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* MM-67913: fix white flash on product navigation by centralizing app__body ownership
Previously, `document.body.classList.add/remove('app__body')` was managed
independently by `ChannelController` and each product plugin (Playbooks,
Boards). When switching products, the plugin's cleanup removed `app__body`
before the incoming tree's effect re-applied it. Because `ChannelController`
is deeply nested and sometimes async, a ~170ms gap could elapse during which
the body fell back to `background: $bg--gray`, producing a visible white
flash behind the transparent `GlobalHeader` and LHS.
Centralize `app__body` ownership in `WithUserTheme` via a new
`useAppBodyClass` hook. Since `WithUserTheme` wraps both the products
Switch and the `/:team` route in `root.tsx`, it stays mounted across
product navigation, so the class is never removed mid-transition.
`ChannelController` no longer toggles `app__body`; tests updated.
Product plugins (Playbooks, Boards) should stop touching `app__body` as
well; companion fixes land in their respective repos.
Made-with: Cursor
* MM-67913: add coverage for app body class ownership
Made-with: Cursor
* Update webapp/channels/src/components/theme_provider/theme_context.ts
Co-authored-by: Harrison Healey <harrisonmhealey@gmail.com>
* MM-67913: fix theme context hook closure
Made-with: Cursor
---------
Co-authored-by: Harrison Healey <harrisonmhealey@gmail.com>
The new TestRegisterPluginForSharedChannels tests added in #36126 broke
master CI because RegisterPluginForSharedChannels assigned opts.Displayname
directly to RemoteCluster.Name, which IsValid validates against the slug
regex ^[a-zA-Z0-9.\-_]+$. Display names with spaces (e.g. "legacy plugin")
fail validation. The tests didn't run in the PR's final CI shard and the
issue surfaced post-merge.
Add CleanRemoteName to the public model, mirroring CleanTeamName and
CleanUsername: lowercase, replace spaces and other disallowed characters
with hyphens, trim, truncate to RemoteNameMaxLength, fall back to NewId
when the result is empty. Use it in RegisterPluginForSharedChannels so
Name is always slug-valid while DisplayName keeps the human-readable label.
This also lets real plugins register with display names containing spaces.
* MM-67979 MM-67980: Add SMTP and push proxy connectivity to support packet
Adds a `notifications` section to `diagnostics.yaml` in the support
packet with SMTP email and push proxy connectivity probe results.
- `notifications.email.status`: ok/fail/disabled based on whether
SendEmailNotifications is enabled and an SMTP connection can be
established using mail.TestConnection()
- `notifications.push.status`: ok/fail/disabled based on whether
SendPushNotifications is enabled and an HTTP GET to the configured
PushNotificationServer URL succeeds
- Error messages are included in the `error` field on failure
- No email or push notification is sent during the probe
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* fix: handle errcheck lint violations in support_packet_test.go
Suppress unhandled error return values from rw.WriteString calls in
the mock SMTP server used in tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: use 127.0.0.1 directly in SMTP reachability test
Replace localhost:0 with 127.0.0.1:0 for the mock SMTP listener so
that it always binds to the loopback interface. In CI Docker containers
localhost may resolve to the container IP rather than 127.0.0.1, causing
the SMTP dial to fail with connection refused. Also switch from string
manipulation to net.TCPAddr type assertion for reliable host/port
extraction.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: override MM_EMAILSETTINGS_SMTPSERVER env var in SMTP reachability test
The CI environment sets MM_EMAILSETTINGS_SMTPSERVER=inbucket via
test.env. Mattermost's config Store.Set() calls GetEnvironment()
(os.Environ()) on every UpdateConfig, so env vars silently override
any programmatic config change. Use t.Setenv before UpdateConfig so
the env var points to 127.0.0.1 for the duration of the subtest.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add model.StatusDisabled constant and use it in support_packet.go
Replace "disabled" string literals with model.StatusDisabled for
consistency with model.StatusOk and model.StatusFail.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: use utils.GetHostnameFromSiteURL, extract testPushProxyConnection helper, set LDAP StatusDisabled
- Replace manual url.Parse with utils.GetHostnameFromSiteURL (consistent with app/config.go)
- Extract push proxy HTTP check into testPushProxyConnection with TODO to move to its own package
- Set d.LDAP.Status = model.StatusDisabled when LDAP is not configured
- Replace "disabled" string literals in tests with model.StatusDisabled
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add status field to ElasticSearch diagnostics with ok/fail/disabled
When indexing is enabled, reports ok or fail based on TestConfig result.
When indexing is disabled or the engine is unavailable, reports disabled.
Backend/ServerVersion/ServerPlugins are still collected when the engine
exists regardless of indexing status.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: update Happy path test for LDAP and ES StatusDisabled assertions
Both are disabled in the test environment so they now report StatusDisabled.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: use GET /version endpoint for push proxy connectivity check
Use url.JoinPath to construct the /version path safely, replacing
raw root URL access. Also validate the HTTP status code so non-2xx/3xx
responses are treated as failures.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* Add XML struct tags and multi-remote registration for shared channels plugin API
Phase 1: Add xml struct tags to model types used in SyncMsg (Post, User,
Reaction, Status, PostAcknowledgement, FileInfo, SyncResponse,
MembershipChangeMsg). Add custom MarshalXML/UnmarshalXML for SyncMsg
(Users map, MentionTransforms map), StringMap, and StringInterface.
Exclude Post.Metadata, PrevStatus, and server-internal FileInfo fields
from XML. JSON serialization is unaffected.
Phase 2: Lift the one-remote-per-plugin constraint so plugins can
register multiple remotes with different SiteURLs. Add SiteURL field to
RegisterPluginOpts (defaults to "plugin_<PluginID>" for backward
compatibility). Add GetAllByPluginID and GetBySiteURL store methods.
Rewrite registration to dedup by SiteURL instead of PluginID. Add
UnregisterPluginRemoteForSharedChannels for single-remote removal with
plugin ownership validation. Validate SiteURL is non-empty in
RemoteCluster.IsValid. Simplify IsPlugin() to check PluginID only.
* ci: compile mmctl e2e tests with requirefips when FIPS_ENABLED=true
Without this, the mmctl test binary was compiled without the requirefips
tag even in the FIPS container, leaving model.FIPSEnabled=false and
PasswordSettings.MinimumLength=8. Short passwords like "somepass" passed
validation and hashing silently succeeded, giving false confidence that
the tests were FIPS-clean.
* tests: fix short password in TestUserConvertCmdF for FIPS
"Valid bot to user convert" reached ConvertBotToUser with "password"
(8 chars), which fails MinimumLength=14 on FIPS builds.
* MM-67319 Move ShortcutKey component into Shared Package
* MM-67322 Add i18n-extract support for shared package and move key constants
* MM-67320 Move WithTooltip into shared package without modification
* Add CSS variables for standard z-indices
* Update TooltipShortcut to point to shared ShortcutKey
* Update TooltipContent to use shared Emoji
* Move isMessageDescriptor into shared package
* Add Floating UI as explicit dependency of shared package
* Fix WithTooltip imports
* Fix imports for ShortcutX types
* Move/copy tooltip constants into shared package
* Fix WithTooltip tests
* Remove unneeded TODO comments
* Actually share new modules with plugins
* Stop publishing src folder for shared package
* omit error_* fields if empty, add status code
* MM-68378: Add tests for 404-delete semantics in ES/OS indexing jobs
* MM-68378: Fix empty error fields and spurious failures for OS/ES bulk deletes
- Log resp.Status unconditionally in OnFailure so status-only failures
(resp.Error nil, err nil) are always identifiable
- Downgrade per-item OnFailure log from Error to Warn; the job-level
Error log already captures the aggregate failure
- Track real failures in a separate atomic counter shared between the
OnFailure callback and the close closure; 404 deletes (document not
found) are silently skipped and not counted
- Report num_failed from the real counter in close stats; retain
stats_num_failed as the raw SDK count for reference
* MM-68353 - show placeholder for redacted files in preview when permission policies are enabled
* add tests for rendering redacted files placeholder in post message preview based on permission policies
* [MM-68231] Tighten post info authorization
Align post info channel access with the standard read path while preserving expected public-channel discoverability. Add regression coverage for guest and compliance-mode access checks.
Made-with: Cursor
* [MM-68231] Strengthen post info test coverage
Tighten the new post info regression coverage so the guest denial case proves its setup and the compliance case asserts the expected non-compliance behavior first.
Made-with: Cursor
* [MM-68231] Expand post info authorization coverage
Add focused regression coverage for invite-team access, compliance behavior on open teams, private-channel permission boundaries, and outsider denial for DM and GM post info.
Made-with: Cursor
* Adds version to the property group model
* Ensures that the REST API rejects v1 group calls
* Ensures field version and group version match
* Simplify property groups on app layer tests
* Add GetByID to PropertyGroupStore and enforce field/group version match on update
* Simplify bits of the code
* Fix i18n and add generic errors
* Fix PropertyGroupStore mock to return stable IDs and default zero version to V1
* Fix tests that were using nonexistent group IDs
* Fix rigidness on valid group names
* Update group not found slug
* Temporary allow to use tempaltes with v1
* Explicitly including tempaltes in the IsPSAv1 check for conflict check
* Return 404 on group not found and template explicit inclusion on patch API endpoint
* Fix CPA test that would use fields from unregistered groups
---------
Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es>
sqlstore's TestMain calls sqlstore.InitTest (which opens postgres and
drops tables) before mainHelper.Main, so the -test.list bailout added
in #36222 never fired and shard-split discovery failed on the GitHub
host. Bail out at the top of TestMain instead, and restore HEAVY_MS
so sqlstore can still be treated as whole.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* Fix invite modal input text clipping and modal width overflow
Root cause: react-select v5 auto-sizes the input container using a CSS
grid with data-value attribute, which grows the grid columns based on
input text width. This caused the control, modal-content, and the
dropdown menu to exceed the modal-dialog's 600px width.
Changes:
1. invitation_modal.scss: Add max-width:100% and overflow:hidden on
.modal-content to prevent it from overflowing the 600px modal-dialog.
2. users_emails_input.tsx: Override react-select's styles:
- input.gridTemplateColumns: '0 minmax(0, 1fr)' prevents the sizer
column from auto-expanding based on typed text width.
- valueContainer.gridTemplateColumns: 'minmax(0, 1fr)' prevents the
value-container grid from auto-sizing columns beyond the container.
- Remove old display:flex and width:100% overrides that fought with
react-select v5's inline-grid layout.
3. users_emails_input.scss:
- Remove legacy width:1px on react-select input wrapper.
- Add min-width:0 and max-width:100% on value-container and
input-container for proper flex/grid containment.
- Constrain dropdown menu to max-width:100%.
- Allow no-match text and menu notices to wrap with overflow-wrap
and word-break. Use min-height instead of fixed height so wrapped
text fits.
Fixes: MM-68461
Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>
* Fix stylelint property order in invitation_modal.scss
Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>
* Update snapshot for react-select style changes
Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>
* Add min-width: 0 to input-container to prevent shrink/overflow regressions
Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* Introduce model.SanitizeFilename and model.IsValidFilename, and
apply them in genFileInfoFromReader and FileInfo.IsValid. The
sanitizer uses filepath.Base, NFC-normalizes Unicode, strips ASCII
control characters, collapses backslashes to forward slashes, and
truncates to the VARCHAR(256) fileinfo.name column width.
The react-select input uses classNamePrefix ManagedCategory, so it did not
inherit the global react-select__input theme color. Set color to
var(--center-channel-color) on the input and input-container to match
placeholder and single-value styling.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Devin Binnie <devinbinnie@users.noreply.github.com>
* MM-66082: Fix invite modal paste by reading text/plain from clipboard
UsersEmailsInput always called preventDefault on paste but read clipboard
data with the legacy 'Text' type, which is empty in modern browsers.
That blocked default paste while adding nothing. Read text/plain first,
fall back to Text, skip custom handling when there is no meaningful
content, and only preventDefault when handling pasted text.
Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>
* Fix ESLint no-void in UsersEmailsInput paste handler
Replace void promise with .catch(() => undefined) so async paste
processing satisfies the no-void rule.
Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>
* MM-66082: Keep arbitrary pasted invite text as draft
Only treat obvious list pastes (comma, semicolon, newline) as bulk
invite input. Let arbitrary pasted text remain in the input so the
existing search and no-match UX can handle it, and keep space-delimited
text as draft rather than splitting it into invite tokens.
Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>
* MM-66082: Restore space-delimited email paste handling
Treat space-separated paste as bulk invite input only when every token
is a valid email. Keep mixed or free-form space-separated paste as draft
text so the existing no-match search UX still applies.
Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>
* MM-66082: Fix invite paste parsing in modal input
Treat pasted input as bulk invite tokens only when it is a single valid
email, an obvious comma/semicolon/newline list, or a space-separated list
of valid emails. Leave mixed or arbitrary pasted text as draft so the
existing no-match search UX still applies. Add focused widget and invite
view regression coverage for the affected paste paths.
Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>
* tests: use example invite paste fixtures
Replace product-specific test data with example.com values, restore the
space-paste length assertion, rename the invalid-word fixture, and remove
extra clipboard MIME lookups that were not needed for the supported paste
path.
Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>
* MM-66082: Fix invite input typing regressions
Fix premature chip creation while typing valid emails by using the paste
classifier only when it returns bulk mode, keep the valid address default
message wired correctly, and add regression coverage for typing and blur
behavior in both UsersEmailsInput and InviteView.
Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* MM-67352 Prevent composer scroll jumps on formatting click
Keep formatting controls from stealing textarea focus on mousedown so long drafts stay in place when markdown buttons are clicked. Add a regression test for the formatting bar interaction.
Made-with: Cursor
* Update webapp/channels/src/components/advanced_text_editor/formatting_bar/formatting_icon.tsx
Co-authored-by: Harrison Healey <harrisonmhealey@gmail.com>
---------
Co-authored-by: Harrison Healey <harrisonmhealey@gmail.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* fix(mmctl): prevent nil pointer panic in websocket command on connection failure
When the WebSocket connection fails immediately, Listen() closes EventChannel
via defer. Reading from a closed channel with a plain receive returns nil,
causing a panic in ToJSON(). Switch to range so the loop exits cleanly,
add a nil guard, and surface ListenError to the caller.
Fixes MM-68351
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(mmctl): add unit tests for websocket nil event and ListenError handling
Extracts the event-processing loop into processWebSocketEvents to enable
unit testing, and adds tests covering the nil-event skip and error surfacing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(mmctl): add happy-path subtest for processWebSocketEvents
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>