* chore: upgrade playwright@1.62 and its deps
* chore: add SyncJobIntervalSeconds and AttributeRefreshIntervalSeconds to policy details tests
* fix: remove MM_FEATUREFLAGS_MOVETHREADSENABLED
* remove MM_FEATUREFLAGS_ENABLEREMOTECLUSTERSERVICE
* fix update login spec to enable, remove deprecated tests and update to postgres:15
* Update documentation and enhance environment handling in test containers
- Added details about user attribute refresh intervals in ABAC documentation.
- Refactored environment resolution in Mattermost container to improve code clarity.
- Enhanced logging to include server environment summary while starting test containers.
- Implemented redaction for sensitive server environment variables in logs.
* Package only the locale catalogs from server/i18n
release.mk copied server/i18n wholesale into the distribution. Everything in
that directory today is a catalog, so it made no difference -- but the server
loader, mmgotool and the sync test all already filter the directory to *.json,
and packaging was the one consumer that did not.
Copy the catalogs explicitly so documentation and any other non-catalog file
placed alongside them stays out of the release bundle.
* Validate the language fallback in display settings
user_settings_display reads getLanguageInfo(userLocale).name without a guard,
and its container fell back to config.DefaultClientLocale without checking
that value resolves. Nothing in the webapp guaranteed it did.
In practice it does today: fixInvalidLocales runs on every config load and
resets DefaultClientLocale to en when it is not in the supported set, so the
dereference cannot currently throw. That invariant lives three layers away in
Go, is silent when it fires, and is one refactor from not holding -- and the
cost of not depending on it is a two-line fallback.
So the container now re-checks the default and drops to
General.DEFAULT_LOCALE, matching what getCurrentLocale already does when the
current locale is unavailable.
Adds index.test.tsx covering the container's locale resolution: a supported
user locale is kept, an unsupported one falls back to DefaultClientLocale, and
an unsupported DefaultClientLocale -- or one excluded by AvailableLocales --
still resolves to something getLanguageInfo can look up. Two of the four fail
without the change.
* Allow getPostInfo join metadata when compliance is enabled
GET /posts/{id}/info was treating compliance as a content-read check
and returning 404 for public-channel non-members, so permalink clicks
never reached joinChannel. Return join metadata regardless of
compliance; content APIs and permalink previews stay gated.
Co-authored-by: maria.nunez <maria.nunez@mattermost.com>
* Move permalink preview sanitizer tests next to embed coverage
Keep compliance on/off permalink embed assertions in
TestSanitizePostMetadataForUser instead of the channel-mentions suite.
Co-authored-by: maria.nunez <maria.nunez@mattermost.com>
* Add guest GetPostInfo regression coverage with compliance enabled
Keep guests denied for public-channel join metadata when they are not
channel members, including after compliance is turned on.
Co-authored-by: maria.nunez <maria.nunez@mattermost.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: maria.nunez <maria.nunez@mattermost.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* Fix 'This actions requires' typo in the export error string
app.export.generate_presigned_url.config.app_error read 'This actions
requires the use of a dedicated export store.' Surfaced while writing
translator descriptions for the keys the master merge brought in.
Fixing it now rather than later: the id is new enough that no locale has
translated it yet, so there is nothing to re-translate and no locale carries
the mistake forward.
* Fix five more English source typos found while writing descriptions
Tracing all 295 new keys to their usage surfaced eight typos in user-facing
English, not the one already fixed. Five are corrected here:
remote users are not allow to log in -> not allowed
Missing require user property -> Missing required
Unable to get know users -> known users
Please check you provider's docs (x8) -> your provider's
Seperate multiple domains -> Separate
All five preserve meaning, so the translations that already exist for these
keys stay correct and nothing needs re-translating. The server strings live in
i18n/en.json, which is hand-maintained; the webapp ones are defaultMessage
values in source, so en.json and the authoring catalog are regenerated.
Deliberately NOT fixed: audit_table.userRemoved reads 'Removed {username} to
the {channelName} channel'. That is not a typo but a wrong preposition -- it
says the opposite of what it means -- and correcting it invalidates the
existing translation in all 21 locales. It wants its own change.
Also drops the 'source contains a typo' notes from the descriptions of the
keys that are no longer wrong.
* Fix reversed preposition in the audit log's user-removed row
audit_table.userRemoved read 'Removed {username} to the {channelName} channel'
-- it described a removal as an addition, right next to audit_table.userAdded
which uses the same wording with 'to'. In an audit log, a row that states the
opposite of what happened is worse than a typo.
Nothing needs re-translating. All 20 non-English locales already say 'from'
(von, du canal, から, из, khỏi): every translator silently corrected the English
rather than reproducing it, so this makes the English match translations that
were already right. The single exception is en-AU, which is a deliberate copy
of the source and therefore inherited the bug; it is corrected here too.
I previously flagged this as needing 21 re-translations. That was wrong -- I
had not checked what the catalogs actually said.
* Add OnLicenseChanged plugin hook
Notify plugins when the server license changes via SetLicense.
Returning an error does not roll back the license change, but
deactivates the plugin and marks it failed to stay running.
Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>
* Drop error return from OnLicenseChanged hook
Match OnCloudLimitsUpdated: fire-and-forget notification with no
plugin deactivation on failure.
Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>
* Set OnLicenseChanged minimum server version to 12.0
Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>
* Document OnLicenseChanged startup behavior in hook godoc
Clarify that the hook is not invoked for the initial license load and
that plugins should use API.GetLicense() from OnActivate instead.
Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>
* [GH-30481] Add negative caching for missing custom emoji names in LocalCacheEmojiStore
Names missing from the Emoji table were never cached, so every fetch of
a post containing emoji-like tokens (e.g. ":11:" inside timestamps) hit
the DB. Cache such names with a short TTL, invalidate on Save, and skip
names that exceed the maximum emoji name length.
* [GH-30481] Skip id-cache lookup for sentinel entries in getFromCacheByName
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
The shutdown error only said "Error shutting down main logger" with no
hint at the cause, which is confusing when the real reason is an
unreachable log target (e.g. a remote TCP endpoint) blocking the
flush. Point the operator at the connection error logs for the
affected target instead.
UpdatePropertyFields panics when passed a nil context because
RequestContextWithMaster dereferences it. Use SystemCallerContext
to match the other sub-tests in the same function.
* [MM-70402] Stop Channel Settings flagging untouched channels as edited
The Info tab seeds its form state from the raw channel record but diffs the
trimmed local value against the untrimmed stored value. Channel purpose and
header are persisted verbatim by the server, so any channel whose text has
surrounding whitespace (a header ending in a newline, for example) reports
unsaved changes as soon as the dialog opens, which then blocks tab switching
and closing the modal.
Compare both sides trimmed, matching what the form actually saves, and derive
the visible SaveChangesPanel and the parent's unsaved-changes flag from one
shared calculation so the two can no longer disagree.
Also read the channel name straight from props when validating on blur instead
of mirroring it into a ref that is only seeded by a passive effect, which could
report "Channel names must have at least 1 character." for a populated field.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-70402] Cover Channel Settings opening on untidy stored channel text
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-70402] Strengthen coverage for Channel Settings opening state
Add a Playwright spec that opens Channel Settings on a channel whose stored
purpose and header carry surrounding whitespace and asserts the save panel is
absent and the modal closes on a single click, plus a companion spec proving a
real edit is still detected and saved. Both need purposeInput and
saveChangesPanel locators on the info settings page object.
On the unit side, validate the rendered channel name rather than a rejected
keystroke, cover the DM header-only branch, pin the whitespace-only edit as a
no-op, and assert the form settles clean once the patched channel arrives.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-70402] Cover Reset and untouched-header preservation on untidy channels
Reset used to re-seed the raw stored text and immediately recompute as dirty,
leaving the panel stuck open, and saving one field used to rewrite every other
field to its trimmed form. Pin both.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-70402] Tighten comments and assertions in the Channel Settings fix
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-70402] Detect channel text changes on raw values, trim only on save
Compare the raw field values for change detection instead of trimming both
operands, so removing stored leading/trailing whitespace is a savable edit.
The form still opens cleanly on untidy stored text because each field is
seeded from the raw channel record, and the save payload is still trimmed.
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-70389] Add Android to the user_agent_platform session attribute
uasurfer has no Android platform, so Android devices were reported as
Linux and "Android" was missing from the user_agent_platform select
list, leaving no way to write a permission policy rule that matches
Android sessions.
Derive the platform name "Android" when the parsed OS is Android (or the
Mattermost Mobile user agent is not iOS) and add the matching option to
the seeded session attribute schema.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-70389] Cover the Android platform value end to end
Add a drift guard tying the platform names the server derives to the
options the user_agent_platform select offers, a session attribute test
proving an Android session stores "Android", and a migration test
proving a newly declared option reaches an already-seeded field without
regenerating the IDs of the options around it.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* [MM-70389] Trim comments and tighten the platform drift guard
Assert getPlatformName's own output against the schema options so a
platform name returned directly, rather than looked up in platformNames,
cannot drift out of the select either.
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>
The centered GenericModal layout makes `.modal-content` a flex item of
`.modal-dialog`. With the default `min-width: auto`, its automatic minimum
size grew to the min-content width of the widest `white-space: nowrap` plugin
description, so `.modal-content` overflowed the 832px dialog on pages holding
long descriptions and snapped back on pages without them.
Setting `min-width: 0` lets `.modal-content` shrink to the dialog width, so the
modal stays 832px on every page and long descriptions truncate with an ellipsis
inside the list as designed.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* MM-63635 Allow plugin RHS panels to open from the App Bar in the Threads view
The Threads view dispatches selectLhsItem(Page), which clears the current
channel. The App Bar click handler only invoked a plugin's action when both a
channel and a channel membership were present, unless the component carried an
rhsComponentId. Plugins registered through registerChannelHeaderButtonAction
(Copilot among them) have no rhsComponentId, so their App Bar icon did nothing
at all outside of a channel.
Invoke the action regardless of channel context, and widen the action types to
reflect that the App Bar can call them without a channel.
Also exempt plugin RHS panels from the suppression that the Threads view
applies on mount, so an open plugin panel survives the switch instead of being
force-closed and left in a state where the next App Bar click toggles it off.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* MM-63635 Add tests for opening plugin RHS panels from the Threads view
Covers the App Bar invoking a plugin action with and without a channel in
context, for plugins registered through both registerChannelHeaderButtonAction
and registerAppBarComponent, asserting the RHS actually opens in the store.
Also covers which RHS states the Threads view suppresses on mount.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* MM-63635 Strengthen the plugin RHS tests after review
Assert the plugin action's channel arguments explicitly, add the case where the
App Bar is clicked while the RHS is suppressed, cover the Threads view mount
effect clearing the current channel, and add an end-to-end case that clicks the
App Bar icon while the Threads view is mounted.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* MM-63635 Use a realistic App Bar icon fixture in the plugin component tests
registerAppBarComponent always supplies an iconUrl, so the fixture rendered
markup production never produces. Give it a URL and assert the active state on
the markup each registration style actually renders.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* MM-63635 Trim narration comments from the plugin RHS change
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>
Release notes, changelogs, ESR support pages, and the upgrade/deployment
guides tied to a release are maintained by the release teams, so route
review of those pages to @mattermost/release-eng or
@mattermost/release-managers rather than general docs reviewers.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Migrate property field reads to request context
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Preserve nil property service request context behavior
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Use explicit session attributes system context
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Use non-nil property contexts for internal calls
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Thread request context through content flagging lookups
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Update content flagging helper tests for request context
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Thread request context through content flagging values
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Fix build: remove unused context imports left after request.CTX migration
Migrating PropertyFieldStore to request.CTX removed the last
context.Context usage from the store.go interface, leaving an unused
"context" import in store.go and in the generated retrylayer/timerlayer
files (regenerated via `make store-layers`, with layer_generators now
stripping the context import when it's unused). Also drop the same
now-unused import in localcachelayer/main_test.go.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Remove context import directly from store layer templates
Store no longer has any context.Context methods after the request.CTX
migration, so drop the hardcoded "context" import from the
retry/timer layer templates instead of stripping it at generation
time.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Remove deprecated store.WithMaster/sqlstore.WithMaster helpers
request.CTX is now used everywhere, so the deprecated
context.Context-based WithMaster helpers and their wrapper in
sqlstore have no remaining callers; inline the logic into
RequestContextWithMaster instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Fix TestDoSetupSessionAttributesProperties nil-context panic
Two subtests still constructed the pre-seed field state via
UpdatePropertyFields(nil, ...), which was the old system-caller
sentinel. isSystemCaller now requires an explicit SystemCallerContext
marker, so a bare nil rctx falls through into validateUpdate and
panics in RequestContextWithMaster. Use SystemCallerContext(th.Context)
like the rest of the suite already does.
* ci trigger
* ci trigger
---------
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: Claude Sonnet 5 <noreply@anthropic.com>
* [MM-70290] Run app migrations locked to the master DB
App migrations write rows and then read them back within the same
function. Those read-backs resolve to GetReplica(), so on a licensed
server with read replicas configured a replica that has not yet caught
up returns zero rows and doAppMigrations aborts startup via mlog.Fatal.
Reported after a 10.11.12 -> 11.7.8 upgrade on Aurora PostgreSQL with a
reader endpoint, crashing on the Managed Category Properties Setup
migration. Restarting is not a reliable workaround: the done flag is
written before the failing read, so the short-circuit path re-runs the
same replica read and a node can crash-loop while lag persists.
Wrapping doAppMigrations in LockToMaster/UnlockFromMaster covers every
read in both migration loops, including SqlPropertyGroupStore.Get and
SearchPropertyFields, which take no context and so cannot be fixed by
per-call-site routing. This mirrors the existing bulk import fix in
app/import.go, which locks to master for the same reason while the
server is serving live traffic.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Expect LockToMaster in the setup-functions store mock
doAppMigrations now locks the store to master, and it runs from
NewServer, so every helper that builds a server on the mock store hit
an unexpected-call panic. Registering both calls in
GetMockStoreForSetupFunctions covers the app, app/email, app/platform
and api4 helpers, which all share this mock.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* MM-69962: Fix inline media flickering between sizes near the 480px container threshold
* bring back row range
---------
Co-authored-by: Nevyana Angelova <nevyangelova@Nevy-Macbook-16-2025.local>
* docs: restore generated plugin SDK reference pages
Reimplements the Hugo-era plugingodocs/pluginjsdocs/pluginmanifestdocs
shortcode pipeline natively in Docusaurus, so the server plugin SDK,
web app plugin SDK, and manifest reference pages render full generated
content again instead of "Generated content (migrating)" placeholders.
Two new Go generators (gen-plugin-godocs, gen-plugin-manifest-docs) and
one Node generator (gen-plugin-jsdocs.mjs) read server/public/plugin,
server/public/model, and webapp/channels/src/plugins/registry.ts
directly from this monorepo and emit gitignored JSON consumed by new
PluginGoDocs/PluginGoExample/PluginJsDocs/PluginManifestDocs React
components, wired into prestart/prebuild alongside the existing
sidebar/OpenAPI generators.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: remove migration plan doc from this PR
Was a handoff/status doc for the implementing agent, not meant to ship
as part of the change itself.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: make plugin-godocs/manifest-docs builds atomic
build:plugin-godocs and build:plugin-manifest-docs redirected straight
into data/plugin-*.json, which doesn't exist on a clean checkout (the
redirection itself would fail before the generator ever ran) and, on a
subsequent failed run, would truncate a previously-good JSON file
before failing. Create data/ up front, write to a .tmp file, and only
mv it into place once the generator exits successfully.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: fix review findings in plugin doc generators
gen-plugin-manifest-docs:
- jsonFieldName now excludes unexported fields (matching encoding/json)
and falls back to the Go field name when there's no tag or the tag's
name component is empty (e.g. json:",omitempty"), instead of
dropping the field entirely.
- exprTypeDocs's SelectorExpr case now resolves a qualified type only
when its package qualifier actually points at the model package
(checked against the declaring file's imports), instead of matching
any x.Sel identifier by name alone regardless of which package it
qualifies.
gen-plugin-godocs:
- The example-code loop now falls back to example.Code when
example.Play is nil (go/doc leaves Play nil when it can't synthesize
a whole runnable program), and propagates printer.Fprint errors
instead of discarding them.
No output change for the current server/public/plugin or
server/public/model content — verified via a full regen.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: drop legacy-repo comment references, align jsdocs generator parser
Remove "port of the old mattermost-developer-documentation Hugo
shortcode" comments from the plugin doc generators/components now that
they're fully native to this monorepo, and switch gen-plugin-jsdocs.mjs
from the TypeScript compiler API to @typescript-eslint/typescript-estree
to match the old script's parser/shape, while keeping the more robust
reArg-declared-parameter-name and node-scoped comment-attachment logic.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: bump plugin doc generators' go.mod to go 1.26
go 1.23 was arbitrarily low; align with a current Go version. go run
auto-downloads a matching toolchain on older local installs, so this
doesn't reintroduce a dependency on server/public/go.mod's version.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: handle RestElement in gen-plugin-jsdocs parameter extraction
paramNamesFromPattern silently dropped rest parameters/destructured rest
members (e.g. (a, ...rest) or {a, ...rest}), since RestElement matched
none of its type checks. No effect on today's registry.ts output (no
rest patterns currently used there), but keeps the extraction correct if
one is ever introduced.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: simplify plugin doc generators README note
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: check stdout write errors in plugin doc generators
os.Stdout.Write's return errors were discarded, so a failed/partial
write (e.g. broken pipe, disk full) would still exit 0. Combined with
the npm scripts' > file.tmp && mv pattern, a truncated write could be
treated as a successful generation. Fatal on either write failing.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: flatten Server/Webapp plugin SDK reference sidebar nesting
server/server-reference.md and webapp/webapp-reference.md were the only
file in their respective folders, so the sidebar generator (which only
collapses a folder into a single entry when it has an index.md) rendered
them as a "Server" > "Server plugin SDK reference" category with one
child instead of one flat entry, unlike every sibling reference folder
(rest-api/index.md, bot-accounts/index.md, etc.).
Rename both to index.md to match that convention, and update the ~20
cross-referencing links (many with #anchor fragments) that pointed at
the old /reference/server/server-reference and /reference/webapp/webapp-
reference paths.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix Avatar layout regressions from MM-69802 without font-size:0
Broken images are no longer replaced elements, so width/height stop
applying and alt text sizes the box. Use inline-block plus overflow and
transparent color so the existing size tokens still apply and alt text
cannot spill out of the circle.
This restores em margins (scheduled/drafts DM avatars) and flex min-width
(agent selector, post previews), and removes the Content Flagging
preview spot-patch from #37972.
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
* Trigger Enterprise CI after mostest_password fix
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* Bump Go version to v1.26.7
* Fix digest
* Update glibc to v16 in runtime image as well
* Trigger E2E tests
* MM-70307: Fix FIPS E2E test failure due to short PostgreSQL password
OpenSSL FIPS requires HMAC keys to be at least 14 bytes (112 bits). The
password 'mostest' (6 bytes) triggers a panic in lib/pq's SCRAM-SHA-256
authentication when the server runs under go-msft-fips with the
glibc-openssl-fips:16 image. Replace it with 'mostest_password' (16
bytes) in all E2E test PostgreSQL connection strings and container
configs. LDAP admin passwords are unaffected (different protocol).
* Revert "MM-70307: Fix FIPS E2E test failure due to short PostgreSQL password"
This reverts commit d31b0eb5e8.
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
* Change test password to mostest_password
This makes the password compliant with the 112 bits minimum length
requirement. Otherwise, FIPS-compliant OpenSSL implementations will
panic when trying to connect from `lib/pq` with a shorter password.
* Simplify test templates' POSTGRES_PASSWORD values
* make generated
* Modify missing "mostest" strings
* MM-69323 Enable Concurrent React
* Fix Cypress tests involving post dot menu
* Fix flaky Cypress tests involving Team Settings modal
* Fix setState in permissions code when called twice in rapid succession by tests
* Update fullname_spec.js to wait for suggestion list to close
This is needed because the setState in SuggestionBox.clear isn't processed until
after the Enter keypress registers. Alternatively, we could wrap that in flushSync,
but since this seems to only occur during testing, I decided not to change
the web app code.
* Update more E2E tests
* Fix accidental commented test code
* Switch useContainerDimensions to useLayoutEffect to fix newly introduced layout shift
* Skip post_height SVG test on all browsers and message attachment test on Firefox
* Bump changes to feature flag for a future PR
* Enable concurrent React in E2E tests
* [MM-69866] Add Applies-to resource picker (Users, Channels, Posts) to New attribute
Adds an "Applies to" Card to the Global Attributes "New attribute" page,
letting a sysadmin pick which resources (Users, Channels, Posts) an
attribute applies to before saving. Each selected resource becomes its
own linked PropertyField, created serially after the template so a
partial failure can be attributed to a specific resource and rolled
back deterministically (linked fields deleted before the template,
per the server's deletion-order protection).
A `user`-scoped linked field shares its namespace with Custom Profile
Attributes, so name conflicts and the shared 20-field cap are newly
reachable failure modes -- both get distinct, actionable banners
instead of a generic failure message.
Client-only change; no server, migration, or Client4 changes required.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Split the Applies-to row into one component per resource type
Replaces the single resourceType-parameterized AttributeAppliesToItem
with three dedicated components (User/Channel/Post), each hardcoding
its own icon, label, and testids instead of looking them up by a prop.
All three share one prop signature (AttributeAppliesToItemProps,
exported from attribute_applies_to_constants.tsx) so the parent's
Record<ResourceObjectType, ComponentType<...>> lookup map fails to
compile if any of the three drift from it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Fix Prettier formatting in the global_attributes E2E files
npm run check for e2e-tests/playwright runs lint && prettier && tsc,
and CI's prettier --check step was failing on both files -- they'd
only been checked against webapp's ESLint config locally, not this
package's Prettier config.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Move the Applies-to row's Remove action into the expanded state
The collapsed row no longer has any remove affordance -- the only way
to remove a resource is to expand it first, then click "Remove
resource" in the header (still a sibling of the toggle, not nested
inside its clickable area). Styled per the design prototype: a plain
text button, transparent by default, --error-text colored, with a
rgba(--error-text-color-rgb, 0.08) hover tint -- the same pattern
already used by OrphanedFieldDeleteButton (system_properties) rather
than a new one-off style.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Add 12px right margin to the Applies-to row's Remove button
Matches the design prototype's spacing between the button and the
row's right edge.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Rename applies-to constants file to .ts
The file has no JSX, so the .tsx extension was misleading.
* Polish the Applies-to card empty state and expanded rows
Match the design: updated copy and type, primary/tertiary add buttons, header-only open tint, and a 176px form row in the expanded body.
* Drop bottom padding on the last Applies-to body row
It stacked with the container padding and looked doubled.
* Size Unique name label, value, and Edit to 12px
The caption and Edit link were 14px against a 12px label; shrink the edit input to match.
* Treat Unique name click-away as Done
Opening Edit then clicking away left the input open and froze auto-derivation. Blur now uses the same commit path as Done, matching the channel URL field.
* Lock Type to Text while an external source is linked
LDAP and SAML only sync on text fields; disabling the type menu until the last chip is removed keeps that invariant in the UI.
* Show linked sources on the Options line as Synced with chips
Once a source is selected the chips replace the Text help copy; the divider stays until then so the unlinked state still separates Options from the add-source trigger.
* Order flex container properties to satisfy stylelint.
* Show CPA banners only for Users and copy leftover-template rollback.
Channels and Posts name conflicts used User Attribute wording, and a leftover template after a linked-field rollback looked like a clean save. E2E cleanup now rediscovers template and linked fields instead of relying on the success-path list.
* Use Compass Button for Applies-to row chrome.
Override quaternary styling so the accordion header stays body-text chrome, and keep Remove as tertiary destructive.
* Await hanging creates before unmount-save negative asserts.
waitFor(() => Promise.resolve()) returns on the first check and can pass before finalizeSave, so the mount guard was not actually load-bearing.
* Drop unused PropertyField and ResourceObjectType imports from the Playwright spec.
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* 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>
* [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>
* 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>
* [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>
* [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>
* 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
* 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
* 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>
* [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>
* 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