Commit Graph
3443 Commits
Author SHA1 Message Date
6941f56901 [MM-70252] Return 400 for malformed date filters in logs query API (#37970)
* [MM-70252] Reject malformed date filters in logs query API

The POST /api/v4/logs/query endpoint parsed date_from/date_to with a fixed
layout and swallowed parse errors, silently dropping the bound instead of
signalling the caller. A malformed date_from became the zero time and a
malformed date_to became now, so the request returned HTTP 200 with an
unfiltered result set.

Add LogFilter.IsValid, which rejects a non-empty bound that cannot be parsed
with the shared LogFilterDateLayout while keeping empty strings meaning
"unbounded", and call it from queryLogs so a bad filter returns 400 naming the
offending field and the expected layout.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* [MM-70252] Add tests for logs query date filter validation

Add a unit test for LogFilter.IsValid covering empty (unbounded), valid, and
malformed bounds, and an api4 integration test that drives POST /logs/query
through the real router to assert malformed date_from/date_to return 400 with
the offending field id while empty and valid bounds return 200.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* [MM-70252] Harden logs query date filter tests

Address test-quality review: exercise the DateTo validation branch with a valid
non-empty DateFrom, move fallible checks out of the require.Eventually condition
to avoid a cross-goroutine failure, and make each api4 subtest self-contained by
polling for the expected messages via a shared helper so valid-bounds also
verifies filtering still returns records.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* [MM-70252] Retrigger CI/CodeRabbit after invalid public-module feedback

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* [MM-70252] Note shared LogFilterDateLayout usage in date filter

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* [MM-70252] Add Client4.QueryLogs to simplify logs query date filter tests

* Address PR feedback: 2 answered, 1 resolved, 0 declined

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: Ben Schumacher <ben.schumacher@mattermost.com>
2026-08-19 09:05:10 +02:00
Jesse Hallam ede2edab4d Enforce snake_case for mlog field keys (#37998)
* introduce mlogFieldNaming

* apply vet-fix changes

* Cover every keyed mlog constructor in the analyzer fixture

* clarify end result in comment

* Check mlog field keys on explicitly instantiated constructors
2026-08-18 18:09:41 -04:00
Jesse Hallam 925a09a5f2 Remove dead Email login button color settings (#38021)
* [MM-57557] Remove dead Email login button color settings from the server

EmailSettings.LoginButtonColor, LoginButtonBorderColor and LoginButtonTextColor
were plumbed into the client config as EmailLoginButtonColor /
EmailLoginButtonBorderColor / EmailLoginButtonTextColor, but no client — web or
mobile — ever consumed them, so the email login button was never colored by
these values.

Remove the fields from the config struct and its defaults, drop the three client
config props, and update the config fixtures that carried them.

Also fixes MM-57556 and MM-57804, and follows the same removal already done for
the AD/LDAP (MM-70140) and SAML (MM-70141) equivalents.

* [MM-57557] Remove Email login button colors from the webapp and docs

Drop the three Email Login Button Color settings from the Admin Console
Experimental Features section along with their en.json strings, remove the
matching ClientConfig and AdminConfig EmailSettings entries to stay in sync with
the server model, and delete the corresponding documentation entries.

The experimental settings doc's jq example referenced
EmailSettings.LoginButtonColor, which no longer exists; point it at
EmailSettings.EmailBatchingBufferSize instead.
2026-08-18 17:36:03 -04:00
Jesse Hallam 0bff02c814 Graduate user typing settings to Site Configuration > Posts (#38023)
* [MM-57814][MM-57815] Graduate user typing settings to Site Configuration > Posts

Move ServiceSettings.EnableUserTypingMessages and
ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds out of
System Console > Experimental > Features into the Performance & Limits
section of System Console > Site Configuration > Posts, and reclassify
their access tags from experimental_features to site_posts (preserving
write_restrictable and cloud_restrictable).

The two settings stay adjacent, and the timeout remains disabled while
typing messages are off. The timeout label now states its unit, since
"User Typing Timeout" alone did not convey milliseconds. The i18n ids
move from admin.experimental.* to the Posts page's admin.posts.*
convention; the "E.g.: 5000" placeholder previously shared with the
experimental user status and profile fetching poll interval is now
defined once per setting.

No config keys, defaults, or runtime behavior change.

* [MM-57814][MM-57815] Assert user typing settings are searchable under Posts

Searching the System Console for "typing" now also matches
Site Configuration > Posts, guarding the new location of the user typing
settings. Experimental Features still matches on unrelated help text
about typing a tilde to trigger channel autocomplete.

* [MM-57814][MM-57815] Move user typing settings docs out of Experimental

Document "Enable user typing messages" and "User typing timeout" in the
Posts section of the site configuration settings guide, and drop them
from the experimental configuration settings guide.
2026-08-18 16:32:00 -03:00
Scott Bishel 78d120399f MM-68396: Remove deprecated dialog date/datetime fields for v12.0 (#37759)
* Drop top-level min_date/max_date/time_interval and allow_manual_time_entry;
require datetime_config (and manual_time_entry). Update docs, tests, and e2e fixtures accordingly.

* update important-upgrade-notes.rst per Doc Impact Analysis
2026-08-18 10:58:59 -06:00
44d12bef80 [MM-66243] Omit sanitized last_viewed_at/last_update_at instead of returning -1 for other users (#37505)
* Omit sanitized channel member timestamps from JSON

The channel member sanitization introduced in #33835 replaced other
users' LastViewedAt and LastUpdateAt with -1, which clients decode as
Dec 31 1969. Serialize the sanitized sentinel as an absent field instead
so the API no longer returns an invalid timestamp for other users.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Add tests and API docs for omitted sanitized member timestamps

Verify at the JSON layer that last_viewed_at and last_update_at are
omitted for other users' memberships (across the channel and user
endpoints) while remaining present for the requester, including a
legitimate zero timestamp. Document the omission in the API spec.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Strengthen sanitized-timestamp test coverage

Cover the NDJSON streaming branch of getChannelMembersForUser and the
getChannelMembersForTeamForUser endpoint, assert the requester's own
timestamps are valid (not the sentinel), use the sanitizedTimestamp
constant, and note the ChannelMemberForExport marshaling footgun.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Marshal team data via a typed struct in ChannelMemberWithTeamData

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Avoid shadowing err in ChannelMemberWithTeamData.MarshalJSON

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Use omitzero tags to omit sanitized member timestamps

Replace the custom ChannelMember/ChannelMemberWithTeamData MarshalJSON
round-trip with the Go 1.24 omitzero tag on LastViewedAt/LastUpdateAt.
SanitizeForCurrentUser now zeroes another user's timestamps so they are
omitted from API responses, per reviewer feedback.

* Give current user a real last_viewed_at in sanitization test

With omitzero, a zero last_viewed_at is legitimately omitted. Have user2
post an unread message and the current user view the channel so the
current-user assertions verify a genuine timestamp survives sanitization.

* Use -1 sentinel for sanitized member timestamps with single-pass marshal

A last_viewed_at of 0 legitimately means "never viewed", so it cannot
double as the sanitization sentinel. Restore the -1 sentinel and omit it
during serialization via shadowing pointer fields, avoiding the previous
marshal/unmarshal/marshal round-trip.

* Clarify ChannelMember.MarshalJSON doc comment per review feedback

* Address PR feedback: 0 answered, 4 resolved, 0 declined

- Simplify sanitizedTimestamp and SanitizeForCurrentUser doc comments per review
- Document that new ChannelMemberWithTeamData fields must be added to MarshalJSON
- Add round-trip test guarding against fields dropped by MarshalJSON

* Address PR feedback: remove round-trip MarshalJSON test

The round-trip test did not guard against forgetting to add a new field to
MarshalJSON, since the same field would also be missing from the test.

* Address PR feedback: assert legitimate zero last_update_at is serialized

* Mark sanitized channel member timestamp fields as nullable in OpenAPI spec

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-08-18 16:46:56 +00:00
6938cabac6 [MM-69646] Disallow MoveThreadsEnabled feature flag (fail server startup) (#37966)
* [MM-69646] Disallow MoveThreadsEnabled feature flag

Reject the MoveThreadsEnabled feature flag during config validation so the
server fails to start while it is enabled. The feature is being retired in
favor of Wrangler and will be removed later.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* [MM-69646] Cover nil FeatureFlags guard in config validation test

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Move MoveThreadsEnabled comment into isValid method body

Keep isValid's doc comment generic since it will validate more flag
combinations in the future, and place the MoveThreadsEnabled-specific
rationale next to the actual flag check.

* [MM-69646] Update TestMoveThread for retired MoveThreadsEnabled flag

Config.IsValid now rejects enabling MoveThreadsEnabled, so the
move-thread API stays disabled. Replace the enabled-path suite with
assertions that the flag cannot be turned on and MoveThread returns 501.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* [MM-69646] Stop forcing MoveThreadsEnabled in e2e environments

E2E was setting MM_FEATUREFLAGS_MOVETHREADSENABLED=true, which now fails
Config.IsValid and prevents the test server from starting. Remove the
override and skip Cypress move-thread specs that require the retired flag.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* [MM-69646] Skip TestMoveThread instead of asserting disabled flag

Mirror the E2E describe.skip approach: retain the original TestMoveThread
body and skip it at the top, since MoveThreadsEnabled is retired and
rejected by Config.IsValid.

* [MM-69646] Park cursor away from post dot menu in edit_file_attachment specs

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Jesse Hallam <jesse@mattermost.com>
2026-08-18 16:11:16 +00:00
Jesse Hallam eb3966e30b Remove atmos/camo image proxy support (#37284) 2026-08-18 11:25:17 +00:00
Jesse Hallam 0912a75c9c Bump minimum supported Postgres version to v15 (#37285) 2026-08-18 11:23:20 +00:00
Jesse Hallam dc6ab54f82 MM-67510 Drop deprecated autotranslation column from ChannelMembers (#37496) 2026-08-18 11:03:05 +00:00
Jesse Hallam 95fc4743df Drop RHEL 7/8 support: switch build image to golang-bookworm (#37229) 2026-08-18 10:59:32 +00:00
Jesse Hallam 54939d47c0 [MM-68249] Drop support for OpenSearch v1.x (#37283) 2026-08-18 07:41:05 -03:00
Felipe MartinandCursor f112b9a715 Remove deprecated built-in Slack import API and CLI (#37999)
The webapp Slack import path was deprecated in v6.0 in favor of mmetl
and Mattermost bulk import; remove the leftover API, importer package,
CLI command, and import_team permission.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-18 08:41:14 +02:00
2b40a0bdae MM-67868: Remove deprecated Slack compatibility type aliases (#37163)
Remove the deprecated backward-compatibility aliases introduced in #35445:
SlackAttachment, SlackAttachmentField, ParseSlackAttachment, and
StringifySlackFieldValue. Plugins should now use the MessageAttachment
equivalents directly.

SlackCompatibleBool is retained as it is still actively used.


Claude-Session: https://claude.ai/code/session_01KnMUsaSbm4HQsNEEtH5zp8

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-08-17 10:11:17 +00:00
Ben SchumacherandClaude ea183fab48 [MM-67157] Remove format parameter requirement from client license endpoint (#37167)
* MM-67157: Remove unused format flag from /license/client endpoint

The `format` query parameter on the `/license/client` (and local
variant) endpoint was effectively dead: it was required but only ever
accepted the single value `old`, returning an error otherwise. This
mirrors the earlier removal of the same flag from `/config/client`,
where the server now ignores the parameter while clients continue to
send `format=old` for compatibility with pre-v11 servers.

The server no longer inspects the `format` parameter, so requests with
no format, `format=old`, or any other value all succeed. The unused
i18n string and the parameter/response documentation in the OpenAPI
spec are removed accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Audz4JLNQN5SJxcPwyQBd

* MM-67157: document format=old retention in webapp client

Mirror the getClientConfig comment so the format=old query param on
getClientLicenseOld is not mistakenly removed; clients keep sending it
for compatibility with pre-v11 servers even though current servers now
ignore it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Audz4JLNQN5SJxcPwyQBd

* MM-67157: stop sending format=old from webapp client

Now that the server ignores the format parameter on /license/client,
drop format=old from the @mattermost/client getClientLicenseOld call and
update the e2e intercepts/helpers that matched the old query string.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Audz4JLNQN5SJxcPwyQBd

* Drop unneeded wildcard from license/client cy.intercept path

The format query param is gone from GET /license/client requests, so
the trailing * used to match it is no longer needed.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-08-17 08:43:18 +02:00
2945359dcc [MM-69895] Delete bot access tokens when permanently deleting a bot (#37907)
* [MM-69895] Delete bot access tokens on permanent bot deletion

App.PermanentDeleteBot removed the bot and user rows but left the
bot's UserAccessToken rows (and their sessions) orphaned, since the
UserAccessTokens table has no FK cascade to Users. Call
UserAccessToken().DeleteAllForUser to match PermanentDeleteUser.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* [MM-69895] Strengthen bot access token deletion regression test

Assert specific not-found errors, cover sessions for every bot token,
and add a control bot to prove deletion is scoped to the deleted bot.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* [MM-69895] Assert not-found status on deleted bot tokens

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* [MM-69895] Clear session cache when permanently deleting a bot

Deleting the access token rows via DeleteAllForUser is plain SQL and never
clears the in-memory session cache, so the bot's tokens kept authenticating
after PermanentDeleteBot. Mirror PermanentDeleteUser: delete sessions, delete
tokens, then clear the session cache (which also broadcasts to the cluster).

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-08-15 06:58:58 +00:00
44c0490c7c Prevent system-owned bots from being disabled (#37200)
* Prevent system-owned bots from being disabled

System-owned bots (system-bot, content-review) could be disabled either
directly via the API or via the owner-deactivation path when
DisableBotsWhenOwnerIsDeactivated=true. Once disabled, they never
self-healed, silently breaking post reminders, reports, and channel
notifications.

- Add model.ProtectedBotUsernames and remove the dead
  BotWarnMetricBotUsername constant.
- Guard UpdateBotActive so protected bots cannot be disabled (403),
  covering both the API and disableUserBots paths.
- Auto-heal the system bot in GetOrCreateSystemOwnedBot by fetching
  including deleted and re-enabling if disabled.
- Hide the Edit and Disable controls for protected bots in the System
  Console bot list.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Strengthen tests for protected system bots

- Parameterize the app-layer guard test over both protected usernames
  (system-bot and content-review).
- Assert the underlying user is also reactivated by the auto-heal path.
- Drive the owner-deactivation test through the real UpdateActive path and
  add a non-protected bot to prove the batch keeps disabling other bots.
- Add an API-layer test asserting a 403 when disabling the system bot.
- Make the webapp recovery test click Enable and assert the action fires.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Address CodeRabbit feedback on protected bot reactivation

- Add reactivateProtectedBot to bypass active-user limit checks when
  auto-healing or re-enabling disabled system-owned bots
- Fail closed on bot store lookup errors in UpdateBotActive before
  mutating user state

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Fix govet shadow lint in reactivateProtectedBot

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Update unknown bot test for bot-first lookup in UpdateBotActive

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Address PR feedback: DRY protected bot reactivation, range over ProtectedBotUsernames, label system bots as Managed by Mattermost

* Refactor UpdateActive to share inner updateActive with protected bot reactivation

* Address PR feedback: remove user-limit bypass for bot activation

Bot accounts are excluded from the active-user/license counts (User().Count
defaults to IncludeBotAccounts=false), so the dedicated bypass path was guarding
a case that cannot occur. Revert the UpdateActive/updateActive split and the
protected-bot branch in UpdateBotActive; bot (re)activation goes through the
normal UpdateActive path again.

* Replace hardcoded webapp protected-bot list with server-driven system_owned field

Addresses marianunez's review comment: the webapp kept its own copy of the
protected bot usernames (system-bot, content-review), duplicating
model.ProtectedBotUsernames and risking silent drift if a new system-owned
bot is added server-side without updating the client list.

model.Bot now computes IsSystemOwned() from ProtectedBotUsernames and
serializes it as system_owned via a custom MarshalJSON, so the webapp reads
it directly off the bot instead of matching usernames itself.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: Ben Schumacher <ben.schumacher@mattermost.com>
2026-08-15 08:12:58 +02:00
Nate Schlossberg 1578db0729 Fix repeating 400s for post_persistent_notifications and delete_expired_posts jobs (#37874) 2026-08-14 09:02:49 -07:00
989d83c637 MM-69403 filter job websocket updates by permission (#37650)
* MM-69403 filter job websocket updates by permission

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Fix websocket event deep copy test setup

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Move job read permission mapping out of public model

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Address job websocket permission review findings

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Remove shared job read permission helper

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Remove extra generic job permission mappings

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Document cached websocket manage system lookup

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Fail closed on job_updated permission filtering during mixed-version rollouts

RequiredPermissions is silently dropped by nodes running a version that
predates it, so keep setting ContainsSensitiveData as a sysadmin-only
fallback for those nodes instead of broadcasting the unfiltered job.
ShouldSendEvent on upgraded nodes ignores ContainsSensitiveData whenever
RequiredPermissions is present.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
2026-08-14 17:37:42 +02:00
Bill Gardner bc6a0c1ebf MM-69881: Add a size limit to the local image proxy's direct image fetch (#37848)
* MM-69881: Cap image size buffered by the local image proxy's direct fetch

ServeImage now accepts an optional max byte count.

* Log only the host, not the full URL, when discarding an oversized image

* Clarify ServeImage doc comment: make maxBytes=0 behavior explicit
2026-08-14 10:27:29 -04:00
6e85747816 MM-70100: Adjust Slack import user handling based on import type (#37818)
* MM-70095: Adjust Slack import user handling based on import type

See MM-70095.

* MM-70095: Add test coverage for non-admin import save failure on email conflict

Ensures the non-admin account-creation fallback path doesn't report success when the underlying save is rejected.

* MM-70095: Assert Save is invoked in email-conflict save-failure test

Explicitly verify the mocked Save call is exercised rather than relying on its return value alone.

* MM-70095: Fix non-admin Slack import user handling

* MM-70095: Fix non-admin Slack import user handling

* MM-70100: Clarify Slack import log message for matching account emails

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Bill Gardner <billg@wavearts.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 10:24:37 -04:00
Devin Binnieandcoderabbitai[bot] 9a9bbe28bd [MM-70188] Convert the platform, os, browser user agent session attributes to select fields (#37969)
* [MM-70188] Convert the platform, os, browser user agent session attributes to select fields

* Update server/public/model/session_attributes.go

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-08-14 08:47:09 -04:00
M-ZubairAhmed a234862de7 [MM-69816] Update prepackaged Calls to v1.12.3 (#37985) 2026-08-14 17:51:05 +05:30
4f8b9d8195 [MM-69641] Promote EnableExportDirectDownload to a Cloud-only configuration setting (#37477)
* promote EnableExportDirectDownload to a Cloud-only configuration setting

Replace the FeatureFlags.EnableExportDirectDownload feature flag with a
FileSettings.EnableCloudExportDirectDownload configuration setting, gated
to Mattermost Cloud environments.

The /exportlink slash command and the export generate-presigned-url API
now require FileSettings.EnableCloudExportDirectDownload to be enabled and
a Cloud license. Operators previously enabling the feature via
MM_FEATUREFLAGS_ENABLEEXPORTDIRECTDOWNLOAD should transition to
MM_FILESETTINGS_ENABLECLOUDEXPORTDIRECTDOWNLOAD.

* add tests for Cloud-only export direct download gating

Cover the new EnableCloudExportDirectDownload + Cloud-license gate on
GeneratePresignURLForExport (app) and the generate-presigned-url API
(api4).

* add EnableCloudExportDirectDownload to FileSettings type

* gate export direct download on Cloud alone, without a configuration setting

* Use a bounded HTTP client in export presigned-URL tests

http.Get has no total timeout; if MinIO accepts the connection but
stalls, these tests can hang until the suite timeout.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-08-14 11:32:19 +00:00
Bill GardnerandMattermost Build 22eaa8b03b [MM-69889] Improve handling of RelayState in SAML flow (#37837)
* [MM-69889] Improve handling of RelayState in SAML flow

RelayState was base64-decoded and trusted without any integrity check,
letting its contents be tampered with client-side. Sign relayProps with
an HMAC key (generated once, cached, stored like AsymmetricSigningKey)
before handing it to the IdP, and verify the signature before trusting
any of its fields on the way back.

* Add short expiry to signed RelayState

Bound the signed RelayState's validity to 5 minutes to restrict the
window in which a captured, unmodified RelayState could be replayed.

* [MM-69889] Use maps.Copy in SignSamlRelayState

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2026-08-14 09:36:17 +02:00
Edgar Bellot MicóandBill Gardner 663ad3dae9 MM-70072: Update team admin assignment during team join (#37922)
* MM-70072: Update team admin assignment during team join

* Assert SchemeUser in team rejoin test case

* MM-70072: Fix team admin assignment in bulk import path

* Preserve computed admin status through scheme role sync in bulk import

---------

Co-authored-by: Bill Gardner <billg@wavearts.com>
2026-08-13 14:22:06 -04:00
Nick MisasiandCursor Agent 9dfbaeca99 Add weekly recurring scheduled posts (#37746)
* Add weekly recurring scheduled posts.

Extend scheduled posts so users can schedule weekly repeats and keep the series healthy across sends, reschedules, and UI updates instead of falling back to one-shot behavior.

Made-with: Cursor

* Add Playwright coverage for recurring scheduled posts.

Cover weekly recurring scheduled messages in the scheduled-messages spec so the recurring UI and reschedule flow stay protected without adding a separate test surface.

Made-with: Cursor

* Fix recurring scheduled post CI failures.

Resolve the initial lint and formatting issues and renumber the new scheduled-post migration so it no longer collides with master during Postgres-backed test setup.

Made-with: Cursor

* Sync recurring scheduled post translation files.

Regenerate the affected English translation catalogs so the recurring scheduled post strings match the source extraction order expected by CI.

Made-with: Cursor

* Fix recurring scheduled post review follow-ups.

Preserve overdue cleanup behavior during weekly catch-up, defer delete websocket events until deletion succeeds, and address the remaining migration and UI review nits.

Made-with: Cursor

* Address recurring scheduled post review feedback

Made-with: Cursor

* Fix recurring scheduled post CI checks

Made-with: Cursor

* Make pending scheduled post keyset cursor index-scannable

EXPLAIN ANALYZE on a 5M-row ScheduledPosts table showed the pure OR-form
cursor forced Postgres to scan idx_scheduledposts_pending_scheduled_at_id
from the top on every page (~383ms/page, ~2M rows filtered). Keeping the
ScheduledAt <= beforeTime bound outside the tie-break restores the index
boundary (~0.12ms/page). Adds storetest coverage for cursor pagination.

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Address code quality review findings for recurring scheduled posts

Server:
- Replace silent-fallback AdvanceWeeklyScheduledNextOccurrence with
  error-returning ScheduledPost.ComputeNextScheduledAt; a recurring post
  whose timezone fails to load is now routed through the failed-post path
  instead of being reposted every job run or silently deleted
- Add ScheduledPost.IsRecurring and partition batches in
  processScheduledPostBatch; advance/delete now run independently so a
  store failure in one path can't cause reposts in the other
- Drop dead generality in UpdateRecurringScheduledPosts (only ScheduledAt
  varies per row; ErrorCode/ProcessedAt are constants)
- Simplify redundant repeat-type condition in GetPendingScheduledPosts

Webapp:
- Add shared isRecurringScheduledPost helper, replacing six scattered
  repeat_type === 'weekly' literals
- Recurrence timezone is now simply the scheduler's current timezone;
  removes initialRepeatTimezone/effectiveTimezone plumbing and the
  modal-label/picker timezone mismatch
- Single enforcement point for hiding send-now on recurring posts
- Use canonical getTeamIdByChannelId at both SCHEDULED_POST_UPDATED
  dispatch sites; rewrite errorsByTeamId update case as remove-then-add
  and add reducer tests

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Simplify recurring scheduled post code per review

- End a recurring series when its channel no longer exists instead of
  advancing it forever (matches the one-shot channel-not-found handling)
- Collapse the errorsByTeamId SCHEDULED_POST_UPDATED case into the
  identical SINGLE_SCHEDULED_POST_RECEIVED case (a scheduled post's team
  can't change) and combine duplicate byId cases; preserves state
  references on no-op updates
- Drop no-op timezone conversions in ComputeNextScheduledAt
- Remove redundant checkbox aria-label (label htmlFor already names it)
- Schedule new job tests in the past instead of sleeping a real second
- Remove redundant test assignment and e2e positional boolean

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Remove slop from recurring scheduled post changes

- Drop the business-rule CHECK constraint from the recurrence migration;
  no other migration enforces model-layer validation in the database,
  and BaseIsValid plus the job's failure handling already own it
- Fold the standalone repeat-validation test file into the existing
  TestScheduledPostBaseIsValid, matching its conventions
- Revert unrelated benchmark modernization in utils_test.go
- Drop an unneeded cast, unused fixture fields, and naming/assertion
  inconsistencies in tests

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Regenerate ScheduledPostStore mock with mockery ordering

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Address CodeRabbit review feedback

- Reject the host-dependent 'Local' value for RepeatTimezone; recurring
  schedules need a fixed zone (UTC or IANA name)
- Hide reschedule for deactivated DMs, matching send-now eligibility

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Resolve scheduled post team bucket from existing state when channel is unloaded

An admin can reschedule before fetchMissingChannels resolves, in which case
deriving the team from the channel returns undefined and the update was
misfiled under directChannels. getScheduledPostTeamId falls back to the
byTeamId bucket that already holds the post.

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Retrigger CI after transient enterprise npm network failure

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Clear hover/focus before asserting scheduled post header details in e2e

The drafts panel hides its timestamp/tag info section while hovered or
focus-within; after the reschedule modal closes, focus returns to the row
and the 'Repeats weekly' tag assertion saw a hidden element.

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Move recurrence columns into baseColumns and dispatch ComputeNextScheduledAt on repeat type

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Preserve recurrence when scheduled post updates omit repeat fields

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Disallow file attachments on recurring scheduled posts

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Stop pinning the channel indicator for recurring-only scheduled posts

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Re-home nullable-Type comment onto baseColumns

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Gate recurring scheduled posts behind a default-off feature flag

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Move presence-preservation rationale to the copy site

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Extract draftHasAttachments helper and require allowRecurring at single-caller layers

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Return null from the indicator selector when nothing should show

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Gate only recurrence transitions and preserve existing series in the modal

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Avoid err shadowing in scheduled post update handler

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Disable the repeat weekly checkbox with a tooltip when the message has attachments

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Return an explicit disposition from postScheduledPost

The batch loop inferred 'channel permanently gone' from the one return
path with a nil error and an error code set - an invariant a future
change could silently break, deleting recurring series by accident.
postScheduledPost now returns posted/failed/unsendable explicitly, the
switch refuses to delete on an unhandled disposition, and a test pins
that both recurring and one-shot posts in a nonexistent channel are
permanently deleted.

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Fix Repeat weekly attachments tooltip centering on the modal row (#37927)

Shrink-wrap the repeat checkbox row so WithTooltip anchors to the
control/label instead of the full modal body width.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-08-13 07:27:23 -04:00
Ben SchumacherandCursor Agent 0eb2ec5a17 [MM-70226] Migrate role GetByName to request context (#37634)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-08-13 06:33:28 +00:00
Ben SchumacherandClaude Sonnet 4.6 2df50ab1fb [MM-69911] Include PAT token ID in server and audit logs for request traceability (#37910)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-13 08:00:48 +02:00
Harshil Sharma 65b1437d08 Added DM GM restriction for flagging a post (#37841)
* Added DM GM restriction for flagging a post

* Added DM/GM check in other content reviewer paths as well

* Fixed an order of operation
2026-08-13 08:05:24 +05:30
Jesse Hallam 27a5abe2d4 Log an error instead of refusing to start on unsupported Postgres, Elasticsearch, and OpenSearch versions (#37929) 2026-08-12 16:55:41 -04:00
Edgar Bellot MicóandClaude Sonnet 5 d0be8f408e MM-70240: Adjust post and thread payload sanitization (#37920)
* MM-70216: Adjust post and thread payload sanitization

* MM-70216: Preserve MM blocks actions in thread test fixture

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 15:07:08 -04:00
Devin Binnie bf0f9de078 [MM-70189] Add operators for CIDR and version checks to the simple policy editor (#37918) 2026-08-12 16:25:48 +00:00
Christopher Poile 929a2e9e3f [MM-70106] Prevent search startup bulk processor leaks (#37873)
* the main fix + unit tests

* add actual analysis-icu-less docker containers in CI to lock it down

* fix AppError test assertions

* fix CI search startup tests

* fix bulk processor flusher shutdown

* fix bulk shutdown test assertion

* Revert "add actual analysis-icu-less docker containers in CI to lock it down"

This reverts commit dfa9aacb08.

* simplify analysis-icu startup guidance

* fix OpenSearch bulk flusher shutdown

* linting fix

* query nodes directly for plugins instead of trying and catching err

* remove unneeded comments
2026-08-12 10:40:24 -04:00
Ben SchumacherandClaude 523292f081 Remove unused context import left behind by UserStore.Get migration (#37921)
PR #37646 (Migrate UserStore Get to request context) migrated
context.Background() call sites to request.CTX but left the now-dead
"context" import in place, assuming other usages in the same files
would keep it alive. A concurrently merged sibling PR (#37637,
GetAllProfilesInChannel migration) removed those other usages first,
so by the time #37646 landed on top of the moved master tip the
import had no remaining references, breaking go vet/build on master.

Affected: server/channels/store/sqlstore/user_store.go,
server/channels/store/storetest/user_store.go,
server/channels/store/storetest/mocks/UserStore.go,
server/channels/store/localcachelayer/user_layer.go(_test.go),
server/channels/app/post_persistent_notification.go

Co-authored-by: Claude <noreply@anthropic.com>
2026-08-12 10:07:43 +00:00
9f0ae6a220 [MM-70222] Migrate UserStore Get to request context (#37646)
* Migrate UserStore Get to request context

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Clean up context imports after UserStore Get migration

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Fix UserStore Get test context fallout

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Update app interfaces for request-aware user get

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Update UserStore Get test fakes

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Thread request context through user get callers

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Use email service logger for batching context

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Pass request context to trial license lookup

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Pass request context to buildShownByList

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Pass request context to buildFavoritedByList

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Add request context to PromoteGuestToUser

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Pass request context to trial and demote flows

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Use request context in AddUserToTeam lookup

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Use plugin request context for trial license

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Use request contexts throughout team user lookups

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Document future GetUser request context migration

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
2026-08-12 09:33:46 +02:00
270a503054 [MM-70225] Migrate Store.GetDiagnostics to request.CTX (#37635)
* Migrate store diagnostics to request context

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

* Keep store context rule only in server AGENTS

Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-08-12 09:03:09 +02:00
265f1509fa [MM-70223] Migrate GetAllProfilesInChannel to request context (#37637)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
2026-08-12 09:02:22 +02:00
Doug Lauder 1f08ac5bb0 MM-69886: Refresh Channel Members RHS on websocket add and reconnect (#37584)
* MM-69886: Refresh Channel Members RHS on websocket add and reconnect

The Channel Members RHS (participant list) could omit members in two
general cases:

- On a user_added websocket event, handleUserAddedEvent recorded the
  profile-in-channel entry and loaded the profile but never loaded the
  ChannelMembership. buildProfileList drops any user without a
  membership, so a member added while the channel was open did not
  appear until the list was fully reloaded. It also affected members
  whose profile was already loaded (e.g. after they posted) but who
  still had no membership. Fetch the membership via getChannelMember
  when it is missing.

- On websocket reconnect, reconnect() reloads only the current user's
  own memberships, never the channel roster, and missed user_added /
  user_removed events are not replayed on a fresh (non-resumed)
  connection. Members added or removed while disconnected stayed stale
  until the channel was re-opened. The RHS now reloads the roster when
  the websocket connection id changes.

This extends the reconcile-on-load work from MM-68660 (PR #36964, same
loadProfilesAndReloadChannelMembers path), which pruned removals only
on channel (re)load and did not cover live adds or reconnects.

Found while re-testing MM-67616 (Shared Channels), where synced members
were not reflected in the participant list even though the server-side
sync was correct.

* Add comment to caution about changing remote cluster service ping frequency.
2026-08-11 16:44:25 -04:00
d3c26275b6 fix(mmctl): lengthen sampledata passwords to meet FIPS minimum (#37867)
* fix(mmctl): lengthen sampledata passwords to meet FIPS minimum

Zero-pad user index to 3 digits in guest and regular-user password
format strings so all generated passwords are >= 14 characters,
matching PasswordFIPSMinimumLength. Update inject-test-data login
hint and add a regression test.

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Simplify sampledata password length test comment

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Use subtests in sampledata password length coverage

So a single require failure reports only that case instead of
aborting the rest of the matrix.

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Name default-user subtests as user for clarity

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Run sampledata password length subtests in parallel

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Empty commit to re-trigger CI

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 17:28:58 +00:00
4a68234d75 [MM-69601] mmctl: support file attachments in post create (#37310)
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: Doug Lauder <wiggin77@warpmail.net>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-08-11 16:21:18 +02:00
Edgar Bellot MicóandBill Gardner 8ce3c54a5e MM-70016: Fix edge case in team invitation handling (#37741)
* MM-70016: Fix edge case in team invitation handling

* Add test coverage for guest magic link invitation token

---------

Co-authored-by: Bill Gardner <billg@wavearts.com>
2026-08-10 23:06:24 +02:00
Devin Binnie a6d008c5c2 [MM-70188] Convert the os_platform session attribute to a select field (#37901) 2026-08-10 16:03:20 -04:00
Edgar Bellot Micó 9cf617a14b MM-70072: Fix role validation for channel and team member updates (#37791)
* MM-70072: Fix role validation for channel and team member updates

* MM-70072: Assert specific error ID in guest/admin role regression tests

Make the new regression tests assert the specific error ID returned for
the guest+admin role combination, rather than only checking for a
generic bad-request status.
2026-08-10 15:40:13 -04:00
Edgar Bellot MicóandClaude Sonnet 5 f1e13cf62e MM-70040: Tighten team search filter combination logic (#37749)
* MM-70025: Fix team search filter combination logic

See MM-70025 for more details.

* MM-70025: Strengthen team search filter tests

Address CodeRabbit review feedback on PR #37749.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 16:47:27 +02:00
6242bc3e2f [MM-70140] Remove experimental AD/LDAP login button color settings (#37855)
* [MM-70140] Remove experimental AD/LDAP login button color settings

Remove the dead-code LdapSettings.LoginButtonColor / LoginButtonBorderColor /
LoginButtonTextColor experimental settings. These values were plumbed into the
client config but never consumed by the web or mobile clients, so no AD/LDAP
login button was ever rendered or colored.

Removes the fields from the server config struct and defaults, the client
config payload, the Admin Console Experimental Features section, the webapp
config type, related en.json strings, API definitions, docs, and test/default
config fixtures.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* [MM-70140] Add test guarding removal of LDAP login button color keys

Assert that GenerateLimitedClientConfig still emits LdapLoginFieldName under an
LDAP license but never emits the removed LdapLoginButtonColor/BorderColor/
TextColor keys, guarding against reintroduction of the dead settings.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* [MM-70140] Remove LDAP login button colors from webapp LdapSettings type

Keep AdminConfig LdapSettings in sync with the server model after the
experimental AD/LDAP login button color settings were removed.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* [MM-70140] Update admin console index test after LDAP color removal

Drop experimental/features from the ldap search expectation now that
the AD/LDAP login button color settings are no longer under Experimental.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Address PR feedback: remove test for removed LDAP login button color settings

Per @lieut-data's review, drop TestGenerateLimitedClientConfigOmitsLdapLoginButtonColors;
there's no need to perpetually test that a removed feature stays removed.

* chore: retrigger CI after GitHub Actions service outage

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* chore: retrigger CI after Actions service recovery

Co-authored-by: mattermost-code <matty-code@mattermost.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: mattermost-build <mattermost-build@users.noreply.github.com>
2026-08-10 10:26:19 -03:00
e02021193a [MM-70141] Remove dead experimental SAML login button color settings (#37857)
* [MM-70141] Remove dead SAML login button color settings

SamlSettings.LoginButtonColor / LoginButtonBorderColor / LoginButtonTextColor
were never consumed by any client (web or mobile) — the values reached the
client config but were never applied to the SAML login button, which renders
with fixed themed CSS. Remove the settings from the server model, client config
payload, Admin Console Experimental Features, webapp config types, config
fixtures, and documentation. The SAML login button label (LoginButtonText)
is retained.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* [MM-70141] Update admin console index test after SAML color removal

Searching for "saml" no longer matches Experimental Features once the
unused SAML login button color settings are removed from that section.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Address PR feedback: remove unnecessary SAML color removal test case

Per reviewer feedback, drop the GenerateClientConfig test case that
asserted the removed SAML login button color props are absent. There is
no value in perpetually testing that a deleted feature stays deleted.

* chore: retrigger CI after transient Actions outage

Previous Server CI / API / Web App CI failures on 0f03c475 were GitHub
Actions infrastructure errors (Failed to resolve action download info /
Service Unavailable), not related to this PR's changes.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-08-10 10:26:01 -03:00
cursor[bot] d04687af22 Fix flaky TestNewSyncsMarkdownMaxLenWithMaxPostSize (#37830)
Automatic Merge
2026-08-10 12:00:10 +03:00
a2a4903293 [MM-62445] Allow changing a team's name (slug) via mmctl team rename (#37169)
* Allow renaming a team's name (slug) via the team update API

The team update API path is sanitized and intentionally ignores the
Name field, so the previously orphaned RenameTeam app method (which no
longer persisted the Name change) is now wired into the updateTeam
handler and fixed to persist the new slug and emit a team update event.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Add --name flag to mmctl team rename

Allow changing a team's name (the URL slug) via mmctl team rename. The
command now accepts --name and/or --display-name, and at least one must
be provided.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Polish team rename error message and fix err shadow

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Strengthen team rename tests

Add regression coverage that the rename path cannot leak non-renamable
fields (email/type), that invalid and duplicate renames leave the slug
unchanged, and a unit case for renaming name and display name together.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Address PR feedback: 1 answered, 1 resolved, 0 declined

* Remove unused App.RenameTeam method

The rename path now goes through App.UpdateTeam (via the team update API
and mmctl), leaving App.RenameTeam orphaned. Remove it and update the
name-occupied error source label to UpdateTeam.

* Return non-not-found errors from team name lookup

Only treat store.ErrNotFound from GetByName as an available slug; any
other lookup failure is now returned instead of silently allowing the
rename to proceed. Addresses CodeRabbit review feedback on the team
update path.

* Remove low-value TestUpdateTeamNameLookupError per review feedback

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-08-10 09:11:17 +02:00
Harshil Sharma 351b4f9686 Updated order of validation in getFile API (#37843)
* Updated order of validation in getFile API

* Lint fix
2026-08-10 09:47:08 +05:30