Commit Graph

23119 Commits

Author SHA1 Message Date
Nick Misasi 62056e5a7c MM-70071: Automatically select hosted push notification server based on license (#37802)
* MM-70071: Automatically select hosted push notification server based on license

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

* test: fix mock-store fallout from push endpoint license listener

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

* MM-70071: address review feedback on push endpoint sync

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

* ci: retrigger enterprise tests against updated companion branch

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

* ci: retrigger flaky artifact build

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

* MM-70071: revert any hosted push endpoint to test on entitlement loss

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

* MM-70071: add nil-safe License.HasMHPNS entitlement check

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

* MM-70071: drop preview-tree docs for auto-selected push server

Monorepo MDX is still unpublished; this belongs in mattermost/docs.

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

* MM-70071: stub InitEmailBatching on guest-invite email mocks

License teardown now SaveConfigs the push endpoint, which fires the existing email-batching config listener.

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

* MM-70071: don't re-init email batching on push-server license sync

License teardown SaveConfigs the push endpoint, which fired the existing
email-batching listener and panicked tests that mock EmailService.
Re-init batching only when EnableEmailBatching changes, and stub the
remaining invite mock used during helper cleanup.

Co-authored-by: Nick Misasi <nick13misasi@gmail.com>

* MM-70071: re-init email batching when the interval setting changes

Keep EmailBatchingInterval live at runtime; only ignore unrelated
config writes such as the push-server license sync.

Co-authored-by: Nick Misasi <nick13misasi@gmail.com>

* MM-70071: isolate mock tests from push endpoint sync

Use custom push endpoints in shared mock fixtures so unrelated tests do not need config-listener expectations.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-08-20 16:11:21 +00:00
cursor[bot] c36f979edd [MM-70198] Fix post preview layout shift by overlaying the "Show more" control (#37974)
* [MM-70198] Overlay permalink preview "Show more" to avoid layout shift

Post permalink previews clip tall content and reveal a "Show more"
affordance only after the body is measured on mount. The ellipsis-style
button was rendered in normal flow below the clipped text, so when it
flashed in the preview grew taller and pushed following content down.

Position the ellipsis "Show more" button absolutely over the faded bottom
of the preview so it is revealed without changing the preview's height.

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

* [MM-70198] Satisfy stylelint property order

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

* Change fade out to use a mask-image so colours match

* Change new post preview fade out to fully fade before toggle text

* Add E2E test for post preview Show more layout shift

Verify that the overflowing permalink preview 'Show more' control is
overlaid on the preview and does not change the post height when it is
revealed after mount.

* Move new E2E test to match format of others

* Address PR feedback: extend permalink preview fade transparent stop to 24px

matthewbirtch suggested moving the mask-image fully-transparent stop from
calc(100% - 18px) to calc(100% - 24px). Applied to both the -webkit-mask-image
and mask-image declarations to keep them consistent.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: Harrison Healey <harrisonmhealey@gmail.com>
2026-08-20 14:30:34 +00:00
Eva Sarafianou bcc9ce5e4a docs(P14): reconcile developer docs drift through Mattermost Blocks (#38043)
* docs(P14): reconcile developer docs drift through Mattermost Blocks

* docs: fix developer docs MDX conversion

* docs: clarify developer redirect filtering

* docs: remove partial developer redirects
2026-08-20 12:27:30 +03:00
Ben Schumacher 7099dac602 [MM-70277] Improve plugin upload dropzone UX (#37569)
* Improve plugin upload dropzone UX

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

* Refine plugin upload dropzone presentation

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

* Update plugin upload Cypress specs

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

* Preserve plugin upload drag state over children

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

* Show plugin manifest name in upload success message

The success toast previously interpolated the raw uploaded filename
(e.g. mattermost-plugin-fl3xx-v0.8.1-linux-amd64.tar.gz), which is not
meaningful to admins. Use the plugin manifest name returned by the
upload response instead, falling back to the filename if unavailable.

* Indicate upgrade/downgrade/same-version on plugin overwrite

State whether an overwritten plugin's success message represents an
upgrade, downgrade, or same-version replace, comparing the previously
installed manifest version with the newly uploaded one via semver.

* Refine plugin upload dropzone presentation and uploading state.

Use a native button with Compass upload icon, keep progress inside the dropzone without changing height, and reserve button color for hover/drag-active.

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

* Show selected filename in the dropzone and improve upload status messaging.

Keep success/error feedback aligned with the dropzone, with a green check or red alert icon beside the message.

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

* Fix no-nested-ternary lint error in plugin upload dropzone

Extract the upload dropzone title text into a helper method to
satisfy eslint's no-nested-ternary rule.

* Fix stylelint order/properties-order errors in plugin_management.scss

* Add unit tests for formatUploadOverwriteMessage semver paths

Covers the upgrade, downgrade, same-version, and fallback
(missing/invalid semver) branches, which the automated test-analysis
bot flagged as untested.

* Add unit tests for remaining disabled-reason branches and file drop

Covers renderUploadDisabledReason's isDisabled and !enable branches,
and adds a fireEvent.drop test for handleUploadDrop, closing out the
remaining gaps from the automated test-analysis review.

---------

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>
Co-authored-by: Matthew Birtch <2040554+matthewbirtch@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 09:35:36 +02:00
Jesse Hallam 260d0cda82 Trim whitespace when saving comma-separated System Console settings (#38042) 2026-08-19 20:28:51 -03:00
Jesse Hallam 95cabdfb3b Graduate theme and onboarding settings to Site Configuration > Customization (#38027)
* [MM-57808][MM-57809][MM-57811] Graduate theme settings to Site Configuration > Customization

Move ThemeSettings.EnableThemeSelection, ThemeSettings.AllowCustomThemes, and
ThemeSettings.DefaultTheme out of System Console > Experimental > Features and
into Site Configuration > Customization, alongside the other branding settings.

The config keys, defaults, license gating, and the AllowCustomThemes dependency
on EnableThemeSelection are unchanged; only the System Console page and the
access classification move. The access tags become site_customization, so the
settings are now governed by sysconsole_{read,write}_site_customization rather
than sysconsole_{read,write}_experimental_features.

i18n ids move from admin.experimental.* to the Customization page's
admin.customization.* convention, and the documentation entries move from the
experimental configuration settings page to the site configuration settings
page.

* [MM-57812][MM-57813] Graduate tutorial and onboarding settings to Site Configuration > Customization

Move ServiceSettings.EnableTutorial and ServiceSettings.EnableOnboardingFlow out
of System Console > Experimental > Features and into Site Configuration >
Customization, next to the desktop app landing page setting that also governs a
user's first-run experience.

The config keys and defaults are unchanged; only the System Console page and the
access classification move. The access tags become site_customization, so the
settings are now governed by sysconsole_{read,write}_site_customization rather
than sysconsole_{read,write}_experimental_features.

i18n ids move from admin.experimental.* to the Customization page's
admin.customization.* convention, and the documentation entries move from the
experimental configuration settings page to the site configuration settings
page.

* [MM-57810] Surface ThemeSettings.AllowedThemes in Site Configuration > Customization

ThemeSettings.AllowedThemes has always existed in the server config and has
always been honoured by the theme picker, but it was never represented in the
System Console schema — not under Experimental > Features and not anywhere else.
Add it to Site Configuration > Customization alongside the theme settings it
constrains.

The server model is []string, so this uses the existing `type: 'text'` with
`multiple: true` widget, the same one ServiceSettings.DCRRedirectURIAllowlist
uses for its []string. The admin console joins the array with commas for display
and splits it back into an array on save, which matches how the client config
already serialises the value, so no behaviour or serialisation changes. The
setting picks up an access tag of site_customization, where it previously had
none, and the documentation entry moves from the self-hosted-only section of the
experimental configuration settings page to the site configuration settings page.

Also add a test asserting the graduated settings are present on Customization,
absent from Experimental > Features, and that AllowedThemes round-trips as a
string array.

* Add runtime-effect tests for graduated theme and onboarding settings

Expand coverage for the graduated Customization settings so that they are
verified to affect their features, not just to save:

- ThemeSettings.AllowedThemes: premade theme chooser only renders the
  allow-listed themes (theme enforcement on/off).
- ServiceSettings.EnableOnboardingFlow: onboarding task list is gated on
  the config value.
- ServiceSettings.EnableTutorial: the Channels tour tip is gated on the
  config value.
- EnableTutorial/EnableOnboardingFlow (and the theme bools) round-trip
  their value through the admin console schema.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
2026-08-19 20:58:18 +00:00
Jesse Hallam 4c6c5a063f Graduate Enable Channel Viewed WebSocket Messages to Environment > Web Server (#38026)
* [MM-57806] Graduate Enable Channel Viewed WebSocket Messages

Move ServiceSettings.EnableChannelViewedMessages out of System Console >
Experimental > Features and into Environment > Web Server, alongside the
other ServiceSettings transport and performance knobs it belongs with.

The access tag changes from experimental_features to environment_web_server,
so the setting is now governed by sysconsole_read/write_environment_web_server.
write_restrictable and cloud_restrictable are preserved, the default remains
true, and the client config continues to publish the value. No runtime
behavior changes.

The i18n ids move from the admin.experimental.* namespace to the
admin.service.* namespace used by the rest of the Web Server page.

* [MM-57806] Document channel viewed WebSocket messages under Environment

Move the Enable Channel Viewed WebSocket Messages entry from the
experimental configuration settings page to the Web Server section of the
environment configuration settings page, matching its new System Console
location, and reformat it to the two-column table style used there.

* [MM-57806] Cover the new location of the channel viewed setting

Assert that the setting is defined on the Environment > Web Server page,
that it is gated on write access to the Web Server console resource rather
than to Experimental Features, and that searching the admin console for
"channel_viewed" resolves to environment/web_server.

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-08-19 15:51:58 -03:00
Harrison Healey 9127a7d9b9 MM-69835 Update React Bootstrap to support React 19 (#37758)
* Update RB to latest version of fork

* Update most snapshots to remove extraneous div

* Forward ref to menu passed to RB Dropdown

* Update unusual tests broken by RB update

* Patch react-overlays to still close modal when default prevented

The old version of react-overlays that we had ignored this, but the newer version
causes the modal to not close when preventDefault is called on the escape keyboard
event. React Select always does that and some other components like the SuggestionBox
sometimes prevent default, and it doesn't seem like Floating UI or MUI look at
preventDefault to know whether or not to close the modal, so I think React Overlays
is the correct place for this patch.

* Remove @types/react-overlays in favour of built-in definitions

* Update RB to merged commit
2026-08-19 11:09:56 -04:00
cursor[bot] 19ffbc9c75 [MM-69643] Fail server startup when the AppsEnabled feature flag is enabled (#37968) 2026-08-19 08:54:00 -03:00
Ben Schumacher a7c6862497 [MM-70221] Use request loggers in store methods (#37648)
* Use request loggers in store methods

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

* Document request logger guidance

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

* Fix missed rctx args in scheduled post tests after master merge

go vet caught call sites the build alone didn't: test files with
stale ScheduledPostStore signatures missing the new rctx parameter.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-08-19 13:08:45 +02:00
Harshil Sharma fb87397dba Data spillage exposure radius report generation (#37809)
* WIP:

* WIP:

* Added API integration

* Removed some unneeded functions and cleaned up unnecessery comments

* CI

* Test improvements

* Coderabit fixes

* Report data updates

* Handled commas and few other chaaracters in channel name

* Data spillage exposure radius UI integration (#37820)

* UI implementation and integration of exposure report APIs

* Minor cleanup

* Coderabit fixes

* Allowed generating exposure report irrespective of status

* Used the new button component

* fixed lint error
2026-08-19 09:16:03 +00:00
sabril 020e9dabdd ci: bump test-system-io-summary action for missed-spec status (#37804)
* ci: bump test-system-io-summary action for missed-spec status

Placeholder bump pending merge of mattermost-test-system-io summary fix.

Co-authored-by: saturnino <saturnino@mattermost.com>

* ci: re-pin test-system-io-summary to main e2d5032

Replace the pre-merge placeholder SHA with the latest
mattermost-test-system-io main commit, which includes the
squash-merged missed-spec summary fix from #96.

Co-authored-by: saturnino <saturnino@mattermost.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: saturnino <saturnino@mattermost.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-08-19 07:45:22 +00:00
sabril ca6fd94e3d chore: bump playwright workers to 20 (#38015) 2026-08-19 15:23:07 +08:00
cursor[bot] 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
Ben Schumacher 480c1c5ed1 [M-70285] Fix plugin settings section handling (#38003)
* Fix plugin settings section handling

Co-authored-by: ben.schumacher <ben.schumacher@mattermost.com>

* Handle failed plugin activation in settings

Co-authored-by: ben.schumacher <ben.schumacher@mattermost.com>

* Process settings across schema sections

Co-authored-by: ben.schumacher <ben.schumacher@mattermost.com>

* Render mixed settings schema content

Co-authored-by: ben.schumacher <ben.schumacher@mattermost.com>

* Strengthen mixed schema rendering test

Co-authored-by: ben.schumacher <ben.schumacher@mattermost.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-08-19 09:02:56 +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
Julien Tant 5bd5b3b899 [MM-69865] Add Delete row action to Manage Attributes (#37875)
* [MM-69865] Enable the Delete row action on Manage Attributes

The kebab menu's Delete was stubbed as disabled + "Coming soon". Wire it
to a confirmation modal and the existing DELETE property-field endpoint,
dispatching PROPERTY_FIELD_DELETED so the row leaves via Redux rather
than local component state.

The modal closes immediately on confirm instead of freezing behind a
spinner, so failures surface in an AlertBanner above the table rather
than inside a modal that is already gone. A 409 (the server refuses
while live linked dependents exist) gets its own message instead of the
generic one -- the wording says "other attributes are still linked to
it", matching what CountLinkedFields actually counts, not the AD/LDAP
and SAML source links, which are plain attrs on the field itself.

Delete stays disabled on plugin-owned rows, relabelled "Plugin-managed"
so the reason is visible rather than the item silently doing nothing.

E2E covers confirm, cancel, and the 409 branch against a real server
response: the linked dependent is seeded through the API, which needs a
non-template object type because IsValid rejects a template field
carrying a linked_field_id.

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

* Resolve server-only plugin names instead of showing the raw plugin ID

getPluginDisplayName only read state.plugins.plugins, which holds
manifests for plugins that shipped a webapp bundle and registered
themselves in the browser. A server-only plugin is never in that map, so
every caller fell through to the fallback and rendered the bare ID --
"com.mattermost.gahelper" in the Manage Attributes Source column.

Consult state.entities.admin.pluginStatuses as a second source before
giving up on the ID. That map covers every installed plugin, server-only
included, and is empty for non-admins, so the non-admin callers
(user_settings_general, integrations/bots) are unaffected.

Nothing on the Manage Attributes page loaded those statuses, so fetch
them there -- once, and only when a plugin-owned row is actually
present. The result is deliberately not awaited: a failure just leaves
the column showing the ID it was already showing.

Also fixes the same latent fallback on custom_profile_attributes,
user_properties_values, and system_user_detail.

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

* Keep e2e attribute names under the 40-char Unique name cap

Every test in the "create attribute" block built expectedName from a
long prefix plus a 13-digit Date.now(), overshooting the Unique name
input's Constants.MAX_CUSTOM_ATTRIBUTE_NAME_LENGTH (40). The derived
slug truncated silently, so "playwright_created_attribute_<13 digits>"
(42 chars) was asserted against the 40 chars actually rendered.

The two linked-source tests only use expectedName for cleanup, so
instead of failing they quietly leaked their seeded field onto the
shared server on every run.

Shorten the prefixes so the derived slug fits. The whole file is
mode: 'serial', so these failures also skipped every test after them --
including the delete coverage at the end of the file.

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

* Announce the delete error banner to assistive tech

The banner was mounted together with its message, and an alert inserted
into the DOM at the same moment as its text is not reliably announced --
so a screen-reader user got silence when a delete they had just
confirmed failed.

Keep the region mounted and swap only its content, matching the reason
attribute_external_source.tsx already keeps its own status region
mounted rather than rendering it alongside the announcement.

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

* [MM-69865] Allow deleting a plugin-owned attribute once its plugin is uninstalled

Delete was permanently disabled on plugin-owned rows, but the server only
protects such a field while its source plugin is still installed:
checkFieldDeleteAccess allows the delete once the plugin is gone, which is how
an admin cleans up what an uninstalled plugin left behind.

Reuse the User Attributes page's orphan check rather than growing a second one.
isFieldOrphaned moves out of system_properties/orphaned_fields_utils.ts into
utils/properties.ts, widened to PropertyField so both pages can use it, with the
redux-aware hook in components/common/hooks/use_field_orphaned.ts. The hook
unions admin.plugins and admin.pluginStatuses because the two attribute pages
populate different slices, and a field should not read as orphaned merely
because the page rendering it loaded only one of them.

The delete confirmation now names the uninstalled plugin, since an admin has no
other way to tell where the leftover attribute came from.

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

* Fix the delete-error scroll and the premature orphan check

The error banner sits above the table, so a delete confirmed from a row
further down left the failure off-screen. The existing scroll fired on the
error state, which lands during the modal's fade-out: GenericModal passes
restoreFocus, so react-bootstrap returns focus to the row's actions button
on close, and focusing an off-screen element scrolls it back into view --
undoing the scroll every time.

GenericModal also starts closing before it invokes handleConfirm, so the
delete response can land either side of the fade and the error and the exit
arrive in either order. Scroll only once both have landed, take focus on the
banner without its own scroll, then scroll the console wrapper to the top.
Focusing the banner also leaves keyboard and screen reader users at the
error rather than back on a row button.

Separately, gate the orphan check on the plugin inventory having settled.
Both admin.plugins and admin.pluginStatuses start empty, and the fetch is
dispatched from an effect, so an inventory that has not loaded is
indistinguishable from one where nothing is installed -- which isFieldOrphaned
reads as "every plugin-owned field is orphaned". A protected row briefly
offered Delete behind a dialog wrongly claiming its plugin was uninstalled,
which the server would then refuse anyway. Settled rather than resolved: a
failed fetch still leaves the inventory as good as it will get, and staying
false forever would strand genuine leftovers as undeletable.

The same gap exists for the system_properties consumers of the hook, which
never fetch the inventory at all; left alone here and noted on the hook.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 09:58:38 -07:00
cursor[bot] 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
cursor[bot] 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 Martin 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
Joram Wilander 6d78e8d551 Keep a collapse toggle for single video attachments (#38012)
MediaGallery hid its header for single tiles, so a collapsed video
disappeared with no way to expand it.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-08-17 19:45:09 -04:00
Devin Binnie d989f802d5 Disable TTL/grace period editing for server-derived attributes (#38001)
* Disable TTL/grace period editing for server-derived attributes

* FIx lint

* Fix bug with dot menu
2026-08-17 16:35:59 +00:00
cursor[bot] fd62fe4faa Allow granting delegated administration roles from the Manage Roles modal (#37202)
* Add delegated administration roles to Manage Roles modal

Allow admins to grant custom (delegated granular administration) roles
directly from the System Console > Users > Manage Roles modal, instead of
only via Delegated Granular Administration.

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

* Add tests for delegated administration roles in Manage Roles modal

Cover rendering, pre-checking, availability filtering, bot handling,
merge/remove on save, ordering, admin/member toggling, save errors,
modal close, and user-switch state reset.

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

* Fix lint in manage roles modal test

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

* Address PR feedback: link to system roles page and refine delegated roles spacing

- Replace per-role subtext with a single help-text link to the System Roles
  page, since customized roles can differ from the default descriptions
- Add padding below the radio buttons above the divider
- Left-align the delegated roles section with the section above and add
  padding below the divider

* Address PR feedback: license-gate delegated roles, refine help text, dedupe SCSS

* Assert delegated roles help text and link in modal tests

* Address PR feedback: gate delegated roles by Enterprise license (exclude Entry), hide for system admins, add Personal Access Tokens section heading

* Add e2e tests for delegated admin roles in Manage Roles modal

* Drop unused radio locator from ManageRolesModal page object

---------

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-17 14:10:26 +02:00
cursor[bot] 3f02a0a1ac [MM-70283] Fix mixed custom-section fallback hiding valid plugin settings (#38004)
* [MM-70283] Fix mixed custom-section fallback hiding valid fallback settings

When a disabled plugin defined multiple custom settings sections and only
some specified fallback: true, the settings page collapsed every section
into a single "enable the plugin" warning, hiding the fallback-enabled
sections that should remain configurable.

The collapse now only happens when no custom section allows a fallback.

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

* [MM-70283] Add test for mixed custom-section fallback

Covers a disabled plugin with mixed fallback sections: the fallback-enabled
section stays configurable, the non-fallback section shows a per-section
warning, and the single collapse warning is not rendered.

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

* [MM-70283] Improve mixed custom-section fallback tests

Rename the visibility test to match its assertions, add an
order-independent case, and assert non-fallback section settings are
suppressed.

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

* [MM-70283] Tighten fallback comment wording

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-17 14:07:37 +02:00
Ben Schumacher 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 Schumacher 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
Ben Schumacher 505d6c1ecf Add copy buttons for generated access tokens (#37494)
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-17 08:21:22 +02:00
cursor[bot] a012af768d [MM-70246] Fix squished author avatar in Content Flagging RHS post preview card (#37972)
* Fix squished author avatar in content flagging RHS post preview card

The author avatar in the post message preview collapsed into a vertical
sliver when the preview was rendered inside a constrained flex container
(the Data Spillage / Content Flagging RHS report card). The avatar column
shares the shrinkable .col__name flex rule (flex: 0 auto; min-width: 0),
so it collapsed and dragged the min-width:0 avatar image down with it.
Pin the avatar column (the .col__name containing .post__img) to its
natural size so the round avatar keeps its correct dimensions.

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

* Pin content flagging RHS preview avatar size to prevent squish

The shared avatar rules (img.Avatar { min-width: 0 }) let the profile
image shrink inside constrained flex layouts, so the round avatar in the
content flagging RHS post preview card rendered as a squished sliver.

Pin the preview avatar image to its intended 24px size (min-width +
flex-shrink: 0) and keep its column from shrinking so it always stays
round regardless of available width.

* Remove redundant author-column flex rule in post header

The preview-specific fix (min-width/flex-shrink on the avatar image plus
flex: 0 0 auto on .post__img) already keeps the avatar round in constrained
flex layouts, so the broader .col__name:has(.post__img) rule is no longer
needed. Removing it also keeps the author text column shrinkable.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-08-17 09:45:40 +05:30
cursor[bot] 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
cursor[bot] 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
Julien Tant 338dc6c74d [MM-69863] Add external source picker (AD/LDAP, SAML) to New attribute (#37845)
* [MM-69863] Add external source picker (AD/LDAP, SAML) to New attribute

Adds a "Link to external source" control to the Definition card, letting
an admin link AD/LDAP and/or SAML to a new attribute (both may be linked
at once, matching Custom Profile Attributes' own dot-menu behavior).
Each linked source renders as its own removable/editable chip; the "add"
trigger's menu only offers not-yet-linked sources and disappears once
both are linked. Reuses the existing AttributeModal component and
ModalIdentifiers unmodified -- no new modal code.

No server-side changes: the access_control property group's existing
AccessControlAttributeValidationHook already validates, persists, and
strips attrs.ldap/attrs.saml for non-Text fields, and the Manage
Attributes list already renders a linked source's icon/label. The
client mirrors that server behavior by forcing the field's type to Text
whenever a link is set, and clearing any links if the type is manually
switched away from Text.

* Cap the Attribute column's width at 65ch on the Manage Attributes list

The table uses table-layout: auto, so this needs to live on the td/th
itself (mirroring the existing td.actions/th.actions rule) rather than
on the inner name span alone -- the column's rendered width is decided
by the auto-layout algorithm, not by max-width on a cell's content.

* Style the "Link to external source" trigger as a quaternary button

Adds the design's separator (margin-top/padding-top: 12px, border-top)
above the trigger, and swaps the ad-hoc link styling for
buttonClassNames({emphasis: 'quaternary'}) -- transparent/no-border at
rest, matching the Cancel button elsewhere on this page -- with the
hover tint overridden to --link-color-rgb per design, since the
quaternary default uses --button-bg-rgb instead.

* Fix trigger button sizing, extract a real Divider, reorder chips, and show the linked value

- The trigger was stretching full-width because display:flex (block-level)
  fought .btn's own display:inline-flex, and its flex-column parent
  defaults every child to align-self: stretch regardless of which display
  wins -- fixed with align-self: flex-start and dropping the display
  override entirely.
- The border-top/margin-top/padding-top separator was living on the
  trigger button itself, so it only ever spanned the button's own
  (now inline) width, not the full row. Extracted a small reusable
  Divider component (webapp/channels/src/components/divider/) -- a
  theme-CSS-var-based hairline <hr>, unlike Menu.Separator/MUI Divider
  which needs a CompassDesignProvider ancestor -- and render it as its
  own full-width element instead.
- Chips now render below the trigger, not above, and show the actual
  linked value ("AD/LDAP: department"), not just the source name.

* [MM-69863] Add e2e coverage for dual-linking, chip edit/remove, and Type-switch clearing

Covers linking both AD/LDAP and SAML (menu exclusion, trigger disappearance,
chip label format), editing/removing a chip without a modal, the
type-mismatch warning, and the status announcement when Type is switched
away from Text.

* [MM-69863] Show "AD/LDAP, SAML" when an attribute is linked to both sources

getSourceKind previously collapsed a dual-linked field's Source cell to
just "AD/LDAP" since ldap was checked before saml. Add a dedicated
ldap_and_saml kind checked ahead of the individual branches, with its
own label and icon, and update the dual-linking e2e test's stale
assertion/comment to match.

* Fix prettier formatting in the dual-linking e2e spec

Pre-existing lines exceeded the project's 120-char printWidth;
prettier --write wraps the long test titles and toHaveText
assertions onto multiple lines with no behavior change.

* Gate AttributeExternalSource on saving/disabled, add chip action focus ring

A delegated (non-sysadmin) admin could still open the link modal and
mutate ldapAttr/samlAttr while every other control on the page was
disabled, and got stuck with an unclearable unsaved-changes prompt.
Thread disabled into the trigger and both chip action buttons.

Also add a :focus-visible style to the chip edit/remove buttons -- the
app's global *:focus{outline:none} reset left keyboard users with no
visible focus indicator on them.

* Clear stale focus-visible on the external source trigger

After linking a source, the modal restores focus to the trigger
programmatically, and because that restore comes from a keyboard-focused
input the trigger inherits :focus-visible. A later mouse click does not
clear it -- an already-focused element fires no new focus event -- so the
item MUI auto-focuses when the menu reopens inherits it in turn and gets
painted with the keyboard focus ring, on a menu opened with the mouse.

Blur the trigger on mousedown when it is already focused and matches
:focus-visible; the click's own default focus then re-evaluates the
interaction as a pointer one. Keyboard opens never fire mousedown, so
they keep their focus ring, and the first item still receives focus
either way so arrow-key navigation is unchanged.

Menu gets an opt-in onMouseDown passthrough on menuButton so the fix
stays scoped to this menu rather than changing every menu in the app.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 09:20:40 -07: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
Ben Schumacher 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
Devin Binnie 3b99b6c9d9 Add session attributes settings to System Console (#37973)
* Add session attributes settings to System Console

* PR feedback, lint fix

* Hide when session attributes are not enabled

* Disable if ABAC is disabled
2026-08-14 15:04:17 +00: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
Edgar Bellot Micó 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 Binnie 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
Jesse Hallam 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
cursor[bot] d18f3dd278 [MM-69748] Add Hide Archived toggle to the Browse Channels modal (#37500)
* [MM-69748] Add Hide Archived toggle to Browse Channels modal

Archived channels previously appeared mixed in with active channels in
the Browse Channels modal under the default "All channel types" filter
(most visibly in search results), with no way to filter them out.

Add a "Hide Archived" checkbox next to "Hide Joined" that hides archived
channels by default. The toggle is backed by a persisted preference and
is not shown when the explicit "Archived channels" filter is selected.

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

* [MM-69748] Add tests for Hide Archived toggle in Browse Channels

Cover the default-hidden behavior, toggling to show archived channels,
the explicit Archived filter override, preference persistence, and the
checkbox being hidden on the Archived filter.

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

* [MM-69748] Strengthen Hide Archived tests per review

Add non-search browse-list coverage for getActiveChannels in both
directions, assert only archived rows are filtered from search, and
query the checkbox by accessible label.

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

* Fix archived channel leak/duplication in Browse Channels filters

- Exclude archived private channels from the private channels selector so
  they no longer bypass the Hide Archived toggle or duplicate with the
  archived list.
- Fold archived channels back into the Public/Private lists (by type) when
  the toggle is off, matching the All list behavior.
- Keep the search Public filter governed by the shared hide-archived block
  instead of hard-excluding archived public channels.
- Add regression tests for the mixed public/private archived scenario.

* Satisfy eslint style rules in browse channels changes

* Update Browse Channels E2E for Hide Archived default

Align Cypress search expectations and Playwright tab/aria
snapshots with the new Hide Archived checkbox (on by default).

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 10:39:53 +02:00
Bill Gardner 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ó 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