Commit Graph
22437 Commits
Author SHA1 Message Date
Daniel Espino García 870c2c6b13 Add root id to webhooks (#36415)
* Add root id to webhooks

* Address feedback

* Address coderabbit comment

* Error if root post is not really a root post

* Fix test

* Address nitpick
2026-05-07 14:09:32 +02:00
Jesse Hallam c1107620bc docs: import server/AGENTS.md from platform/AGENTS.md to ensure eager loading (#36400)
* docs: import server/AGENTS.md from platform/AGENTS.md to ensure eager loading

* docs: use sentence format for AGENTS.md import comment
2026-05-07 03:40:21 -07:00
Harrison Healey 10ad2505a7 Split out buttonClassNames utility and use for most places Button isn't (#36328)
* Split out buttonClassNames utility and use for most places Button isn't

* Update StartTrialBtn to use buttonClassNames

Ideally, we'd:
1. Use Button, but that requires sorting out the one case that overrides
   btnClass entirely.
2. Use a button for all of these since none of these should have a link
   role, but that's outside of the scope of this ticket.

* Update another new button to use Button
2026-05-06 17:35:10 -04:00
Harrison Healey c1ddd77481 MM-68654 Add Button to the Component Library (#36412)
* Add Button to Component Library

* Fix key warnings in Component Library

* Add grid of Buttons to match Figma

* Fix trailingIcon default
2026-05-06 19:36:18 +00:00
Jesse Hallam 7f161bb24c Lower default test console log level from stdlog to debug (#36455)
* Lower default test console log level from stdlog to debug

Suppresses trace-level log spam (e.g. MlvlNotificationTrace) that
flooded CI output. MM_LOGSETTINGS_CONSOLELEVEL still overrides for
local debugging.

* Fix TestEnvironmentVariableHandling to expect debug default

Updates the assertion to match the new default console log level.
2026-05-06 16:35:40 -03:00
3b86b9e14a refactor(color_input): migrate ColorInput to a function component (#33363)
* feat(color_input): migrate ColorInput to function component

* test(color_input): migrate tests from enzyme to react testing library

* test(color_input): remove obsolete snapshots

* test(color_input): update snapshots

* test(color_input): update test

* refactor(color_input): remove unnecessary state update in function body

* Revert "refactor(color_input): remove unnecessary state update in function body"

This reverts commit 2c7647a3e4.

* Fix ColorInput tests

* Simplify click outside handler

By changing the button to always show the picker instead of toggling it
and making it so that the click outside handler checks for clicks
outside of the whole ColorInput, we can get rid of the setTimeout and
the very specific timing needed for the click outside handler.

* Wrap handleColorChange in useCallback

* Update snapshot

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Harrison Healey <harrisonmhealey@gmail.com>
2026-05-06 14:45:20 -04:00
Harrison Healey e898ccdf3d MM-68397 Add shared package to STYLE_GUIDE.md (#36425)
* MM-68397 Add shared package to STYLE_GUIDE.md and CLAUDE.OPTIONAL.md

* Add webapp/AGENTS.md
2026-05-06 13:15:40 -04:00
6293e354c9 Translations update from Mattermost Weblate (#36404)
* Translated using Weblate (Polish)

Currently translated at 100.0% (3191 of 3191 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pl/

* Translated using Weblate (Polish)

Currently translated at 100.0% (7243 of 7243 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/

* Translated using Weblate (Turkish)

Currently translated at 91.5% (6634 of 7243 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/tr/

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 99.9% (7239 of 7243 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/zh_Hans/

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (3191 of 3191 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/zh_Hans/

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (7243 of 7243 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/zh_Hans/

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (7243 of 7243 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/zh_Hans/

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (3191 of 3191 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/zh_Hans/

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (7243 of 7243 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/zh_Hans/

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (3191 of 3191 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/zh_Hans/

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (7243 of 7243 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/zh_Hans/

* Translated using Weblate (Turkish)

Currently translated at 93.3% (6763 of 7243 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/tr/

* Translated using Weblate (Polish)

Currently translated at 100.0% (7243 of 7243 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/

* Translated using Weblate (Polish)

Currently translated at 100.0% (7243 of 7243 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/

* Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/

* Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/

* Translated using Weblate (Norwegian Bokmål)

Currently translated at 76.9% (5617 of 7303 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/nb_NO/

---------

Co-authored-by: master7 <marcin.karkosz@rajska.info>
Co-authored-by: Kaya Zeren <kayazeren@gmail.com>
Co-authored-by: Sharuru <mave@foxmail.com>
Co-authored-by: ThrRip <coding@thrrip.space>
Co-authored-by: Frank Paul Silye <frankps@gmail.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-05-06 15:35:52 +00:00
Harrison Healey 076beaff52 Update interdependency between packages to 11.8.0 (#36449)
* Update interdependency between packages to 11.8.0

* Fix bad merge upstream
2026-05-06 10:46:10 -04:00
Harrison Healey 34ef034dd9 MM-68261 Add Button component to shared package (#36191)
* Add initial version of Button

* Use Button in ConfirmModal

* Use Button in easy places that use className='btn btn-primary'

This is everywhere that I could just replace `<button className='btn
btn-primary'>` with `<Button emphasis='primary'>` (and some other
emphasis versions) without any additional changes. There's still more
places where this could be used which require more in-depth changes that
will be in a following commit.

* Use Button in place of divs with className='btn btn-primary'

This is a minor functional change because these elements are now
accessible.

* Use Button in SpinnerButton

This is removing some usage of a save-button CSS class
that doesn't seem to affect these components.

* Replace RB Button with our Button

There's a small functional change here because the copy button in the
header of the FullLogEventModal is now styled when it wasn't before.

* Use Button in many places which used btn-secondary, btn-tertiary, and btn-danger

This removes some CSS classes from some different elements, but as
elsewhere, those CSS classes don't actually do anything. I think some
might have had a purpose once, but there seems to be quite a few that
were copied around during previous, possibly AI-assisted refactors.

* Use Button in many places in System Console

Notably, this includes:
1. Cleaning up some complicated logic in PurchaseLink/RenewalLink for
   determining their styling.
2. Making some minor functional changes in ChannelProfile/TeamProfile
   because they didn't use standard CSS classes previously. The styles
   mostly match a secondary button, but they had slightly different
   padding and colours previously.
3. I also removed a workaround for an old issue with OverlayTrigger and
   disabled buttons in favour of just using the disabled attribute. For
   more information on the previous code, see
   https://github.com/mattermost/mattermost-webapp/pull/10387. Based on
   some brief testing, that's no longer needed.

* Use Button in MultiSelect and remove unneeded backButtonClass prop

Everything that used that prop either passed the tertiary class that
was the default or passed a class that didn't exist.

* Use Button in more places that used btn-primary/secondary/tertiary/quaternary

* Use Button in more places that used btn-danger

* Use Button for all buttons with a className starting with 'btn btn-...'

* Migrate anchors that really should've been buttons to Buttons

All of these are anchors with click handlers and the btn class, so
they'd appear as buttons anyway.

* Migrate SettingItemMax and SettingPicture to Button

* Use Button in BrowseChannels

* Use Button in TourTip

* Migrate GenericModal to Button

There's a minor UX change due to the old `delete` class having a slightly
different colour from `btn-danger`, but I think that was from an older
version of the default themes.

Ideally, we'd remove the `GenericModal__button`, `confirm`, and `delete`
classes from the buttons on that modal, but doing that would require
changes to a large number of E2E tests that I'd rather not do now.

* Change order of building packages in postinstall

* Fix move_thread E2E tests

* Coderabbit feedback

* Address feedback

* Add JSDoc comments and remove width prop

I don't think we need this since this should be set by a parent with
`display: flex`, so I'm not going to add it to the Button.

* Share Button with plugins
2026-05-06 09:38:16 -04:00
Andre Vasconcelos a6ea65b44b Bumping prepackaged gitlab version to 1.12.2 (#36430) 2026-05-06 16:25:23 +03:00
Guillermo VayáandClaude Opus 4.7 ecf8a741ac Add unread badge to Recaps sidebar link (#36246)
* Add unread badge to Recaps sidebar link

Shows the count of unread finished recaps (completed or failed) on the
LHS Recaps link. Pending and processing recaps are excluded so the badge
only reflects work the user can actually read. When any unread recap has
failed, the badge is colored as an error to surface the failure.

The badge updates live through the existing recap_updated WebSocket
event, which refreshes the recap in the Redux store.

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

* Fix Recaps failed-badge color losing to active sidebar rule

The failed-badge modifier selector had the same specificity (0,4,0) as
`.channel-view .sidebar--left .active .badge` in _badge.scss, so when
the Recaps link was the active route the global mention background
color won on cascade order. Scope the rule with `#SidebarContainer` so
it wins on specificity (1 id + 4 classes) regardless of active state.

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

* Fix Recaps badge selector memoization

getUnreadFinishedRecapsBadge was keyed off getAllRecaps, which is not
memoized and returns a new array on every call. That broke reselect's
reference-equality input check, so the selector recomputed and returned
a fresh {count, hasFailed} object on every store dispatch — forcing
RecapsLink (always mounted when the feature flag is on) to re-render
on every action. Key the selector off state.entities.recaps directly
and iterate ids in the result function so memoization holds when the
recaps slice is unchanged.

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

* Address PR feedback on Recaps sidebar badge

- Pass shallowEqual to the useSelector consuming
  getUnreadFinishedRecapsBadge. The selector returns a plain
  {count, hasFailed} object, so recap updates that change a recap
  but leave the badge values the same (e.g. marking a read recap)
  would otherwise force RecapsLink to re-render.
- Scope the "no badge" negative assertion to the render container so
  it only asserts on the badge element, not any '1' or '.badge'
  elsewhere in the DOM.

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

* Address UX feedback on Recaps sidebar badge

- Add `unread` class to the sidebar item and `unread-title` to the
  link when there are unread recaps so the label goes bold and the
  icon goes full-opacity, matching how channels and the threads link
  indicate unread state.
- Keep the badge (and the new failed icon) visible on hover so it
  doesn't disappear under the cursor -- same override the threads
  link uses.
- Replace the red failed-badge modifier with an amber alert icon
  rendered in place of the count badge when any unread recap has
  failed. Red mention badges are reserved for urgent priority
  messages and caused confusion here.

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

* Keep Recaps badge in place on hover

The global sidebar hover rule shrinks padding-right from 16px to 5px
to make room for the per-channel menu button, which shifted the badge
right since it stays visible. Restore padding-right: 16px on hover for
the Recaps link, matching what the threads link already does.

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

* Align Recaps failed-icon aria-label with tooltip

The aria-label on the .RecapsFailedIcon span was a hardcoded English
string ("Recap failed") that differed from the tooltip shown to
sighted users ("One or more recaps failed"). Derive the aria-label
from the same intl message used by the tooltip so screen readers and
sighted users get the same wording and the label is localized.

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

* Stop Recaps link from overriding global unread label styling

The combined `.active .SidebarLink, .SidebarLink.unread-title` rule
pushed font-weight: 400 onto .SidebarChannelLinkLabel with specificity
(0,4,0), overriding the global `.SidebarChannel.unread` rule that sets
font-weight: 600 and --sidebar-unread-text at (0,3,0). As a result the
Recaps label rendered at normal weight when unread, inconsistent with
channels and the threads link. Split the rules: keep the active-state
overrides as they were, and limit the unread-title rule to the
icon-specific styling Recaps actually needs, letting the global unread
styling apply to the label.

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

* Add i18n entry for Recaps failed-tooltip

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

* change size of alert icon

* fix the right icon

* Add ViewedAt to recaps and POST /recaps/mark_viewed endpoint

Introduce a new ViewedAt field on Recap, separate from ReadAt, that
tracks whether the user has at least seen a finished recap on the
recaps page. ReadAt keeps its existing per-recap "Mark read" semantics.

- New Postgres migration 000172 adds the ViewedAt column (default 0)
  and an idx_recaps_user_id_viewed_at index mirroring the existing
  ReadAt index.
- New store method MarkRecapsAsViewed(userId, statuses) does a single
  UPDATE ... WHERE ViewedAt = 0 AND Status IN (...) RETURNING Id so
  the app layer can fan out one WS event per affected recap.
- New App.MarkRecapsAsViewed(rctx) marks the user's not-yet-viewed
  completed/failed recaps and broadcasts WebsocketEventRecapUpdated
  per affected id.
- New POST /recaps/mark_viewed handler. Registered before the
  {recap_id} regex routes so mark_viewed isn't captured as an id.
- RegenerateRecap now resets ViewedAt = 0 so a regenerated recap is
  surfaced again in the badge once it completes. As a related fix,
  UpdateRecap now persists ReadAt and ViewedAt -- previously it
  silently dropped the ReadAt = 0 reset that RegenerateRecap was
  setting in memory.

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

* Mark recaps as viewed when the recaps page mounts

Wire the new server endpoint into the webapp:

- Recap type now includes viewed_at: number.
- Client4.markRecapsAsViewed posts to /recaps/mark_viewed.
- New markRecapsAsViewed redux action, fired alongside getRecaps and
  getAgents in the recaps page mount effect. The server broadcasts
  recap_updated per affected recap so other tabs/devices receive the
  update through the existing handleRecapUpdated WS handler -- no new
  client-side handler needed.
- getUnreadFinishedRecapsBadge now filters on viewed_at === 0 instead
  of read_at === 0, so the sidebar badge clears on page open instead
  of requiring per-recap "Mark read" clicks. Selector tests updated to
  match.

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

* Address review feedback on Recaps viewed_at change

- Defer markRecapsAsViewed until after getRecaps resolves on the
  recaps page mount. Previously they ran in parallel, so getRecaps
  could land last and overwrite the viewed_at: <now> timestamps the
  WS-driven refresh had just written, briefly re-showing the badge.
- Switch the markRecapsAsViewed audit log to LevelContent and record
  the affected ids as result state, matching the pattern of every
  other mutating recap handler (markRecapAsRead, deleteRecap, etc).
  recap_count meta is now recorded unconditionally.
- Add an app-layer test that asserts MarkRecapsAsViewed publishes a
  recap_updated websocket event for each affected recap. The fan-out
  is the entire reason this lives in the app layer, so a regression
  removing the publish loop should fail loudly.
- Add a store-layer regression test that UpdateRecap actually
  persists ReadAt = 0 / ViewedAt = 0 resets, guarding the regenerate
  flow against a future change that drops those columns from the
  update map.

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

* Update migrations.list for 000172_add_recaps_viewed_at

Regenerated via `make migrations-extract` so the autogenerated
sequence list includes the new recaps ViewedAt migration files.

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

* Use AddMeta for Recaps mark_viewed audit ids

AddEventResultState takes a model.Auditable, not a plain map[string]any,
so the previous attempt to record the affected ids did not compile.
Record them as audit metadata instead, matching the pattern used by
getRecaps which similarly returns a slice and uses AddMeta only.

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

* Split Recaps ViewedAt index into a CONCURRENTLY migration

The lint check rejects bare CREATE/DROP INDEX in migrations because
they take an ACCESS EXCLUSIVE lock and block DML. Split the index off
into 000173 with CONCURRENTLY + the morph:nontransactional directive,
matching the pattern used by 000168/000169 (LinkedFieldID column +
its index). 000172 keeps just the ALTER TABLE ADD COLUMN, which can
stay transactional.

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

* Add viewed_at to existing Recap test fixtures

The Recap type now requires viewed_at, so the fixtures in
recap_item.test.tsx, recap_processing.test.tsx, and recaps_list.test.tsx
need it too. CI was rejecting them with TS2741.

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

* Mock markRecapsAsViewed in recaps.test.tsx

The mount effect now also dispatches markRecapsAsViewed, but the
manual jest.mock for 'mattermost-redux/actions/recaps' only exposed
getRecaps, so the runtime call resolved to undefined and crashed
with "markRecapsAsViewed is not a function". Add the missing entry
to the mock.

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

* Add /recaps/mark_viewed and Recap.viewed_at to OpenAPI spec

The recap-spec validator rejected the new POST /api/v4/recaps/mark_viewed
handler because it had no documented operation. Add the path with its
MarkRecapsAsViewed operationId, response shape, and behavior, and add
the new viewed_at timestamp field to the Recap schema in definitions.

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

* Fill in app.recap.mark_viewed.app_error translation

The new MarkRecapsAsViewed app method references this i18n key but the
en.json entry was added with an empty translation.

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

* Skip markRecapsAsViewed when getRecaps fails

Marking recaps as viewed implies the user just looked at them. If
getRecaps fails the user is staring at an error/empty state, so we
shouldn't ack them on the server. Gate the dispatch on the thunk's
result.error -- the codebase's bindClientFunc swallows errors and
returns {error}, so the conventional try/catch pattern doesn't apply
here.

Update the recaps.test.tsx dispatch mock to return a resolved promise
so the new awaited result has the expected shape.

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

* Clear and assert markRecapsAsViewed mock in recaps.test.tsx

Reset the new mock in beforeEach so it doesn't carry state across
tests, and assert that the mount effect dispatches markRecapsAsViewed
after getRecaps resolves. Awaiting via waitFor since the mark fires
inside an async fetchData chain.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 15:14:18 +02:00
Jesse Hallam 97c3b53873 MM-68532: default EnableSearchPublicChannelsWithoutMembership to true for new installations (#36399)
* MM-68532: default EnableSearchPublicChannelsWithoutMembership to true for new installations

* fix test: disable backfill in watcher tests to avoid mock store panic

* fix test: mock System store so backfill returns early in watcher tests

* MM-68532: add SetDefaults unit test for EnableSearchPublicChannelsWithoutMembership
2026-05-06 09:13:58 -04:00
5124cc7387 [MM-68590] Clarify membership policy UI copy (#36428)
* MM-68590 add mixed-channel policy notice and tests

Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com>

* Add reusable membership and permission policy testing skill

Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com>

* Remove local-only policy testing skill file

Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com>

* MM-68590 update membership policy UI copy

Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Ibrahim Serdar Acikgoz <isacikgoz@users.noreply.github.com>
2026-05-06 11:50:50 +02:00
1a88e3b322 Adds experimental label to the views endpoints (#36398)
Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-05-06 10:15:49 +02:00
unified-ci-app[bot] d5946e9477 Update latest minor version to 11.8.0 (#36437)
Automatic Merge
2026-05-06 08:23:38 +02:00
Maria A NunezandCursor Agent 7d1ee48c08 MM-68647: Fix Data Spillage reviewer pill background in dark mode (#36424)
* MM-68647: Fix Data Spillage reviewer pill background in dark mode

The SelectableUserPropertyRenderer wraps a react-select component using a
UserMultiSelector classNamePrefix, so the global .react-select styles in
_react-select.scss did not apply. As a result the inner control kept its
default white react-select background, which clashed with dark themes
when viewing the Data Spillage reviewer field.

Make the control transparent and ensure the value/placeholder/input/menu
all use theme-driven colors so the reviewer pill blends correctly in
both light and dark mode.

Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>

* Fix stylelint: order color before gap in placeholder

Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-05-06 07:41:57 +05:30
Jesse Hallam 71fb5b1e54 fix: tolerate concurrent update conflicts in content flagging migration (#36421)
When two servers race through doSetupContentFlaggingProperties
simultaneously (e.g. HA deployments or parallel CI tests sharing a DB),
both read the same UpdateAt timestamps for existing property fields and
both call UpdatePropertyFields. The store's optimistic concurrency
control causes the second writer to get ErrConflict. Since both servers
are writing identical expected values, tolerate the conflict the same
way the create path already does.

Adds a regression test that fires 5 concurrent goroutines at the
migration after fields already exist, verifying all succeed.
2026-05-06 07:36:01 +05:30
2b753c49f2 Remove unused GetChannelCounts store and app methods (#36351)
GetChannelCounts was only reachable from App and had no callers.
Remove SqlChannelStore implementation, store interface, timer/retry
layers, tests, mock, model.ChannelCounts, and the orphaned i18n key.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>
2026-05-05 10:04:36 -07:00
Harrison Healey c787d06506 MM-68494 Directly import WithTooltip/ShortcutKey from shared package (#36270)
* MM-68494 Directly import WithTooltip and ShortcutKeys from shared package

* Fix import order

* Remove unneeded mocks for WithTooltip

* Remove mock from Input test

* Update newly added import paths and remove mocks from new tests
2026-05-05 15:14:22 +00:00
Jesse Hallam e43608762d docs: clarify PR template rules in AGENTS.md (#36422) 2026-05-05 14:47:10 +00:00
Jesse Hallam dd9d16128c MM-68149: upgrade mattermost-server-build images to Go 1.26.2 (#36419) 2026-05-05 16:45:00 +02:00
Christopher PoileandClaude Opus 4.7 0502d6b3c5 [MM-68655] Surface RPC errors from plugin hooks (#36414)
* add WithRPCErr hooks (server-facing/internal only)

* zero _returns on RPC failure in WithRPCErr companions

Aligns the WithRPCErr template with the HooksRPCErr godoc contract: when
g.client.Call returns a transport error, gob may have partially decoded the
reply. Reassign _returns to a zero value before destructuring so callers always
receive zeroed outputs alongside a non-nil transport error.

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

* rename HooksRPCErr to HooksWithRPCErr for naming consistency

Every related symbol uses the WithRPCErr suffix (MessageHasBeenPostedWithRPCErr,
RunMultiPluginHookWithRPCErr, RunMultiHookWithRPCErr, etc.). Aligning the
interface name removes the only outlier and makes the convention uniform.

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

* rename rpcErrImpl to hooksWithRPCErrImpl

Mirrors the existing hooksImpl/Hooks naming pattern on hooksTimerLayer.

* add supervisor.HooksWithRPCErr() and drop runtime type assertion

The old path did rp.supervisor.Hooks().(HooksWithRPCErr) and handled the
"doesn't implement" branch — but that branch was structurally unreachable
(the compile-time `_ HooksWithRPCErr = (*hooksTimerLayer)(nil)` assertion
guards it).

Change supervisor.hooks from `Hooks` to the concrete `*hooksTimerLayer`
(which implements both interfaces, enforced at field assignment), add a
parallel HooksWithRPCErr() accessor, and call it directly. Hooks() keeps
its public Hooks-interface signature via implicit conversion at return.

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

* drop "implemented by" clause from HooksWithRPCErr godoc

Both hooksRPCClient and hooksTimerLayer satisfy the interface, and naming
implementations in interface godocs adds rot — the contract is what readers
need, not the list of wrappers.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 10:41:11 -04:00
Daniel Espino García ebc066e7fd [MM-68273] Add system messages for share / unshare events (#36032)
* [MM-68273] Add system messages for share / unshare events

* Fix CI

* Address feedback

* Fix CI

* Remove unknown prop

* Add missing tests
2026-05-05 15:25:25 +02:00
Harrison Healey 357d1ef0bd MM-67982 Only focus mobile search box when search is opened (#36346)
* MM-67982 Only focus mobile search box when search is opened

* Add E2E tests
2026-05-05 09:21:34 -04:00
Maria A Nunez 8c72083414 MM-68547: Tighten authorization on group syncable link and patch endpoints (#36316)
* MM-68547: Tighten authorization on group syncable link and patch endpoints

Adds an additional permission check on the group syncable link and patch
endpoints. Callers must hold the role-management permission for the
target team or channel (or the sysconsole groups-management permission).

Made-with: Cursor

* Linting

* MM-68547: Extend group syncable scheme_admin authorization checks

Gate any explicit scheme_admin value (in either direction) on link and
patch. Populate SchemeAdmin in the singular getGroupSyncable so that
patches that do not touch scheme_admin no longer overwrite the persisted
value. Restrict PermittedSyncableAdmins to active syncables. Start the
link upsert from the existing active row to preserve fields the caller
did not, or could not, set.

Made-with: Cursor

* MM-68547: Add store-layer regression coverage for SchemeAdmin handling

Extend testGetGroupSyncable to round-trip SchemeAdmin: true through
UpdateGroupSyncable and re-fetch, locking in that getGroupSyncable
populates the field from the persisted row.

Strengthen groupTestPermittedSyncableAdmins{Team,Channel} to assert
that DeleteGroupSyncable preserves SchemeAdmin in the persisted row
and that PermittedSyncableAdmins still excludes the row, making the
coupling between the two store changes explicit.

Made-with: Cursor

* MM-68547: Fix group details role-change dedup on remove

The roleChangeKey helper was reading team_id/channel_id from the items
in itemsToRemove, but onRemoveTeamOrChannel pushes those items with a
generic id field. The deletion of the staged role change in
handleRemovedTeamsAndChannels therefore never matched the key produced
by onChangeRoles, and a stale patchGroupSyncable was dispatched after
the unlink.

Accept either id or team_id/channel_id when computing the key. Also
extend the e2e assertion to verify the channel removal took effect
(delete_at != 0) alongside the existing scheme_admin check.

Made-with: Cursor

* MM-68547: Mirror delete_at assertion on the removed-team e2e test

The team variant of "does not update the role of a removed X" was left
asserting only on scheme_admin. Add the matching delete_at != 0 check
already present in the channel variant so both tests verify the same
user-visible contract.

Made-with: Cursor

* Skip SyncSyncableRoles if no scheme_admin
2026-05-05 09:01:56 -04:00
Alejandro García Montoro 9bbe9ea6e6 Update Agents plugin FIPS version to v2.0.3 (#36417)
Automatic Merge
2026-05-05 14:53:38 +02:00
Pablo Vélez 969ae195e1 MM-68500 - add AttributeValueMasking flag and HasMaskedValues field (#36408)
* MM-68500 - add AttributeValueMasking flag and HasMaskedValues field
2026-05-05 12:33:28 +02:00
Harshil Sharma 9e955bf683 Edit attachment permission (#36227)
* Implemented edit file permission

* lint fixes

* Updated snapshot

* Updated tests

* Updated test

* CI

* Permission reordering and tooltip text update

* Made a geneeric function
2026-05-05 08:31:19 +05:30
Caleb Roseland 8f9b08f07b MM-56762: Bookmarks overflow menu with drag-and-drop reordering (#35118) 2026-05-04 20:14:54 +00:00
Doug Lauder 022acb74c5 MM-68536: Show actual remote names in system console channel list (#36298)
* MM-68536: Show actual remote names in system console channel list

  The system console "Channels" list rendered SharedChannelIndicator
  without a remoteNames prop, so every shared channel showed the generic
  "Shared with trusted organizations" fallback even after PR 35908.

  Add a small connected wrapper that selects remote names via
  getRemoteNamesForChannel and dispatches fetchChannelRemotes on mount,
  mirroring the pattern used by the LHS sidebar. The shared_channel_remote_updated
  websocket event added in PR 35908 already refreshes the same Redux slice,
  so the system console list now stays in sync automatically.
2026-05-04 14:53:57 -04:00
Nick MisasiandCursor 49260c4289 Prepackage mattermost-plugin-agents v2.0.3 (#36401)
Bump the default prepackaged Agents plugin from 2.0.2 to 2.0.3 (non-FIPS).
FIPS prepackaged plugin package list is unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-04 18:03:31 +00:00
sabril 154286f53f fix: only run e2e tests for fips for versions v11+ (#36374) 2026-05-04 23:11:55 +08:00
Maria A NunezandMattermost Build 724c5b7191 CPA Display Name Support (#36247)
* Phase 1: CPA display_name + CEL-safe name validation (server)

- Add typed DisplayName field to CPAAttrs + display_name attr key constant.
- Add ValidateCPAFieldName helper enforcing CEL IDENTIFIER + reserved-word blacklist.
- Wire validation into App.CreateCPAField (always) and App.PatchCPAField (lenient grandfather: skip when Name unchanged).
- Trim + 255-rune cap DisplayName in CPAField.SanitizeAndValidate.
- Developer-facing godoc note documenting rule, sources of truth, and Option C scoping.
- Asserting test for documented Option C plugin-API bypass (closed by PR #36173).

Spec: planner/projects/property-display-name/ideas/001-cpa-display-name/spec.md
Plan: .planning/phase-1/PLAN.md
Made-with: Cursor

* Phase 1 (review): address Reza's Major + Minor findings

- Rename misleading subtest "empty DisplayName is omitted from attrs"
  to "empty DisplayName round-trips as empty string" (Major #1).
- Add TestCPAAttrs_JSONOmitEmpty pinning the omitempty wire-format
  contract that PR #36173's typed-attrs strategy relies on (Major #1).
- Extend TestValidateCPAFieldName: case-sensitivity (IN/In ok),
  single-character names (a/_/A ok), missing "as" reserved word
  (Minor #2). Add whitespace-only DisplayName case (Minor #2).
- Document PropertyFieldNameMaxRunes reuse in SanitizeAndValidate
  to prevent drift (Minor #3).
- Replace broken PLAN-server.md reference in bypass-test docstring
  with in-tree CPAAttrs godoc reference (Minor #4).
- Document omitempty semantics on CPAAttrs.DisplayName field to
  prevent the same misreading caught in review (Minor #5).
- Document grouping intent above CPAFieldNameReservedWords (Minor #8).

Review: .planning/phase-1/REVIEW.md
Made-with: Cursor

* Phase 2: in-app backfill migration for CPA display_name

- Add cpaDisplayNameBackfillKey + cpaDisplayNameBackfillVersion constants.
- Implement (*Server).doSetupCPADisplayNameBackfill: idempotent, cursor-paged
  scan over CPA group fields; backfill attrs.display_name = name when empty.
- Register in m1 migration slice in doAppMigrations (mlog.Fatal on error,
  matching existing convention).
- Three migration tests: NoExistingFields, BackfillsMissing, Idempotent.

System-key idempotency + per-field DisplayName-empty check together provide
HA-safe behavior on rolling deploys (last-write-wins on the System key;
data-level idempotency from the per-field check).

Spec: planner/projects/property-display-name/ideas/001-cpa-display-name/spec.md
Plan: .planning/phase-2/PLAN.md
Made-with: Cursor

* Phase 2 (review): document race + harden idempotency test

- Document SearchPropertyFields→UpdatePropertyFields rolling-deploy
  race: stale snapshot can revert concurrent admin CPA rename. Pre-
  existing systemic shape (no UpdateAt optimistic-lock); narrow
  window; bounded blast radius (admin re-rename, ABAC ID-keyed).
  Accepted limitation per spec Out of Scope (Major #1, Option C).
- Tighten TestCPADisplayNameBackfill_Idempotent: snapshot UpdateAt
  before second run; assert no DB write on the System key or the
  field row (Major #2).
- Extract clearCPABackfillMarker helper with explanatory godoc to
  centralize the 3x-repeated test precondition (Minor #1).
- Comment fieldA seed as the "key-present-as-empty-string" idempotency
  boundary case (Minor #6).
- Add godoc to doSetupCPADisplayNameBackfill (Minor #10).

Review: .planning/phase-2/REVIEW.md
Made-with: Cursor

* Linting

* Removing unnecessary comments

* Clean up tests

* Linting

* Fix tests

* Updated API doc

* Fix tests

* PR Feedback

* Move migration to PropertyService

* Linting

* Linting

* Removed pagination

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2026-05-04 10:33:05 -04:00
Doug Lauder 846791aa65 MM-68622: start inter-cluster services before plugin activation (#36366)
* MM-68622: start inter-cluster services before plugin activation

  Move startInterClusterServices from the end of Server.Start() to the
  beginning, before Channels().Start() initializes plugins. This lets
  plugins call shared channels APIs (ShareChannel, InviteRemoteToChannel,
  UninviteRemoteFromChannel, UnshareChannel, UpdateSharedChannel,
  CheckCanInviteToSharedChannel) during OnActivate instead of failing with
  "Shared Channels Service is disabled".

  Side-effect analysis:

  * Plugin API gating: getSharedChannelsService in
    channels/app/shared_channel.go:32 only requires the service to be
    non-nil. The plugin-facing wrappers all pass ensureIsActive=false, so
    Active() is bypassed. Once SetSharedChannelService runs, calls succeed
    on both leader and follower nodes. This is the fix path.

  * Multi-node leader timing: the enterprise cluster impl in
    enterprise/cluster/cluster.go:70 initializes currentLeader="", so
    IsLeader() returns false before StartInterNodeCommunication runs. The
    immediate onClusterLeaderChange in scs.Start at
    platform/services/sharedchannel/service.go:151 therefore takes the
    pause path, which is a no-op since the service was never active. When
    memberlist.Create fires NotifyJoin for the local node,
    addPotentialLeader runs and InvokeClusterLeaderChangedListeners drives
    the registered listener to resume() the sync loop on the elected
    leader. End state matches the prior ordering.

  * Single-node: IsLeader() returns true unconditionally per
    channels/app/platform/cluster.go:33, so SharedChannelSyncHandler is
    active during plugin OnActivate. Events emitted by plugins during
    activation (posts to shared channels, DM creation) now flow through
    sync where they were previously dropped. This is intended correctness,
    not a regression.

  * Transport and handlers: api4 remote-cluster routes are registered
    before Server.Start, so HTTP handlers exist when rcs.Start runs early.
    rcs and scs do not send cluster-broadcast messages during Start; they
    only register topic listeners on the rcs transport, which is
    independent of cluster gossip. registerClusterHandlers ordering is
    unaffected.

  * Config: scs reads only ConnectedWorkspacesSettings and the License at
    construction, both stable from the initial config load. ReloadConfig
    at server.go:912 has no bearing on inter-cluster service init.

  Errors from startInterClusterServices remain logged and non-fatal,
  matching prior behavior.
2026-05-04 08:53:59 -04:00
Eva SarafianouandClaude Opus 4.7 b7a97f4bdc ci: disable fullyparallel for unsharded weekly Postgres jobs (#36390)
Restore the `fullyparallel: false` override for the unsharded
`Postgres with binary parameters` and `Postgres FIPS` jobs in the
weekly workflow. The override was originally added to the binary
parameters job in #35995 to prevent resource exhaustion on a single
runner, but was dropped when both jobs moved into
server-ci-weekly.yml in #36036, leaving them on the template default
of `true`.

Without it, the hosted runner is overwhelmed (too many server
instances, WebSocket hubs, and DB connections) and the runner agent
itself loses communication with GitHub mid-run, surfacing as
"hosted runner lost communication with the server" at ~55-60 min
into the Run Tests step. Both runs on April 27 and May 4 failed
this way; the sharded FIPS variant retained for FIPS-touching PRs
in server-ci.yml is unaffected because each shard handles only a
fraction of the packages.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:52:35 +03:00
Amy Blais 7ddf584741 Removing Beta Label From Hungarian Language (#36386)
#### Summary
Moving Hungarian language from Beta to Alpha as Hungarian has fallen below the 79% quality threshold for over three months.

#### Release Note
```release-note
Downgrading Hungarian translations from Beta to Alpha.
```
2026-05-04 09:48:30 -03:00
Mattermost Build 1b7b15b121 MM-67931: Prepackage the FIPS flavour of Playbooks v2.8.1 (#36387) (#36391)
Automatic Merge
2026-05-04 14:17:40 +02:00
Alejandro García Montoro e5344bc57d Update Agents plugin FIPS version to v2.0.2 (#36389)
Automatic Merge
2026-05-04 13:17:40 +02:00
Harshil Sharma d4f147e2da Data spillage deletion summary (#36018)
* Report POC

* Including more error logs

* Added localisationj for each reviewer

* Optimisations

* Minor tweaks

* restored go module files

* lint fixes

* Added back transslations

* Added translations

* linter and test fixes

* restored go module files

* e2e lint fix

* lint fixes

* AI fixes

* fixed typo

* fixed nil pointer error

* Added more tests

* Publish report even if deletion fails

* Fixed the e2e test

* Distinguished between no data and deleted data

* lint fixes

* fixed tests

* e2e test fix

* Updated test to also upload actual file

* Removed file name tracking

* Text updates

* fixed e2e test

* lint fix
2026-05-04 06:40:26 +05:30
JG Heithcock ace28cd516 [MM-67867] Update Playbooks plugin to v2.8.1 (#36361)
* [MM-67867] Update Playbooks plugin to v2.8.1

Updates the prepackaged Playbooks plugin to v2.8.1 (regular and FIPS builds).

Prepackage FIPS version for Playbooks.

* Removing FIPS version
2026-05-03 15:08:20 -07:00
Rajat DabadeandMattermost Build 0aa28f9812 Fix the gap between trail branner and the button (#34688)
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-05-01 18:04:09 -04:00
Scott Bishel 5bad893cad Move interactive dialog date/datetime properties into datetime_config (#36067)
* Move min_date, max_date, time_interval into DialogElement.datetime_config

Consolidate date/datetime configuration into the datetime_config sub-object
on both DialogElement (Go/TS) and AppField (TS), deprecating the top-level
fields while keeping them for backward compatibility. DateTimeConfig values
take precedence over legacy fields via EffectiveDateTimeConfig() (Go) and
nullish coalescing fallback chains (TS).

Also fixes: timezone indicator now uses FormattedMessage for i18n, CSS class
with theme variable instead of inline styles, and proper DateTimeConfig type
instead of Record<string, unknown> cast.
2026-05-01 10:00:20 -06:00
Nick Misasi 7d6816abdf MM-68382: Align team creation invite permission checks (#36188)
* MM-68382 - align team creation invite permissions

Keep invite-related team settings consistent during team creation so authorization matches existing update and patch behavior.

Made-with: Cursor

* MM-68382 - move team create helper closer to usage

Keep the create-team authorization helper next to createTeam so the file reads in usage order.

Made-with: Cursor

* commit before ff

* MM-68382 - reject invite fields on create without permission

Reject createTeam with 403 (matching updateTeam/patchTeam) when the creator
tries to set AllowOpenInvite or AllowedDomains without PermissionInviteUser,
instead of silently stripping those fields. Add scheme-branch coverage and
log scheme-fetch failures from the permission check.

Made-with: Cursor

* api4: add symmetric happy path test for scheme InviteUser without invite fields

Cover team creation when the scheme grants InviteUser but AllowOpenInvite and
AllowedDomains are unset, asserting InviteId is returned in the response.

Made-with: Cursor
2026-05-01 14:41:15 +00:00
Christopher Poile 035c3ba4b9 Update go version to 1.25.9 (#36357) 2026-05-01 10:25:58 -04:00
Nick Misasi 99b73d4c4a [MM-68393] Tighten protected role patch authorization (#36197)
* [MM-68393] Tighten protected role patch authorization

Harden role patch authorization for protected system roles and cover the restricted paths with focused API tests.

Made-with: Cursor

* [MM-68393] Fix role patch test shadowing

Rename shadowing response variables in the protected role patch tests so govet passes in core and enterprise check-style jobs.

Made-with: Cursor

* [MM-68393] Block privileged role permissions

Made-with: Cursor
2026-05-01 17:20:04 +03:00
Maria A Nunez 01bb3dcf7a MM-64977: Fix channel switcher row overlap with long channel and team names (#36330)
* MM-64977: Fix channel switcher row overlap with long names

In the Find Channels modal, very long channel names overflowed
their row and visually overlapped the team name shown on the right
because the team label was absolutely positioned and the channel
column did not reserve horizontal space.

Restructure the SwitchChannelSuggestion row to use a real flex
layout: a primary column wrapper holds the channel name and inline
metadata with `flex: 1 1 auto; min-width: 0;` so the name truncates
with an ellipsis, and the team-name span becomes a flex sibling
with `flex: 0 0 auto; max-width: 40%;` so it remains visible. The
channel name is wrapped in WithTooltip whose disabled prop is
driven by a useLayoutEffect-based scrollWidth > clientWidth check,
so the full name is shown on hover only when truncation occurs.

Made-with: Cursor

* MM-64977: Show tooltip on truncated team name as well

Mirror the channel-name tooltip behavior on the team-name span in
the channel switcher row: track its truncation state via the same
useLayoutEffect + ref pattern, and wrap the team name in WithTooltip
whose disabled prop is driven by scrollWidth > clientWidth. Hovering
the team label now reveals the full team display name when (and only
when) it is actually truncated.

Extend existing tooltip tests to assert the team-name tooltip
disabled flag mirrors the truncation state in both branches; loosen
the layout test to permit the WithTooltip wrapper around the team
span while still asserting the team name does not live inside the
primary column.

Made-with: Cursor
2026-05-01 09:09:54 -04:00
Eva Sarafianou 1ead9ff038 update buildserver go base image versions to 1.25.9 (#36348)
Made-with: Cursor
2026-05-01 08:17:34 -04:00
Nick Misasi 082f6ba85a Prepackage Agents plugin v2.0.2 (#36349)
Update non-FIPS PLUGIN_PACKAGES entry to mattermost-plugin-agents-v2.0.2
(https://github.com/mattermost/mattermost-plugin-agents/releases/tag/v2.0.2).

FIPS prepackage is unchanged; v2.0.2 FIPS artifacts are not published yet.

Made-with: Cursor
2026-04-30 20:14:56 +00:00
Alejandro García Montoro 8d7507b1eb Update Agents plugin FIPS version to v2.0.0 (#36344)
Automatic Merge
2026-04-30 18:47:40 +02:00