* Add support packet DB diagnostics for pool and pg_stat
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Fix support packet mock store for new DB diagnostics
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Add context timeouts to support packet pg diagnostics
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Move support packet DB diagnostics queries into sqlstore
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Fix support packet diagnostics lint and partial data handling
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Stabilize support packet pool idle assertion
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Relax live support packet DB counter assertions
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Fix deterministic pool diagnostics test wiring
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Mock support packet diagnostics in app test store
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Move SupportPacketDatabaseDiagnostics out of public model
The struct is an internal store→platform transport (no yaml tags, never
serialized directly) so it doesn't belong in server/public/model where
it would form a public API contract for plugins. Move it into the store
package as the natural return type of GetSupportPacketDatabaseDiagnostics.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Describe SupportPacketDatabaseDiagnostics by content, not history
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* wording
* Align store diagnostics with sqlstore conventions
- Rename Store.GetSupportPacketDatabaseDiagnostics to Store.GetDiagnostics
and rename the holding files to diagnostics{,_test}.go.
- Drop the queryRowScanner / rowScanner / sqlQueryRowScanner test seam.
The collectors now use the sqlxDBWrapper master handle and bind result
rows into local structs via sqlx GetContext, matching how the rest of
the sqlstore package talks to Postgres.
- Replace the hand-rolled mock-based unit tests for the Postgres
collector with an integration test driven through StoreTest, the
pattern used by the other sqlstore tests (e.g. schema_dump_test.go).
The pure pool-stats unit test (TestApplyDBPoolStats) is kept.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Drop MasterDBStats/ReplicaDBStats from the Store interface
After GetDiagnostics moved into the store layer, no caller outside the
sqlstore package itself reads MasterDBStats/ReplicaDBStats through the
Store interface — the diagnostics collector calls them on the concrete
*SqlStore receiver. Remove them from the interface, the retry/timer
layer wrappers, the storetest fake, and the generated mock; drop the
now-redundant fixedDBStatsStore shim methods and mock setups in the
support packet tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Rename SupportPacketDatabaseDiagnostics to DatabaseDiagnostics
Now that the type lives in the store package and is returned by
Store.GetDiagnostics, the SupportPacket prefix is just legacy framing —
support packets are one consumer of the data, not its identity. Rename
to store.DatabaseDiagnostics for consistency with the package and method
name.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Inline diagnostics SQL into the collector functions
Each pg_stat query has a single caller, so a package-level constant just
adds indirection between the function and the SQL it owns. Move the
query strings to local consts inside the collectors that use them.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Fix Connectios typo to Connections
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Fix sqlstore diagnostics build: call GetMaster().DB() method
The recent rebase brought in a change that turned the SqlStore DB
field into a method, so passing ss.GetMaster().DB to
collectPostgresDatabaseDiagnostics (which expects *sqlx.DB) no longer
compiles. Call the method instead.
* Fix gofmt alignment in SupportPacketDiagnostics
The post-merge struct had extra spaces on MasterConnections /
ReplicaConnections that broke gofmt alignment.
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [MM-68577] Add OAuth2/OpenID Connect provider status to support packet
Probe configured GitLab, Google, Office365, and OpenID providers and report
their connectivity status in the support packet diagnostics. For providers
with a DiscoveryEndpoint, the probe verifies a valid OIDC discovery document
(JSON with an "issuer" field) is returned; otherwise it probes the
TokenEndpoint host, treating any HTTP response as reachable since token
endpoints reject GETs.
Disabled providers report status: disabled, enabled providers report ok or
fail with the underlying error. No secrets are read or transmitted; only
public endpoint URLs are probed.
* [MM-68577] Drain response body in probeOAuthTokenEndpoint
Closing resp.Body without first reading it leaves unread bytes on the wire,
which prevents net/http from returning the underlying TCP connection to the
idle pool for keep-alive reuse. Drain with io.Copy + io.LimitReader (1MB
cap to bound a misbehaving server) and use defer for the close.
* [MM-68577] Extract drainAndCloseBody helper for HTTP probe responses
Three call sites in this file (probeOIDCDiscovery, probeOAuthTokenEndpoint,
testPushProxyConnection) now share the same drain-then-close idiom needed
to keep TCP connections eligible for keep-alive reuse. Replace the inline
copies with a single drainAndCloseBody helper that bounds the discard at
1 MiB to limit exposure to a misbehaving server.
Also fixes the same un-drained Close() bug in the pre-existing
testPushProxyConnection while we're here.
* Add comment explaining 1 MiB discovery response size cap
Addresses review feedback asking for clarity on the 1<<20 limit.
* Fix MM-57406: prevent IPv6 hex segments from parsing as emoji
Add word-boundary lookbehind and lookahead to EMOJI_PATTERN so a
:name: token is only matched when neither side abuts an
alphanumeric/underscore. Without this, hex runs in IPv6
addresses (e.g. :beef: in 2001:18:1:beef::/64) were tokenized and
rendered as custom emoji. The new semantics align with the server
parser at server/public/shared/markdown/emoji.go.
Bump the webapp babel safari target from 16.2 to 16.4 since regex
lookbehind is a syntax feature and is not lowered by
@babel/preset-env. The actual supported minimum is well above 16.4, so
the previous target was stale.
Add regression tests for the IPv6 case and guardrail tests for
back-to-back, paren-wrapped, and punctuation-terminated emoji. Update
the existing asdf🐐asdf💨asdf test to reflect the
new (server-aligned) semantics.
* Dedupe word-char class in EMOJI_PATTERN
Use \w in the lookarounds and inside the name matcher ([\w+-])
so the word-char set is expressed once instead of being repeated
as two slightly different literal character classes. Same set,
same semantics; addresses review feedback on PR #36541.
---------
Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es>
* Fix: Global Threads shows 1 quick reaction emoji instead of 3 (MM-68681)
When posts are viewed in the Global Threads full-width view, the hover
toolbar was incorrectly showing only 1 quick reaction emoji instead of 3.
Root cause: In post/index.tsx, the isExpanded prop (which controls whether
the toolbar shows 3 or 1 emojis) was derived only from
state.views.rhs.isSidebarExpanded. When navigating to Global Threads,
suppressRHS is dispatched (setting state.views.rhsSuppressed = true), but
isSidebarExpanded remains false. Since posts in the thread viewer use
RHS_ROOT/RHS_COMMENT locations (not CENTER), and the #sidebar-right element
is suppressed (width = 0), the showMoreReactions check in post_options.tsx
always fell through to showing only 1 emoji.
Fix: Include state.views.rhsSuppressed in the isExpanded computation so that
when the RHS is suppressed (i.e., we are in a full-width context like Global
Threads or Drafts), the toolbar correctly renders 3 quick reaction emojis.
Tests: Added post_options.test.tsx with 4 unit tests verifying:
- CENTER location → 3 emojis (existing behavior)
- RHS_ROOT + isExpanded=false → 1 emoji (narrow RHS, existing behavior)
- RHS_ROOT + isExpanded=true → 3 emojis (expanded RHS or Global Threads)
- RHS_COMMENT + isExpanded=true → 3 emojis
Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com>
* Refactor: use getIsGlobalThreadsView selector instead of rhsSuppressed (MM-68681)
Replace the broad state.views.rhsSuppressed check (which also fires for the
Drafts view) with a precise getIsGlobalThreadsView() selector that reads the
already-existing state.views.lhs.currentStaticPageId field.
When global_threads.tsx mounts it dispatches selectLhsItem(LhsItemType.Page,
LhsPage.Threads) which sets currentStaticPageId to LhsPage.Threads ('threads').
This is the existing, canonical signal that the Global Threads full-width view
is active; the selector wraps it with a name that conveys intent directly.
Changes:
- selectors/lhs.ts: add getIsGlobalThreadsView() — returns true iff
state.views.lhs.currentStaticPageId === LhsPage.Threads
- selectors/lhs.test.ts: 3 new tests covering Threads, Drafts, and empty page
- post/index.tsx: import getIsGlobalThreadsView and use it in isExpanded
- post_options.test.tsx: update test description to match new mechanism
Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com>
* Fix tests: use renderWithContext + real component tree, not jest.mock
The previous post_options.test.tsx used jest.mock() to replace
PostRecentReactions, DotMenu, and several other components. That pattern
is not established in this codebase — post_component.test.tsx and
post_reaction.test.tsx both exercise the real connected component tree
via renderWithContext with a partial Redux state.
Rewrite to match:
- Drop all jest.mock() calls for child components.
- Provide minimal Redux state (roles with ADD_REACTION + user with
system_user role) so that ChannelPermissionGate lets the emoji
buttons render — the same pattern used in post_reaction.test.tsx.
- Use proper SystemEmoji shaped objects (with short_name) as
recentEmojis so that getEmojiName() does not crash.
- Assert on the real rendered emoji buttons (data-testid=
'post-menu__item_emoji') rather than a mocked prop capture.
Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com>
* Remove selector comment; add e2e test for Global Threads quick reactions (MM-68681)
- selectors/lhs.ts: remove JSDoc block from getIsGlobalThreadsView; the
name is self-explanatory and the comment was narrating the code.
- emoji_recently_used_spec.js:
* Add group tag @collapsed_reply_threads (test now requires CRT config).
* Add MM-T4261_3: verifies that hovering a post in the Global Threads
full-width panel shows 3 quick reaction emojis, matching the center
channel and unlike the narrow RHS sidebar which shows 1.
* Add GLOBAL_THREADS case to validateQuickReactions helper (uses
rhsPost id prefix, numReactions=3).
Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com>
* Fix lint: correct import order in post_options.test.tsx and post/index.tsx
- post_options.test.tsx: move @mattermost/types/emojis type import before
mattermost-redux/constants; add missing blank line between import groups.
- post/index.tsx: move selectors/lhs import before selectors/posts
(alphabetical order within the selectors/* group).
Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com>
* Fix types: use correct EmojiCategory value 'people-body' not 'people'
Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com>
* Remove dead RHS_EXPANDED branch from validateQuickReactions helper
No call site ever passes 'RHS_EXPANDED' to validateQuickReactions — the
branch was unreachable. Keep only the 'GLOBAL_THREADS' case that the new
MM-T4261_3 test actually exercises.
Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Miguel de la Cruz <mgdelacroix@users.noreply.github.com>
* Add SAML connectivity status to support packet diagnostics
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Fix SAML diagnostics tests for config validation
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Add enterprise SAML diagnostics hook for support packet
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
* Cleanup
* Fix SAML support packet tests to use enterprise mock interface
Tests were expecting the platform layer to perform HTTP metadata URL
checks directly, but that logic belongs in the enterprise SAML
diagnostic implementation. Updated tests to install a mock enterprise
interface (matching the existing pattern in the override test) instead
of relying on bare HTTP calls that only work without the enterprise
interface registered.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Simplify SamlDiagnosticInterface to return error instead of (string, string)
The status return was always either StatusOk or StatusFail, which maps
directly to nil/non-nil error. Removing the redundant status string
makes the interface idiomatic Go and lets the call site derive status
from error presence.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* lint fix
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Ben Schumacher <hanzei@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix flaky TestUserHasJoinedChannel
The UserHasJoinedChannel plugin hook is invoked from AddChannelMember via
Srv().Go, so the hook post can appear after the join system post. Under CI
load the 5s Eventually window was sometimes too short. Confirm the plugin
is active before triggering the hook, poll posts in channel order like the
sibling subtest, and extend the wait to 10s.
Tests-only change. Verified with `go test -run '^TestUserHasJoinedChannel$' -race
-count=50` locally.
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* test: add plugin activation assertion messages
* test: deduplicate plugin hook post assertion
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
* Add inline action buttons for bot-posted markdown
Bots, webhooks, and plugins can now embed clickable action buttons
inside markdown (including table cells) using mmaction://actionId
links, with row-specific parameters forwarded to the integration on
click. This enables use cases like a per-row "Mx Plan" button in a
fleet-status table that opens a dialog scoped to the clicked row.
Design
- New post prop inline_actions maps actionId (alphanumeric) to a
PostActionIntegration {URL, Context}, capped at 50 entries.
- Markdown link with scheme mmaction:// emits a placeholder span that
messageHtmlToComponent converts to the InlineActionButton component.
- Click POSTs inline_context (parsed from the URL query string) to the
existing /posts/{id}/actions/{action_id} endpoint; the server merges
it into the integration request as context.inline_params while
preserving the post-level context.
- Only bot, webhook, and plugin posts render the button; non-integration
posts have inline_actions stripped on create, update, and ephemeral
broadcast. Hardened-mode also covers the new prop.
- Reuses the existing PostAction dialog pipeline: plugin handlers reply
with a trigger_id and call /actions/dialogs/open as before.
Security
- InlineContext capped at 50 entries / 128-char keys / 2 KB values.
- Integration Context cloned per click so per-click inline_params and
selected_option cannot leak into the cached post for other clickers.
- Plugin response updates cannot add inline_actions to a post that did
not already have them; invalid entries are dropped with a warn log.
- Label content and data attributes are escaped; labels are flattened
to plain text (tags stripped, entities decoded, then escaped).
- Malformed JSON request bodies now return 400 instead of falling
through with an empty inline_context.
Tests
- Model: validators, normalization, GetInlineAction, strip, fallback.
- App: create strip, update guard (4 subtests including
AllowInlineActionsUpdate bypass), ephemeral strip, inline_params
merge, context-map isolation, plugin-response guards, from_bot and
from_plugin retention across plugin updates.
- API: inline_context validation (size bounds + error id),
omitempty backward compat, malformed JSON 400.
- Webapp: renderer scheme handling, allow/deny flags, size caps,
HTML escape, tag strip, entity decode, attribute-injection defense;
component click dispatch, double-click race guard, unmount safety,
error-result recovery, aria state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* lint fix
* i18n-extract
* Review fixes for inline action buttons
- renderer: preserve actionId case; reject opaque mmaction: URI
- app: require bot AND integration session to preserve inline_actions
- app: restore original inline_actions when plugin response is invalid
- i18n: rename key to ...app_error to match convention
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Tighten UpdatePost inline_actions guard; fix test seeds
- app: UpdatePost now requires AllowInlineActionsUpdate to modify
inline_actions. Integration session alone is insufficient — a
PAT-wielding user could otherwise inject inline_actions on any
post they could edit.
- tests: seed bot posts with inline_actions via an integration
session (intSeedCtx) so they survive the create-time strip.
- renderer: lint fix (blank line before comment block).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Reject malformed inline-action authorities at render time
- renderer: enforce ^[A-Za-z0-9]+$ on actionId, mirroring the server
regex. Authorities like mmaction://plan:443 or mmaction://user@plan
now fall through to plain text instead of rendering a dead button.
- post: clarify in the strip comment that webhooks and plugins bypass
CreatePostAsUser entirely (they call CreatePost / CreatePostMissingChannel
directly), so the strip block does not apply to them.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Tighten inline-action renderer tests
- Replace oversized-params test with boundary pair (at-cap and over-cap)
to lock in the > vs >= behavior of the size-limit check.
- Add a "surrounding text survives" assertion for the tag-strip path so
a future swap from regex strip to a DOM sanitizer won't silently
drop legitimate content along with tags.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Inline action buttons via mmaction:// markdown links
Adds inline action buttons rendered from mmaction:// links in markdown,
with the click pipeline reusing the existing post-action infrastructure.
Aligned with the broader mm_blocks_actions framework (Daniel's PR).
* fix lint, DoS hardening, fix and rename test
* Address review feedback
* lint fix
* Reject percent-encoded path traversal in validateIntegrationURL (e.g. %2e%2e%2f) by parsing the URL and checking the decoded path.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* Add TestDesanitizeRemovesAllFakeSettings to catch future omissions
Walks every string field in the config after a Sanitize+desanitize
round-trip and fails if any still holds FakeSetting. This catches the
case where a field is added to Sanitize without a corresponding
desanitize entry.
* Fix ElasticsearchSettings.ClientKey being incorrectly masked as a secret
ClientKey is a file path, not a secret value. Masking it caused the
asterisk string to be persisted to the database on config writes, which
broke TLS client auth on restart.
* Fix desanitize missing entries for fields added in 504fb96fdd504fb96fdd masked five fields in Sanitize without adding the
corresponding desanitize entries, meaning a config save through the
API would permanently overwrite those fields with FakeSetting:
- FileSettings.ExportAmazonS3SecretAccessKey
- ServiceSettings.GoogleDeveloperKey
- ServiceSettings.GiphySdkKey
- CacheSettings.RedisPassword
- AutoTranslationSettings.LibreTranslate.APIKey
* fixup! Add TestDesanitizeRemovesAllFakeSettings to catch future omissions
* fixup! Fix desanitize missing entries for fields added in 504fb96fdd
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
* Fix flaky email sort test by ignoring punctuation in localeCompare
PostgreSQL's en_US.UTF-8 collation ignores hyphens at the primary sort
level, but JS localeCompare() on a C-locale CI runner uses byte order,
causing the expected and actual sort orders to diverge for emails
containing hyphens. Passing ignorePunctuation:true aligns JS collation
with Postgres behavior.
* E2E/Cypress: re-enable CYPRESS_* env var overrides
allowCypressEnv: false was introduced in the v15.13 upgrade (PR #36091)
but broke the existing CYPRESS_adminUsername / CYPRESS_adminPassword
override mechanism that local and CI runners depend on.
* E2E/Cypress: fix MM-T1508 accessibility image test flakiness
The test was failing because the admin user could have a stale
compact display mode preference from a previous spec, causing
post avatars to render with pointer-events: none and blocking
the .status-wrapper click.
Two fixes:
- resetUserPreference() now resets message_display to 'clean'
so compact mode doesn't leak across spec files
- accessibility_image_spec before() now runs as a fresh user
with default preferences rather than the shared admin account
* MM-68501 - implement GetMaskedVisualAST and wire API handler
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* add missing test and fix style issues
* fix styles
* implement coderabbit feedback
* MM-68501 - PR review: split masking file, model-level access mode, reject contradictory config
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* MM-68501 - apply shared_only filter to non-option field values (binary masking)
* MM-68501 - consolidate masking flag check and log corrupt text value during masking
* MM-68503 - add CEL utilities, write-path validation, and merge helpers
Combined set of helpers consumed by BE-5's save path:
CEL construction / serialization
- extractStringValues, buildCELFromConditions, conditionToCEL,
celStringLiteral, celValueLiteral. Used to rebuild a CEL string from a
VisualExpression, including for GetMaskedExpression on the read-side
of policy GET / search responses.
Merge-on-save helpers
- getHiddenValues (per-condition, with pre-fetched fields map for N+1
avoidance) — finds which stored values are not visible to the caller.
- mergeConditionValues — re-injects the hidden values into a submitted
condition without duplicates.
- Together, these let BE-5 preserve attribute values the caller cannot
see while still letting them edit the visible parts of a policy.
Write-path value-hold validation
- validatePolicyExpressionValues, invalidValueError, validateConditionValues.
- Generic "Invalid value." error on every rejection — no signal about
whether the value exists or is merely not held (prevents enumeration).
- Rejects the masked-token sentinel "--------" if submitted as a literal.
These all live in access_control_masking.go alongside the masking primitives
that BE-2 introduced. i18n entries added for the two new error IDs
(app.pap.save_policy.invalid_value, app.pap.validate_expression_values.app_error).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* MM-68503 - handle the masked-token sentinel in validation and merge
When the GET /policies endpoint returns a policy via MaskPolicyExpressions,
the raw expression contains the masked-token sentinel "--------" in place
of hidden values. If the frontend round-trips that expression unchanged
back to the server (e.g., the admin only modified channel assignment, not
the rules), the sentinel reaches the save path.
The previous code in validateConditionValues rejected the sentinel as
"Invalid value." This blocks the legitimate round-trip case.
Fix:
- validateConditionValues: treat the sentinel as a placeholder and skip
it during visibility / source-only / unknown-mode checks. Other values
are still validated normally.
- mergeConditionValues: strip the sentinel from submitted values before
appending hidden values, so it never propagates to the stored result.
Both array and single-value forms (string == "--------") are handled.
TestMaskedTokenRejection (which asserted the old rejection behavior) is
replaced by TestMaskedTokenConstant which only verifies the sentinel
string itself.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* MM-68504 - integrate save-path masking: 403 block on delete, merge-on-save, response masking
Save path (CreateOrUpdateAccessControlPolicy):
* validatePolicyExpressionValues runs on the submitted expression before
merge so re-injected hidden values are never validated against the
caller's holdings.
* mergeStoredPolicyExpressions re-injects hidden values from the stored
policy and blocks (HTTP 403) any attempt to remove a condition that
contained values the caller cannot see — closes the row-deletion gap
in classified environments.
* mergeExpressionWithMaskedValues unwraps single-element arrays for scalar
operators after restoring the stored operator (avoids "attr == [val]"
invalid CEL when the frontend submits "attr in []" as the masked-row
placeholder for an originally-scalar condition).
* checkSelfInclusion is bypassed for system admins (they may legitimately
write conditions for values they do not hold); masking and value-hold
validation still apply to system admins.
Delete path (DeleteAccessControlPolicy):
* Same masked-values 403 block — a caller with masked values cannot delete
the policy at all (UI Delete button is also disabled in FE-3).
Response masking:
* createAccessControlPolicy and setAccessControlPolicyActiveStatus run
MaskPolicyExpressions on the response so even a save reply doesn't
leak the values the caller does not hold.
GetMaskedExpression, maskConditionValuesWithToken, replaceHiddenValuesWithToken,
MaskPolicyExpressions live alongside the rest of the masking helpers in
access_control_masking.go.
team_access_control.go: corrects ValidateChannelEligibilityForAccessControl
call site (drops the spurious receiver and rctx; it's a package-level helper
that only takes channel).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* MM-68503 - address PR review: batch field fetches, propagate errors, fail-closed write path
* MM-68503 - restore team-admin api4 tests accidentally dropped during BE-5 rebuild
* MM-68503 - address review and CodeRabbit feedback on save-path masking
* add tests for delete masking, self-inclusion, GET mask
* add assertions to strengten tests
* MM-68505 - add has_masked_values type and MaskedChip component
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* MM-68506 - add masking support to TableEditor and team settings modal
TableEditor (table_editor.tsx, table_editor.scss):
- hasMaskedValues plumbed through rows; lock operator/attribute selectors on
masked rows.
- Row remove (trash) button disabled on masked rows; disabled-state CSS so
the icon doesn't show the destructive hover colour or a pointer cursor.
- Test Rules button disabled when any row has masked values, with tooltip.
- onMaskedStateChange callback to notify the parent for cross-component
states (CEL editor read-only, Save disabled, banners).
Value selectors (single_value_selector_menu.tsx, multi_value_selector_menu.tsx,
selector_menus.scss, value_selector_menu.tsx):
- Append MaskedChip after visible chips on multi-value rows.
- Render MaskedChip as the sole value on single-value rows where the caller
holds no visible value.
Policy details (policy_details.tsx, .scss, .test.tsx):
- Track hasMaskedRows state; receive from TableEditor via onMaskedStateChange.
- Show masked-values warning banner above the editor when present.
- Same banner on the Delete confirmation modal so admins understand why
deletion is consequential.
Team settings modal (team_policy_editor.tsx, .scss):
- Same masked-values plumbing; delete button uses the disabled state when
a policy has masked values, regardless of whether channels are assigned.
- Pre-save check no longer treats "in []" as an incomplete rule — that
placeholder comes from fully-masked rows that merge-on-save will fill in.
i18n entries added for the new strings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* MM-68506 - fix hook order in SingleValueSelector when masked state changes
The early return for `hasMaskedValues && !value` sat between useState
and useCallback declarations, so when a parent re-render flipped the
masked state (e.g. after deleting a sibling rule) React saw a different
hook count and crashed with "Rendered fewer hooks than expected".
Move the read-only short-circuit after all hook declarations so the hook
order stays stable across renders.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* MM-68507 - CEL editor read-only when masked + system console wiring
CEL editor (editor.tsx, editor.scss):
- hasMaskedRows prop: when true, Monaco is set to read-only and a banner
explains why ("This expression contains restricted values. Switch to
Simple mode to edit the values you have access to, or delete the entire
rule.").
- Test Rules button disabled in CEL mode when hasMaskedRows is true.
Policy details (policy_details.tsx, .scss):
- hasMaskedRows state plumbed to CELEditor, TableEditor, and the Save /
Delete buttons.
- Save button disabled while masked rows are present (kept after the
save-allowed-with-masked-values change in BE-5? — no, here we keep Save
enabled so admins can add/modify rules; only row removal of masked rows
is blocked).
- Delete Policy button disabled when hasMaskedRows; a SectionNotice above
the Delete card explains why ("This policy contains restricted values
- Deletion not allowed").
- New save error messages: invalid_value and self_exclusion are surfaced
from the server's generic responses.
Policies list (policies.tsx): minor wiring change for the new state plumbing.
Table editor (table_editor.tsx): cross-component coordination — emits
onMaskedStateChange and respects the disabled-for-masked-row policy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* MM-68508 - E2E suite for attribute-value masking
Covers the full read+write masking flow against a real server:
- Masked chip rendering, operator/attribute lock, Test Rules disabled.
- System admin subject to masking like any other caller (no role bypass).
- Save with masked values: hidden values preserved by merge-on-save.
- Trash button disabled on masked rows; server returns 403 on direct
API attempt to remove a masked condition.
- Delete Policy button disabled + server 403 when policy has masked values
(both system console and team settings modal paths).
- Self-inclusion failure only fires when the caller holds full visibility.
- CEL editor read-only with banner when masked rows present.
- Direct API validation: non-held values and the masked-token sentinel
rejected with a generic "Invalid value." error.
- Feature-flag-off path: no masking, all values visible.
- Text-field shared_only masking (binary) with `in` and `==` operators.
A pluggable DB-setup helper marks specific CPA fields as shared_only for
the duration of a test (with per-test cleanup) since the API blocks setting
access_mode=shared_only without a source_plugin_id.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* MM-68506 - fix lint, jest mock factory, and unreachable delete-modal test
* MM-68506 - localize masked-condition-deleted save error
* MM-68506 - fix masked-policy delete warning detection and localize masked_rule_deleted
* fix linter issues
* MM-68506 - surface delete error, lock value selector on masked rows, drop dead remove-modal
* fix linter, add translations, adjust specs
* import wittoltip from shared
* fix linter and use the correct button variant
* MM-68506 - drop dangling rationale comment in access_control_field_test
* fix linter, translation and e2e tests
* use pg ts types and dependencies for e2e types mocks
* adjust switch mode persistance restriction
* fix team settings style buttons
* fail-closed guard for advanced expressions in merge-on-save, plus helper unit tests, and FF/test-helper cleanups
* MM-68505 - add has_masked_values type and MaskedChip component
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* MM-68506 - add masking support to TableEditor and team settings modal
TableEditor (table_editor.tsx, table_editor.scss):
- hasMaskedValues plumbed through rows; lock operator/attribute selectors on
masked rows.
- Row remove (trash) button disabled on masked rows; disabled-state CSS so
the icon doesn't show the destructive hover colour or a pointer cursor.
- Test Rules button disabled when any row has masked values, with tooltip.
- onMaskedStateChange callback to notify the parent for cross-component
states (CEL editor read-only, Save disabled, banners).
Value selectors (single_value_selector_menu.tsx, multi_value_selector_menu.tsx,
selector_menus.scss, value_selector_menu.tsx):
- Append MaskedChip after visible chips on multi-value rows.
- Render MaskedChip as the sole value on single-value rows where the caller
holds no visible value.
Policy details (policy_details.tsx, .scss, .test.tsx):
- Track hasMaskedRows state; receive from TableEditor via onMaskedStateChange.
- Show masked-values warning banner above the editor when present.
- Same banner on the Delete confirmation modal so admins understand why
deletion is consequential.
Team settings modal (team_policy_editor.tsx, .scss):
- Same masked-values plumbing; delete button uses the disabled state when
a policy has masked values, regardless of whether channels are assigned.
- Pre-save check no longer treats "in []" as an incomplete rule — that
placeholder comes from fully-masked rows that merge-on-save will fill in.
i18n entries added for the new strings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* MM-68506 - fix hook order in SingleValueSelector when masked state changes
The early return for `hasMaskedValues && !value` sat between useState
and useCallback declarations, so when a parent re-render flipped the
masked state (e.g. after deleting a sibling rule) React saw a different
hook count and crashed with "Rendered fewer hooks than expected".
Move the read-only short-circuit after all hook declarations so the hook
order stays stable across renders.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* MM-68507 - CEL editor read-only when masked + system console wiring
CEL editor (editor.tsx, editor.scss):
- hasMaskedRows prop: when true, Monaco is set to read-only and a banner
explains why ("This expression contains restricted values. Switch to
Simple mode to edit the values you have access to, or delete the entire
rule.").
- Test Rules button disabled in CEL mode when hasMaskedRows is true.
Policy details (policy_details.tsx, .scss):
- hasMaskedRows state plumbed to CELEditor, TableEditor, and the Save /
Delete buttons.
- Save button disabled while masked rows are present (kept after the
save-allowed-with-masked-values change in BE-5? — no, here we keep Save
enabled so admins can add/modify rules; only row removal of masked rows
is blocked).
- Delete Policy button disabled when hasMaskedRows; a SectionNotice above
the Delete card explains why ("This policy contains restricted values
- Deletion not allowed").
- New save error messages: invalid_value and self_exclusion are surfaced
from the server's generic responses.
Policies list (policies.tsx): minor wiring change for the new state plumbing.
Table editor (table_editor.tsx): cross-component coordination — emits
onMaskedStateChange and respects the disabled-for-masked-row policy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* MM-68508 - E2E suite for attribute-value masking
Covers the full read+write masking flow against a real server:
- Masked chip rendering, operator/attribute lock, Test Rules disabled.
- System admin subject to masking like any other caller (no role bypass).
- Save with masked values: hidden values preserved by merge-on-save.
- Trash button disabled on masked rows; server returns 403 on direct
API attempt to remove a masked condition.
- Delete Policy button disabled + server 403 when policy has masked values
(both system console and team settings modal paths).
- Self-inclusion failure only fires when the caller holds full visibility.
- CEL editor read-only with banner when masked rows present.
- Direct API validation: non-held values and the masked-token sentinel
rejected with a generic "Invalid value." error.
- Feature-flag-off path: no masking, all values visible.
- Text-field shared_only masking (binary) with `in` and `==` operators.
A pluggable DB-setup helper marks specific CPA fields as shared_only for
the duration of a test (with per-test cleanup) since the API blocks setting
access_mode=shared_only without a source_plugin_id.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* MM-68506 - fix lint, jest mock factory, and unreachable delete-modal test
* MM-68506 - localize masked-condition-deleted save error
* MM-68506 - fix masked-policy delete warning detection and localize masked_rule_deleted
* fix linter issues
* MM-68506 - surface delete error, lock value selector on masked rows, drop dead remove-modal
* fix linter, add translations, adjust specs
* import wittoltip from shared
* fix linter and use the correct button variant
* MM-68506 - drop dangling rationale comment in access_control_field_test
* fix linter, translation and e2e tests
* use pg ts types and dependencies for e2e types mocks
* adjust switch mode persistance restriction
* fix team settings style buttons
* fail-closed guard for advanced expressions in merge-on-save, plus helper unit tests, and FF/test-helper cleanups
* Refactor access control methods to use GetPropertyGroup for CPA group ID retrieval
* fix styles
* disable delete on masked policies in list view and remove dead modal warnings
* fix unit tests
* preserve hasAnyOf operator display for fully-masked multiselect conditions
* address PR feedback: lock Actions on masked save, filter source/shared_only from /attributes, add unit tests and e2e tests
* fix e2e tests
* comment out e2e to isolate issue
* completely remove the files to pass linter
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* MM-68838: ping restored plugin remote immediately on re-register
RegisterPluginForSharedChannels' restore branch updated the row but did
not call PingNow, leaving the restored remote offline until the next
pingLoop tick (up to PingFreq, default 1 minute). The new-connection
branch already calls PingNow; the restore branch now mirrors it so
sync attempts immediately after a plugin restart no longer fail with
"offline remote cluster".
* MM-68838: gob-encode error returns in apiRPCServer.ReceiveSharedChannelAttachmentSyncMsg
The apiRPCServer wrapper for ReceiveSharedChannelAttachmentSyncMsg
assigned the hook's error return directly to the gob-encoded response
struct. When the framework's App.ReceiveSharedChannelAttachmentSyncMsg
returned an error wrapped with %w (*fmt.wrapError, an unexported type),
gob refused to encode it and the RPC server broke the connection with
"type not registered for interface: fmt.wrapError".
Every subsequent plugin/server RPC call then returned the zero-value
response struct, causing plugins that dereferenced the nil returns to
crash.
Apply the existing encodableError() helper so the returned error
becomes a gob-safe ErrorString, matching every other apiRPCServer
method in this file.
* Hide Download Apps UI when running in Desktop app
Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>
* Fix ESLint import order for Desktop app visibility changes
Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* Create config-change-checker.yml
* Create check_config_changes_ci.py
* Update config-change-checker.yml
* Update check_config_changes_ci.py
* Update check_config_changes_ci.py
* Update check_config_changes_ci.py
* Update check_config_changes_ci.py
* Update config-change-checker.yml
* Update check_config_changes_ci.py
* Update config-change-checker.yml
* Update config.go
* Fix check_api to detect multi-line and multi-method endpoints
The previous implementation matched the .Handle(...).Methods(...) regex
line-by-line against diff lines. This silently missed two real and
common patterns in api4/:
1. Multi-line .Handle(...) declarations — e.g. group.go has 18 of
them, where the path lives on one line and the wrapper/handler on
the next. The regex never matched, so PRs adding such endpoints
produced empty release-note entries.
2. Multi-method declarations like
.Methods(http.MethodGet, http.MethodHead) (4 instances in file.go)
— the old regex required a closing paren immediately after the
first method.
The fix:
- Add a file_at(ref, path) helper that snapshots a file at a git ref
via 'git show', so checkers can compare full file states instead of
pattern-matching diff text.
- Add _scan_endpoints() that whitespace-collapses the file before
matching, letting the regex span what were originally multiple
lines.
- Loosen _HANDLE_RE to capture the methods list as a substring and
extract individual HTTP verbs with a known-method allowlist, so
multi-method declarations produce one entry per verb.
- Switch check_api to set-diff (after - before) / (before - after)
on the parsed endpoint sets. This also cleanly handles routes
that move within a file (no fragile add/remove dedup needed).
- Anchor the new/deleted file detection to '^new file mode \d+' to
avoid false positives from stray text in source files.
Made-with: Cursor
* Track enclosing struct in check_config to avoid dedup collisions
The previous check_config keyed its add/remove dedup on the bare field
name. The dedup intent was to ignore fields that were merely reordered
within config.go (which appear in the diff as both '-Foo' and '+Foo').
But because the key was just the field name, an unrelated rename in one
struct could silently cancel out a real new field with the same name in
a different struct. For example, in a single PR:
- EnableFoo *bool // removed from ServiceSettings
+ EnableFooV2 *bool
- EnableBar *bool // removed from EmailSettings
+ EnableFoo *bool // newly added — but wrongly cancelled below
The dedup would see 'EnableFoo' in both lists and drop both entries,
hiding the brand-new EmailSettings.EnableFoo from the release-note
output.
The fix tracks each field's enclosing struct using a brace-depth stack
that walks the file at BASE_SHA and HEAD_SHA. Fields are keyed as
(struct_name, field_name) tuples, so identically-named fields in
different structs are distinct, and the dedup only collapses true
reorderings. As a side benefit the rendered output is now
'StructName.FieldName' which is much more useful to reviewers.
Switching to file-at-revision scanning + set diff also removes the
custom dedup logic entirely — set arithmetic handles "moved within
file" naturally.
Made-with: Cursor
* Switch remaining checkers to file-at-revision style; drop lines_by_sign
check_audit_events and check_go_version still parsed +/- diff lines
directly, with the same brittle dedup-and-cancel logic that was used in
the previous check_config. After the previous two commits the rest of
the file uses the file_at(ref, path) helper to compare full file
states between BASE_SHA and HEAD_SHA, which:
- removes the entire moved-within-file dedup dance (set arithmetic
handles it for free),
- aligns all four checkers on a single, easy-to-reason-about pattern,
- is robust to whitespace-only or reordering edits in the watched
files.
For Dockerfile.buildenv the helper also avoids a subtle case where the
old code only inspected +/- lines: an edit to an unrelated RUN line
that didn't touch the FROM line could in theory leave both old_ver and
new_ver as None even though the version was effectively unchanged.
Reading the file at each revision compares the actual current and
previous FROM line directly.
The lines_by_sign helper now has no callers, so remove it.
Made-with: Cursor
* Update config.go
* Update config.go
* Update check_config_changes_ci.py
* Update check_config_changes_ci.py
* Update check_config_changes_ci.py
* Update check_config_changes_ci.py
* Tighten check_config_changes_ci.py: regex coverage + idempotency
- Restore tolerant `_HANDLE_RE` so 2-arg wrappers (e.g. `api.APISessionRequired(handler, handlerParamFileAPI)`)
are not silently dropped from the api4 endpoint scan; broaden the `.Methods(...)`
capture so string-literal variants (`Methods("GET")`) work too. Filtering moves
back to the `_HTTP_METHODS` allowlist in `_parse_methods` to keep stray
identifiers from being treated as HTTP verbs.
- Make `strip_old_note` also remove auto-generated lines that landed outside
the ```release-note fence (the inject_note fallback paths) so reruns no
longer accumulate duplicates when a PR has no fence.
- Skip the GitHub PATCH when the PR description is already up to date, so
every commit no longer triggers an unconditional write.
- Wire up `check_go_version`'s `additions` path in `_format_lines` and
`_AUTO_LINE_RE` so a freshly-added Dockerfile.buildenv emits a note.
- Remove the now-dead `CheckResult.to_markdown` method (replaced by
`_format_lines`).
Made-with: Cursor
* Restore ExperimentalSettings.EnableWatermark
The field was removed in f71527f0b1 but `server/config/client.go`,
`server/config/client_test.go`, and `server/public/model/config_test.go`
still reference it (added on master in #36025). Restoring the field
makes the branch compile again so CI can go green.
Made-with: Cursor
* Replace placeholder release-note content (NONE / N/A) on injection
The script previously appended its auto-detected lines INSIDE the
```release-note fence but never displaced template placeholders, so PRs
that only had `NONE` ended up with output like:
NONE
Added `Foo.Bar` configuration setting.
Go runtime updated from 1.25.8 to 1.25.9.
When the existing fence content is empty or consists only of placeholder
tokens (NONE, N/A, NA, dashes — case-insensitive), replace it entirely
with the auto-detected entries. User-written human content is still
preserved by appending instead.
Idempotent: stripping followed by re-injection keeps the placeholder
visible when there's nothing to inject, and replaces it again when there
is.
Made-with: Cursor
* Update config-change-checker.yml
* Update check_config_changes_ci.py
---------
Co-authored-by: Your Name <eva.sarafianou@gmail.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
Workers no longer run `npm ci` — `node_modules` and framework binaries
are restored from actions/cache populated once by a new `prep-deps` job.
This closes the intermittent EEXIST/ENOENT failure inside npm's own
cacache writer that occasionally fails `npm ci` on a runner. Removing
`npm ci` from workers also cuts ~5 min of duplicated install work per worker.
dispatch-begin now runs as its own job after prep-deps so it fires once the
per-worker test-server setup is the only remaining work before dispatch-run.
* Add Classification Markings admin console page
Adds a new admin console page under Site Configuration for managing
classification markings. This allows system administrators to define
classification levels (e.g., UNCLASSIFIED, SECRET, TOP SECRET) with
associated colors and rank ordering, which will be used for system-wide
and per-channel classification banners.
The page includes:
- Enable/disable toggle backed by the property field system (field
existence = enabled)
- Country preset dropdown (US DoD, NATO, UK GSCP, Canada, Australia
PSPF) that auto-fills standard classification levels
- Editable classification levels table with drag-and-drop reorder,
inline text editing, color picker, and delete
- Auto-switch to "Custom" preset when levels are manually modified
- Confirmation dialog when switching presets would overwrite custom data
Also adds:
- ClassificationMarkings feature flag (default off)
- Generic property field client methods (get/create/patch/delete) for
the /api/v4/properties/ endpoints
- Enterprise license + feature flag gating on the admin page
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix classification markings: add validation, error handling, and system object type
- Add "system" as a valid property field object type so the
classification markings API calls succeed
- Surface load errors instead of silently swallowing them (only
suppress 404 for unconfigured state)
- Validate before save: require at least one level, non-empty names,
and no duplicates
- Default to custom preset with empty levels on first open
- Add section strings to searchableStrings for admin console search
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Move classification field to CPA group targeting users
Store the classification markings property field in the
custom_profile_attributes group with object_type 'user' instead of the
attributes group with object_type 'system'. Clear target_id for PSAv2
system target compliance and mark the field as admin-managed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Stabilize preset option IDs and add danger warning on preset switch
Hardcode deterministic IDs for all preset classification levels so
switching away and back preserves option IDs, preventing orphaned
property values. Compare only level data (not preset label) for change
detection so cosmetic preset switches don't trigger false save states.
Show a danger modal with red confirm button when changing presets on an
existing field, warning about system-wide impact on classified resources.
The warning appears once per session then allows frictionless switching.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Remove system object type from property fields
Not needed yet — will be added when system/channel banners are implemented.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix ESLint errors in classification markings admin page
Fix import ordering and remove unused generateId import.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address CodeRabbit review feedback for classification markings
- Register property field API endpoints when ClassificationMarkings flag
is enabled (not just IntegratedBoards) to prevent 404s
- Preserve preset option IDs when creating a new classification field
instead of blanking them with empty strings
- Add sysconsole read/write permission constants for classification
markings across server and webapp, and wire up resource-level
permission checks in the admin definition
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add rank attribute to classification marking options
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add classification markings permissions migration and read-only support
Add a permissions migration to grant classification markings sysconsole
permissions to existing roles on upgrade. Wire up the disabled prop so
read-only users can view but not edit classification settings. Register
the permission in the Delegated Granular Administration UI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Paginate loadField to find classification field beyond first page
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix lint errors and warnings in classification markings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Remove classification markings sysconsole permissions; gate on sysadmin instead
Classification markings admin page no longer uses feature-specific
read/write permissions. Visibility is gated on license + feature flag,
editing is gated on system admin role. This avoids coupling
feature-specific permissions to the generic property service.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Set sysadmin-level permissions on classification markings field creation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Use stable IDs instead of array indices for classification level operations
Switch updateLevel/deleteLevel to identify levels by ID rather than
index, sort levels by rank on load, and extract i18n strings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Refactor classification markings into extracted helper functions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add tests for classification markings admin console feature
Add unit and component tests covering:
- Pure function tests for detectPreset, optionsToLevels, levelsToOptions,
processClassificationField, and fetchClassificationField pagination logic
- React component tests for rendering states, validation, and user interactions
- Client4 property field method tests for URL construction and HTTP verbs
- Server routing test verifying routes register with ClassificationMarkings flag
- Feature flag default and serialization test
Export pure functions from classification_markings.tsx to enable direct testing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix lint errors in classification markings tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix test compilation error
* Fix color input auto-filling after 3 hex characters in classification markings
Buffer ColorInput onChange in a LevelColorCell wrapper so the table
doesn't re-render mid-typing, preventing the input from losing its
focus-guarded local state.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fixing style issues with color picker z-index
* Added fix to prevent immediate dismissal when clicking inside color picker
* Adding E2E test suite for configuration
* Removing duplicates
* Fixing unrelated linter error
* Fixing test linting issues
* Updating tests to skip appropriately
* Matching configuration to UX specs
* Fixing style lint
* Added informational banner for presentational nature of markings
* Enabling the markings flag on playwright server
* Added missing feature flag to e2e test environment in ci
* Reverting changes to color_input
- Not needed as we're using a custom component
* Added and polished global banner configuration
* Refactoring webapp for readability
- Separating components
- Adding unit tests
- Isolating helper methods into utilities
* Fixing linter errors
* linter fix
* Manually fixing linter issues
* Separating global classification component
* Added persistence of classification marking configuration
* Changing LevelID with LevelName
* Making changes for PR reviews
* Changing property object of classification field to template
* syncing i18n file
* Removing inaccurate note from comments
* PR fixes for UX review
* Cleaning up unused value
* Added GlobalClassificationBanner component
- Made sure it syncs on change by using normal configuration values on it
- Works with "top" and "top_and_bottom"
- Renders on both root and admin_console
* Adding E2E test cases for global classification
* Linter fixes, i18n extract
* PR Fixes
* Linter fix
* Matching default messages
* Fixing type errors
* Fixing pipeline and runtime errors
* Fixing announcementbar rendering on top of global classifications
* Increasing banner & font sizes
* Fixing font size to 12px instead of 16px
- I read it wrong
* Replacing config values with property
* Test linter fixes
* Fixing type errors and go format error
* Making changes needed to align with specs
- Ensuring system_classification is a separate linked property that differs from the template
- Saving the global classification banner values as a propertyvalue
* Added missing arguments in e2e tests
* Added missing conditions for useEffect
- Also fixing E2E error in pipeline
* Fixing issues with V1 and V2 group mismatch
* Fixes for linter errors and coderabbit review
* Addressing more issues found by coderabbit
* Fixing issues found by coderabbit
* Migrating to use system properties
* Ran all linters and prettier
- Resolving coding style drift that happened from not running prettier on the webapp (even though CI doesn't check for this)
* Undoing the prettier changes in webapp
* Cleaning up unwanted autoformatted changes
* Reverting prettier changes to clean diff
* Fixing E2E test
* Import fixes in test
* Applying changes for PR feedback
* Fixing issues with failing e2e tests
* Changing key of selection from name to id
* Replacing field setup in E2E tests to use levelId instead of levelName
* Added classification setup per channel on channel creation
* WIP: Adding classification banner integrated with channel banners
- Using a hook to resolve which values should be evaluated when displaying the banner
* Fixing style of dropdown input for classifications
* Fixing visual issues with dropdown inputs
* Adding E2E Tests and linter fixes
* General fixes and improvements
* Applying linter fixes
* Resolving lingering linter issues
* Updated snapshot and extracted i18n
* Adding test cleanup to prevent failures due to duplicates
* Addressing nitpick comment for test mapping of values
* Applying more fixes to E2E tests
* Improving test coverage and e2e test cleanup
* Resolving type issues
* Refactoring classification constant names an documentation
* Ensuring propertyvalue only stores single id, storing banner text in banner_info
* Fixing issues with linter alongside style issues on header
* Updating test assertion to account for fallback
* Fixing issues found during testing
- Removing custom selection from being an option and turned it into a state
- Ensuring only system administrators can set channel classification levels
* Fixing z-index issue with color input popover
* Setting classification level to lowest available value when switching it on
* Updating unit tests to match new spec for preselection
---------
Co-authored-by: David Krauser <david@krauser.org>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: David Krauser <david@kruser.org>
Co-authored-by: Mattermost Build <build@mattermost.com>