mirror of
https://github.com/hanzoai/team.git
synced 2026-09-30 01:57:35 +00:00
master
543
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
045c8b0fa2 |
Put the redux workspace where everything names it
webapp/package.json lists platform/@hanzoteam/redux, check-external-links.mjs
reads platform/@hanzoteam/redux/src, and the package's own homepage and
repository.directory both say platform/@hanzoteam/redux. The directory was
platform/mattermost-redux, so none of those resolved.
npm ci says nothing about it — a workspace whose directory is absent is
silently dropped — and then the very next command dies:
Failed to build subpackages
ENOENT: no such file or directory, open 'platform/@hanzoteam/redux/package.json'
at getPlatformPackagesContainingCommand (scripts/utils.mjs:17)
scripts/build.mjs reads the workspace list from package.json and opens each
one, so it does not get npm's forgiveness.
A scoped name is two directories, not one, and three things counted the old
depth: the project references and compiled sources are one `../` further out,
postbuild copies from the channels path that moved with it, and the ignore
globs read platform/*/lib, which stops matching one level down and would have
committed 37,093 lines of build output.
The lockfile gains the workspace entry that its absence had left out.
Co-authored-by: Hanzo Dev <[email protected]>
|
||
|
|
65d285cc8e |
Ask npm for the icon package by the name npm has
compass-icons is published by Mattermost and consumed from the registry; this tree contains no source for it and webapp/platform has no such workspace. Under our scope npm has nothing to fetch: 404 Not Found - GET https://registry.npmjs.org/@hanzoteam/compass-icons/-/compass-icons-0.1.61.tgz The lockfile is the proof of what it is. Its integrity for the renamed entry is sha512-Xq7QHyxq7NPljgqlzwRp6tWGe5s…, byte for byte the integrity npm publishes for @mattermost/[email protected] — one tarball, filed under a name nobody serves. The install then succeeds on any machine whose npm cache already holds that tarball, because a cache hit by integrity never asks the registry, and fails on every clean one. That is the whole distance between a developer's box and a build node: `npm ci --cache <fresh dir>` fails on master and passes here. Same rule the scope change already applied to calls-common and desktop-api: a package we merely consume keeps the name its publisher gave it. Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
c671872b0b |
The packages this repo publishes carry our scope
webapp/platform publishes six packages and the channels app carries a seventh in-tree, and every one of them went out under @mattermost — types, client, components, shared, eslint-plugin, compass-icons and mattermost-redux — while the plugin that consumes them already asks for @hanzoteam/types and @hanzoteam/client by name. It could not build: it was asking a registry for a package nobody publishes. They are @hanzoteam/* now, and mattermost-redux is @hanzoteam/redux, moved to src/packages/@hanzoteam/redux so the directory matches the alias that always pointed there. @mattermost/calls-common and @mattermost/desktop-api stay. Those are published by someone else and resolved from npm; renaming a package we merely consume would name a thing that does not exist. The jest moduleNameMapper entries needed doing by hand — they spell the scope inside a regex, so a sweep over the plain name walks straight past them, and a missed one fails at require time rather than at build. |
||
|
|
0a32777642 |
the published name does not exist in this tree, so it is applied at the boundary
The client's source imported @hanzoteam/types, the name the sibling is PUBLISHED under, so that the emitted declarations would name what a consumer installs. But that package does not exist inside this monorepo — the workspace is @mattermost/ types — and tsconfig `paths` hid it: paths satisfy the type checker and change nothing tsc emits, so the 11 value imports among the 71 compiled into requires for a module no workspace provides. Type checking was green and the web app could not be bundled at all: "Can't resolve '@hanzoteam/types/client4'". So the rename moves to publish.mjs, which is where the other two renames already live and which is the only place that knows the published name is in play. The same OURS map is now read a third time, over the staged build output, matching a module specifier rather than a substring — @mattermost/typesNOTAPACKAGE is left alone. One name in the tree, one rename at the boundary, and the published .d.ts still names @hanzoteam/types so its types do not degrade to `any`. The image's Go stage moves to 1.26.6 because go.work now asks for >= 1.26.5 and the builder runs GOTOOLCHAIN=local, which does not fetch one: the base image has to carry it or the build stops before compiling anything. Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
811885d271 |
webapp: internal workspace pins are carets, not exact versions
Four packages pinned their siblings at exactly 11.11.0. A workspace link holds only while the pin matches the sibling's version, so the moment client and types took a patch, npm unlinked them and went looking for @mattermost/[email protected].0 on a registry that has never had it — the install failed with ETARGET naming a package that lives in this repo. Same fix as the published peer one commit ago, and the same reason: an exact pin on something that moves is a pin on a version that will not exist. `tsc -b` in channels reports 380 errors before and after this change, identically — that state predates it and is untouched here. Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
ec8ad18084 |
platform: stop our own packages capping their consumers
Every package here declared `typescript: "^4.3.0 || ^5.0.0"` and client pinned its sibling exactly (`@mattermost/types: "11.11.0"`). Both are upstream's monorepo conventions and both are wrong once the packages are published: the first makes OUR packages the thing blocking a consumer from moving to a newer compiler, and the second breaks every consumer the moment types ships a patch. Measured, not theorised — the agents plugin could not install TypeScript 6 and npm named us as one of the reasons, beside @typescript-eslint and ts-jest. These ship declarations, not a runtime a compiler can break, so the peer is now `>=4.3` with no ceiling, and the sibling is a caret. A package of ours should never be the reason someone else is stuck. client 11.11.2 -> 11.11.4, types 11.11.0 -> 11.11.1, shared and redux widened in source (unpublished — they have no built output here and nothing consumes them yet). Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
b20ac0e9d0 |
client: import @hanzoteam/types, so the published declarations resolve
The package is published as @hanzoteam/client and its .d.ts imported @mattermost/types — a package a consumer of ours does not install. TypeScript writes a module specifier exactly as authored, so all 71 of them survived into the published declarations, and on the far side every type they carry silently degraded to `any`. The plugin saw it as four implicit-any errors in files that never mentioned the client, which is the shape this class of bug always takes: the failure surfaces nowhere near the package that caused it. Renaming the name and not what the name points at is the same mistake the peer dependency had one commit ago. The source imports @hanzoteam/types now, and both tsconfigs point the BUILD at the sibling package, which is still named @mattermost/types in this monorepo — a mapping that retires itself when the monorepo rename lands. Emitted declarations: 71 of 71 now name @hanzoteam/types. client 11.11.1 -> 11.11.2. Verified from the consumer: typecheck clean, webpack bundle clean, eslint clean, and a cold-cache `npm ci` that succeeds with the read-only token and fails E401 without it. Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
2d6d378509 |
platform: publish the @hanzoteam packages from a script, and fix client's peer
@hanzoteam/[email protected].0 was unusable from the moment it was published: it carried a peerDependency on @mattermost/types at THIS fork's version, 11.11.0 — a coordinate that exists in no registry, ours or upstream. npm 7+ installs peers automatically, so every `npm i @hanzoteam/client` died with ETARGET. Nothing in the source was wrong; the hand-run rename was applied to the package's name and not to what its name points at. publish.mjs makes that class of mistake unrepresentable rather than merely fixed. One map says which packages this fork publishes and what each is called, and it is read twice — once to rename the package, once to rewrite its dependencies — so a dependency is renamed if and only if we publish it. The webapp also depends on four @mattermost packages we do not fork (desktop-api, calls-common, use-external-link, no-dispatch-getstate); those resolve from public npm and are left alone, which a blanket prefix substitution would have broken. It publishes from a staging copy, so a failed run cannot leave a rewritten manifest in the tree, and the credential it writes lives in that copy and dies with it. client 11.11.0 -> 11.11.1, republished. Verified with the read-only token a build holds: [email protected].1, [email protected].0 and [email protected] install clean, and client now peers on @hanzoteam/types. Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
12561d9f00 |
signing out ends the session at the issuer too
Sign-out forgot the session here and stopped: it revoked the server session, cleared the cookies, and pushed a route. The issuer's session was never told, and while it stands the next visit is admitted with no prompt — so the person appeared never to have left, which is what "logout is broken" means. Ending it has to be a navigation. The issuer keeps its session in a SameSite=Lax cookie, which a browser presents on a top-level navigation and withholds from anything a script sends, so a request would arrive with no session attached, find nothing to end, and answer 200 regardless. A route push never leaves the document at all, so it could not work by construction. The address is derived from the authorize endpoint rather than configured beside it, the same way AcceptsPassword derives from one switch: authorize and logout are siblings at the issuer, so a brand pointing this server at its own hanzo.id carries the matching logout with it and the two cannot be set to disagree. When this server owns the session the address is empty and the push is the whole story. |
||
|
|
c85589eb27 |
serve the API at /v1/workspace
The base moves off /v1/team for two reasons that point the same way. It collided: /v1/team on hanzo.team is a carve-out routed to the older cloud service, so the moment this image shipped its own API would have been answered by something else -- a 404 on our own endpoints, on the host we serve from. And workspace is the better noun. It is what the product calls itself in its own words, and it does not read as though the API belongs to one team among several. No /api/ anywhere in a path we own. Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
6015136ac4 |
Move the webapp client to the /v1/team base
Every versioned route in the client funnels through getBaseRoute, so the urlVersion field is the only place the base is written and the whole surface follows from it. Two callers had escaped that funnel and would have kept requesting the old path: the log download link built its href from the host plus a hand-written prefix, and the file-type helper recognised a proxied image by matching the literal base, which decides whether an attachment renders as an image at all. Both now read the base from the client. The dev server proxied /api to the server, which forwarded the API and carried the websocket upgrade. Nothing is served under /api any more, so that entry moves to /v1 or local development loses both. Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
6e62296b89 |
keep the desktop token a token, and stop tagging other people's links
Renaming the product caught two strings that are protocol, not prose:
- isDesktopApp matched the User-Agent the desktop client sends, so
"Hanzo Team" with a space broke detection outright. The token is
HanzoTeam -- one word, because that is what a UA token is -- and the
label stays readable. Every fixture spells it the same way.
- Outbound links carried utm_source=mattermost and were tagged only on
mattermost.com. They now carry our source, on our domains; a link to
a site we do not own leaves without our parameters attached.
The rest is test truth catching up: the default theme is hanzo, a job
title in a fixture gained a word so its suffix suggestions gained an
entry, and 373 snapshots re-recorded. Those were regenerated under
TZ=Etc/UTC, which the project's test script sets and I had initially
missed -- the first pass baked local time into them.
Co-authored-by: Hanzo Dev <[email protected]>
|
||
|
|
021d8848d6 |
show one sign-in button, and let it say Hanzo
The login and signup screens read the renamed slot, route to /oauth/hanzo, and draw the Hanzo mark instead of the GitLab glyph -- BrandMark rather than a second copy of the same five paths. The GitLab entry in the admin console was a feature-discovery panel: a teaser that exists to sell a licence, not a setting anyone can change. It goes with the rest of the upsell. Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
60af6c5c86 |
Add weekly recurring scheduled posts (#37746)
* Add weekly recurring scheduled posts. Extend scheduled posts so users can schedule weekly repeats and keep the series healthy across sends, reschedules, and UI updates instead of falling back to one-shot behavior. Made-with: Cursor * Add Playwright coverage for recurring scheduled posts. Cover weekly recurring scheduled messages in the scheduled-messages spec so the recurring UI and reschedule flow stay protected without adding a separate test surface. Made-with: Cursor * Fix recurring scheduled post CI failures. Resolve the initial lint and formatting issues and renumber the new scheduled-post migration so it no longer collides with master during Postgres-backed test setup. Made-with: Cursor * Sync recurring scheduled post translation files. Regenerate the affected English translation catalogs so the recurring scheduled post strings match the source extraction order expected by CI. Made-with: Cursor * Fix recurring scheduled post review follow-ups. Preserve overdue cleanup behavior during weekly catch-up, defer delete websocket events until deletion succeeds, and address the remaining migration and UI review nits. Made-with: Cursor * Address recurring scheduled post review feedback Made-with: Cursor * Fix recurring scheduled post CI checks Made-with: Cursor * Make pending scheduled post keyset cursor index-scannable EXPLAIN ANALYZE on a 5M-row ScheduledPosts table showed the pure OR-form cursor forced Postgres to scan idx_scheduledposts_pending_scheduled_at_id from the top on every page (~383ms/page, ~2M rows filtered). Keeping the ScheduledAt <= beforeTime bound outside the tie-break restores the index boundary (~0.12ms/page). Adds storetest coverage for cursor pagination. Co-authored-by: nick.misasi <[email protected]> * Address code quality review findings for recurring scheduled posts Server: - Replace silent-fallback AdvanceWeeklyScheduledNextOccurrence with error-returning ScheduledPost.ComputeNextScheduledAt; a recurring post whose timezone fails to load is now routed through the failed-post path instead of being reposted every job run or silently deleted - Add ScheduledPost.IsRecurring and partition batches in processScheduledPostBatch; advance/delete now run independently so a store failure in one path can't cause reposts in the other - Drop dead generality in UpdateRecurringScheduledPosts (only ScheduledAt varies per row; ErrorCode/ProcessedAt are constants) - Simplify redundant repeat-type condition in GetPendingScheduledPosts Webapp: - Add shared isRecurringScheduledPost helper, replacing six scattered repeat_type === 'weekly' literals - Recurrence timezone is now simply the scheduler's current timezone; removes initialRepeatTimezone/effectiveTimezone plumbing and the modal-label/picker timezone mismatch - Single enforcement point for hiding send-now on recurring posts - Use canonical getTeamIdByChannelId at both SCHEDULED_POST_UPDATED dispatch sites; rewrite errorsByTeamId update case as remove-then-add and add reducer tests Co-authored-by: nick.misasi <[email protected]> * Simplify recurring scheduled post code per review - End a recurring series when its channel no longer exists instead of advancing it forever (matches the one-shot channel-not-found handling) - Collapse the errorsByTeamId SCHEDULED_POST_UPDATED case into the identical SINGLE_SCHEDULED_POST_RECEIVED case (a scheduled post's team can't change) and combine duplicate byId cases; preserves state references on no-op updates - Drop no-op timezone conversions in ComputeNextScheduledAt - Remove redundant checkbox aria-label (label htmlFor already names it) - Schedule new job tests in the past instead of sleeping a real second - Remove redundant test assignment and e2e positional boolean Co-authored-by: nick.misasi <[email protected]> * Remove slop from recurring scheduled post changes - Drop the business-rule CHECK constraint from the recurrence migration; no other migration enforces model-layer validation in the database, and BaseIsValid plus the job's failure handling already own it - Fold the standalone repeat-validation test file into the existing TestScheduledPostBaseIsValid, matching its conventions - Revert unrelated benchmark modernization in utils_test.go - Drop an unneeded cast, unused fixture fields, and naming/assertion inconsistencies in tests Co-authored-by: nick.misasi <[email protected]> * Regenerate ScheduledPostStore mock with mockery ordering Co-authored-by: nick.misasi <[email protected]> * Address CodeRabbit review feedback - Reject the host-dependent 'Local' value for RepeatTimezone; recurring schedules need a fixed zone (UTC or IANA name) - Hide reschedule for deactivated DMs, matching send-now eligibility Co-authored-by: nick.misasi <[email protected]> * Resolve scheduled post team bucket from existing state when channel is unloaded An admin can reschedule before fetchMissingChannels resolves, in which case deriving the team from the channel returns undefined and the update was misfiled under directChannels. getScheduledPostTeamId falls back to the byTeamId bucket that already holds the post. Co-authored-by: nick.misasi <[email protected]> * Retrigger CI after transient enterprise npm network failure Co-authored-by: nick.misasi <[email protected]> * Clear hover/focus before asserting scheduled post header details in e2e The drafts panel hides its timestamp/tag info section while hovered or focus-within; after the reschedule modal closes, focus returns to the row and the 'Repeats weekly' tag assertion saw a hidden element. Co-authored-by: nick.misasi <[email protected]> * Move recurrence columns into baseColumns and dispatch ComputeNextScheduledAt on repeat type Co-authored-by: nick.misasi <[email protected]> * Preserve recurrence when scheduled post updates omit repeat fields Co-authored-by: nick.misasi <[email protected]> * Disallow file attachments on recurring scheduled posts Co-authored-by: nick.misasi <[email protected]> * Stop pinning the channel indicator for recurring-only scheduled posts Co-authored-by: nick.misasi <[email protected]> * Re-home nullable-Type comment onto baseColumns Co-authored-by: nick.misasi <[email protected]> * Gate recurring scheduled posts behind a default-off feature flag Co-authored-by: nick.misasi <[email protected]> * Move presence-preservation rationale to the copy site Co-authored-by: nick.misasi <[email protected]> * Extract draftHasAttachments helper and require allowRecurring at single-caller layers Co-authored-by: nick.misasi <[email protected]> * Return null from the indicator selector when nothing should show Co-authored-by: nick.misasi <[email protected]> * Gate only recurrence transitions and preserve existing series in the modal Co-authored-by: nick.misasi <[email protected]> * Avoid err shadowing in scheduled post update handler Co-authored-by: nick.misasi <[email protected]> * Disable the repeat weekly checkbox with a tooltip when the message has attachments Co-authored-by: nick.misasi <[email protected]> * Return an explicit disposition from postScheduledPost The batch loop inferred 'channel permanently gone' from the one return path with a nil error and an error code set - an invariant a future change could silently break, deleting recurring series by accident. postScheduledPost now returns posted/failed/unsendable explicitly, the switch refuses to delete on an unhandled disposition, and a test pins that both recurring and one-shot posts in a nonexistent channel are permanently deleted. Co-authored-by: nick.misasi <[email protected]> * Fix Repeat weekly attachments tooltip centering on the modal row (#37927) Shrink-wrap the repeat checkbox row so WithTooltip anchors to the control/label instead of the full modal body width. --------- Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
a50f2ac4fd |
[MM-69557] Fix post actions menu not closing on outside click in mobile view (#37394)
* [MM-69557] Fix post actions menu not closing on outside click in mobile view
In the narrow/mobile-responsive layout the post actions ("…") menu renders
as a full-height GenericModal. React-bootstrap only dismisses the modal when
its outer container is clicked directly, but the full-height dialog element
intercepted every click in the dimmed area around the menu, so clicking
outside never closed it.
Let clicks on the empty dialog area fall through to the modal container via
pointer-events (scoped to .menuModal) so clicking outside dismisses the menu,
matching standard popup behavior, while keeping the menu itself interactive.
Co-authored-by: mattermost-code <[email protected]>
* [MM-69557] Add E2E test for mobile post actions menu outside-click dismissal
Adds a Playwright spec covering the mobile/narrow layout where the post actions
("…") menu renders as a full-screen modal. Verifies clicking the dimmed area
outside the menu (both above and below the menu list) dismisses it, and that
selecting a menu item still works. The outside-click test fails without the
pointer-events fix, so it is coupled to the real behavior.
Co-authored-by: mattermost-code <[email protected]>
* [MM-69557] Use type-only import for PlaywrightExtended in mobile dot menu spec
Satisfies the @typescript-eslint/consistent-type-imports lint rule.
Co-authored-by: mattermost-code <[email protected]>
* [MM-69557] Don't treat pointer-events: none as non-focusable in focus trap
The mobile menu fix sets pointer-events: none on .menuModal (restoring
auto on .modal-content). useFocusTrap's isElementVisible() walked the
ancestor chain and rejected any element under a pointer-events: none
ancestor, which broke keyboard focus trapping inside the modal.
pointer-events only affects mouse/touch hit-testing, not keyboard
focusability, and can be re-enabled on descendants, so remove it from
the visibility check. Adds a regression test.
* Address PR feedback: make focus-trap test spy cleanup exception-safe
---------
Co-authored-by: mattermost-code <[email protected]>
Co-authored-by: Hanzo Dev <[email protected]>
|
||
|
|
6a87848c45 |
[MM-70140] Remove experimental AD/LDAP login button color settings (#37855)
* [MM-70140] Remove experimental AD/LDAP login button color settings Remove the dead-code LdapSettings.LoginButtonColor / LoginButtonBorderColor / LoginButtonTextColor experimental settings. These values were plumbed into the client config but never consumed by the web or mobile clients, so no AD/LDAP login button was ever rendered or colored. Removes the fields from the server config struct and defaults, the client config payload, the Admin Console Experimental Features section, the webapp config type, related en.json strings, API definitions, docs, and test/default config fixtures. Co-authored-by: mattermost-code <[email protected]> * [MM-70140] Add test guarding removal of LDAP login button color keys Assert that GenerateLimitedClientConfig still emits LdapLoginFieldName under an LDAP license but never emits the removed LdapLoginButtonColor/BorderColor/ TextColor keys, guarding against reintroduction of the dead settings. Co-authored-by: mattermost-code <[email protected]> * [MM-70140] Remove LDAP login button colors from webapp LdapSettings type Keep AdminConfig LdapSettings in sync with the server model after the experimental AD/LDAP login button color settings were removed. Co-authored-by: mattermost-code <[email protected]> * [MM-70140] Update admin console index test after LDAP color removal Drop experimental/features from the ldap search expectation now that the AD/LDAP login button color settings are no longer under Experimental. Co-authored-by: mattermost-code <[email protected]> * Address PR feedback: remove test for removed LDAP login button color settings Per @lieut-data's review, drop TestGenerateLimitedClientConfigOmitsLdapLoginButtonColors; there's no need to perpetually test that a removed feature stays removed. * chore: retrigger CI after GitHub Actions service outage Co-authored-by: mattermost-code <[email protected]> * chore: retrigger CI after Actions service recovery Co-authored-by: mattermost-code <[email protected]> --------- Co-authored-by: mattermost-code <[email protected]> Co-authored-by: mattermost-build <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
33e7012eb3 |
[MM-70141] Remove dead experimental SAML login button color settings (#37857)
* [MM-70141] Remove dead SAML login button color settings
SamlSettings.LoginButtonColor / LoginButtonBorderColor / LoginButtonTextColor
were never consumed by any client (web or mobile) — the values reached the
client config but were never applied to the SAML login button, which renders
with fixed themed CSS. Remove the settings from the server model, client config
payload, Admin Console Experimental Features, webapp config types, config
fixtures, and documentation. The SAML login button label (LoginButtonText)
is retained.
Co-authored-by: mattermost-code <[email protected]>
* [MM-70141] Update admin console index test after SAML color removal
Searching for "saml" no longer matches Experimental Features once the
unused SAML login button color settings are removed from that section.
Co-authored-by: mattermost-code <[email protected]>
* Address PR feedback: remove unnecessary SAML color removal test case
Per reviewer feedback, drop the GenerateClientConfig test case that
asserted the removed SAML login button color props are absent. There is
no value in perpetually testing that a deleted feature stays deleted.
* chore: retrigger CI after transient Actions outage
Previous Server CI / API / Web App CI failures on
|
||
|
|
52c4931af5 |
[MM-69587] Remove CustomProfileAttributes feature flag (#37389)
* [MM-69587] Remove CustomProfileAttributes feature flag Remove the CustomProfileAttributes feature flag and all associated conditional gating, leaving custom profile attributes permanently enabled (still Enterprise-license gated). Co-authored-by: mattermost-code <[email protected]> * [MM-69587] Update admin sidebar snapshots for permanent CPA User Attributes now appears based solely on license tier since the feature flag no longer gates it. Co-authored-by: mattermost-code <[email protected]> * Update channel_header snapshot styled-components icon hash after master merge Align pending-join-requests snapshot with the Icon-clPswv hash used by all other ChannelHeader snapshots; CI received this after merging discoverable channels from master. Co-authored-by: mattermost-code <[email protected]> * chore: retrigger CI after unrelated Postgres shard 1 flake Server CI Postgres (shard 1) failed on TestCheckLdapUserPasswordConcurrency (mock DoLogin panic under concurrent On/Called) and a one-shot TestLicenseFromBytesEnvironmentMismatch mismatch that passed on re-run. Neither touches CustomProfileAttributes removal; empty commit to retrigger. Co-authored-by: mattermost-code <[email protected]> --------- Co-authored-by: mattermost-code <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
ba55e8631f |
Post attributes feature flag group (#37829)
* Add post attribtues feature flag and helpers * Adds post attributes group registration --------- Co-authored-by: Miguel de la Cruz <[email protected]> |
||
|
|
4ace37d5fb |
Fix: mobile menu backdrop fails to dismiss modal (#37430)
* Fix: mobile menu backdrop fails to dismiss modal * test: add mobile RHS focus functional spec * fix: stabilize mobile RHS focus tests and handle keyboard trap * fix: refine mobile menu tests and scope focus trap to backdrop interaction * e2e: test mobile menu backdrop dismissal with click action * style: fix trailing whitespace and missing eol newline --------- Co-authored-by: Mattermost Build <[email protected]> |
||
|
|
07fa4509dc |
Update latest minor version to 11.11.0 (#37827)
Automatic Merge |
||
|
|
e463bf8024 |
Fix UI flash when closing the Decision details modal in permission policy simulation (#37141)
Automatic Merge |
||
|
|
ba37d0f241 |
Mm 70056 missing info banner team abac team admin (#37790)
* [MM-70056] Return imported parent policies so team admins see the system policy banner * [MM-70055] Resolve parent policy expressions for team admins in the mode-flip modal * [MM-70057] Let team admins read team sync jobs for their own team policy |
||
|
|
501beb1365 |
Mm 69832 abac review nits (#37640)
* MM-69831 - Add configurable interval for the ABAC membership sync schedulers * MM-69832 - Hoist the ManageSystem permission check in createAccessControlPolicy * MM-69832 - Add policy-scoped search term to the ABAC team invite candidate endpoint * MM-69832 - Switch strict ABAC invite picker to server-side policy-scoped typeahead * MM-69832 - Rename the ABAC invite candidate search param to term for consistency * MM-69832 - Gate ABAC invite full-name term search behind ShowFullName for non-admins * MM-69832 - Add API and jest coverage for the ABAC invite term search and full-name privacy gate * MM-69832 - Clean up comments |
||
|
|
bca64fb658 |
MM-69912: let plugins bring their own editor extensions and read structured content (#37678)
* MM-69912: let plugins bring their own editor extensions and read structured content * lint * Claude PR feedback * Claude AI review second pass * gate autosave on load failure and harden json-mode parse path * fix stale hasContentError handle and scope the error latch to load * shorten required comment --------- Co-authored-by: Nevyana Angelova <[email protected]> Co-authored-by: Mattermost Build <[email protected]> |
||
|
|
d6eb1b5b75 | [MM-69734] Add no session data, ensure session attributes are not leaked through evaluation trace to non sysadmins (#37600) | ||
|
|
40d1a1f3a8 | MM-69612: Add opt-in EnableAuditLogging setting for ABAC (#37322) | ||
|
|
a64152606a |
Add discoverable private channels request-to-join UX (#37078)
* Add discoverable private channels request-to-join UX Let users mark private channels discoverable, find them in Browse Channels, submit and withdraw join requests, and track status via My pending requests instead of DM notifications. * Drop unrelated server changes from the branch Restore server files to master; this PR is webapp-only and builds on the existing discoverable-channels server implementation. * Close Request to Join modal immediately on submit Drop the in-modal pending state so the modal closes as soon as the request is sent, rather than flipping into a "sent / Withdraw" view before disappearing. Browse Channels rows already handle the post-send state (Requested pill + Withdraw), so the modal's pending branch was redundant and visually noisy. * Fix lint in channel_settings_info_tab test Split multi-prop JSX onto separate lines and remove the trailing blank line inside the Discoverable toggle describe block. Resolves the react/jsx-max-props-per-line and padded-blocks errors that were failing check-lint and cascading into a skipped build / failed Spinwick. * Fix stylelint property order in request_join_channel_modal SCSS Move min-width before align-items in .RequestJoinChannelModal__header to satisfy stylelint's order/properties-order rule (dimensions before flex alignment). * Restore unintended deletions from original branch commit The initial PR commit accidentally reverted master-side changes in several shared files when the AI agent regenerated them. The rebase onto current master preserved those reverts. Restore each piece while keeping the branch's discoverable additions: - client4.ts: previewLicense method - websocket_actions.ts: handleFileUploadRejected function + switch case - websocket_message.ts / websocket_messages.ts: FileUploadRejected type - config.ts: License type fields (expires_at, customer required, sku_short_name, is_gov_sku) - constants.tsx: CHANNEL_MENTION_AUTO_FOLLOW notification section - en.json: master keys (admin.license.*, admin.access_control.*, etc.) restored; discoverable keys re-inserted using the Mattermost i18n formatter sort rule (case-insensitive, '_' before '.') - package-lock.json: reset to master * Sync webapp package-lock.json with master to fix CI install The branch carried a stale package-lock.json pinning [email protected].0 while package.json (identical to master) declares [email protected].4. npm aborted with an ERESOLVE peer conflict (@typescript-eslint/[email protected].0 peers eslint@^8.56.0), failing check-lint and the E2E check before any code ran. This branch adds no dependencies, so the lockfile is restored to match master. * Fix join-request pending count double-decrement and prune stale pending map Decrement countsByChannel from the actual pending->terminal transition instead of the incoming status alone, so the acting admin's optimistic dispatch plus the server's WebSocket echo of a single approve/deny no longer double-decrements the badge. Make CREATED count-idempotent for re-delivered events. Rebuild myPendingByChannel from the authoritative full list on RECEIVED_MY_CHANNEL_JOIN_REQUESTS so a request that resolved while the client missed the WS event no longer lingers as stale. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> * Surface discoverable channels in Browse without requiring a search getChannels only returns public channels, so non-member discoverable private channels (and the Discoverable filter) were empty until the user typed a search term. Fetch them on mount via an empty-term non-admin search (already ABAC-filtered server-side), fold them into the default All list and the Discoverable filter, and resolve My pending requests rows against them. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> * Browse row cleanup: drop unused action, keep Withdraw if discoverability revoked Remove the requestJoinChannel action that was wired into Browse Channels but never called (the confirmation modal dispatches it directly). Show the Withdraw affordance for any non-member with an open request even after an admin flips the channel's discoverable flag off, instead of falling through to a Join button the server rejects for a private channel. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> * Fix discoverable-toggle a11y labels and gate settings toggle on in-progress type - Description <label>s reused aria-label={discoverableTitle}, so screen readers announced the title twice and skipped the description; point them at discoverableDescription (channel settings + new channel modal). - Gate the channel-settings discoverable toggle on the in-progress channelType selection rather than the saved channel.type, so it appears immediately during a Public->Private conversion, matching the New Channel modal. The save flow already applies the privacy conversion before the discoverable patch. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> * Guard Browse request/withdraw re-entry and make Withdraw keyboard-accessible Ignore row clicks that would re-trigger request/withdraw while one is already in flight (the row bypassed the disabled button), and disable the buttons whenever any request/withdrawal is pending. Drop tabIndex={-1} from the Withdraw button: pending rows are a no-op on Enter, so the button was the only action and needed to stay keyboard-focusable. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> * Persist untracked join-request updates so duplicate terminal events stay idempotent When a terminal CHANNEL_JOIN_REQUEST_UPDATED arrives for a row not yet in byChannel (only the count was loaded), store it instead of leaving the list unchanged. A re-delivered terminal event then finds it as terminal and no longer decrements countsByChannel a second time. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> * Memoize filtered pending join-request selectors getPendingChannelJoinRequests and getMyPendingJoinRequestList ran an unmemoized .filter() on every call, returning a new array reference each time and causing avoidable re-renders in any connected consumer. Wrap both in createSelector. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> * Address review: drop redundant done() guards and harden withdraw error message done is a required callback, so the if (done) guards in handleRequestToJoin and handleWithdrawRequest were dead. Fall back to server_error_id / a generic string when the withdraw error has no message, since error.message isn't guaranteed at runtime. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> * Address review: use a class for the discoverable row indicator The indicator rendered id='discoverableIndicatorContainer' on every discoverable row, producing duplicate ids in the DOM when more than one discoverable channel is listed. Switch it to a class (and update the SCSS selector + test) so the markup stays valid. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> * Add discoverable private channels admin join-request queue UX (#37082) QA deferred to the base branch * Fix Quick Switch join flow for discoverable channels in recent/default lists (MM-68764) The discoverableNonMember / hasPendingJoinRequest flags were only set on Quick Switch search results, so a discoverable private channel surfaced via the Recent or default (no-search-term) list fell through to the legacy private-channel join confirmation, which the server rejects for a non-member — leaving the user in a broken confirm/error loop. Extract the detection into a shared applyDiscoverableFlags helper and run it on every channel-list path (search via formatGroup, recent + unread via wrapChannels) so the Request to Join flow fires consistently. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> * Bound concurrency of sidebar join-request count prefetch Address CodeRabbit: the effect fired one count request per manageable discoverable channel simultaneously. Process them in capped batches (5 at a time) and cancel in-flight work on unmount / dependency change so a user who manages many channels doesn't trigger an unbounded request burst. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> * Add ChannelHeader snapshot test for pending join requests Address CodeRabbit: cover the join-request badge's conditional rendering with a hasPendingJoinRequests=true snapshot case. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> * Add e2e tests for discoverable private channels request-to-join UX Cover common use cases behind the DiscoverableChannels feature flag: a non-member requesting to join a discoverable private channel from Browse Channels and withdrawing, and selecting a discoverable channel from Find Channels (Cmd+K) routing to the Request to Join modal rather than the legacy join. Add supporting Browse Channels modal page-object helpers. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> * Address review: locate Browse Channels rows by display name in e2e helpers Per the Playwright suite conventions, filter rows by visible display name (.more-modal__row + hasText) instead of data-testid, and pass channel.display_name from the spec. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> * Reveal Browse Channels row actions on keyboard focus (:focus-within) Address review: the Withdraw button is keyboard-focusable (tabIndex removed earlier) but the row action buttons are visibility:hidden and only revealed on :hover / row :focus. Tabbing to the Withdraw button moves focus off the row, so the buttons hid again. Add :focus-within so the actions stay visible while any child button holds keyboard focus. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> * Route discoverable channel URL / mention-link navigation to Request to Join Address review: opening a discoverable private channel a user isn't a member of via a pasted channel URL or a ~channel mention link fired the legacy 'Join private channel' confirmation, which attempts a direct join the server rejects, leaving the user on a broken channel view. When the feature flag is on and the channel is discoverable, redirect to a safe channel and open the Request to Join modal instead — matching Browse Channels and Quick Switch. Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> --------- Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> Co-authored-by: Mattermost Build <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
dc5b71c6ee |
MM-69828 - Fix ABAC team Access-tab stuck-public cards, parent-policy mode-flip count (#37558)
* MM-69828 - Fix ABAC team Access-tab stuck-public cards, parent-policy mode-flip count * Add fallback handling for parent-policy fetch failure in AccessTab component |
||
|
|
a65fabff69 |
[MM-67163] Scheduled Recaps (#35495)
* docs: map existing codebase with focus on recaps
- STACK.md - Technologies and dependencies
- ARCHITECTURE.md - System design and patterns
- STRUCTURE.md - Directory layout
- CONVENTIONS.md - Code style and patterns
- TESTING.md - Test structure
- INTEGRATIONS.md - External services
- CONCERNS.md - Technical debt and issues
* docs: initialize project
Scheduled AI Recaps — enable recurring scheduled recaps instead of only manual one-off summaries
* chore: add project config
Mode: yolo
Depth: standard
Parallelization: enabled
* docs: complete domain research for scheduled recaps
Research files:
- STACK.md: Job server patterns, ScheduledPosts reference implementation
- FEATURES.md: Table stakes vs differentiators, anti-features to avoid
- ARCHITECTURE.md: Component boundaries, data flow, 5-phase build order
- PITFALLS.md: Timezone, cluster, schema critical pitfalls
- SUMMARY.md: Synthesized findings and roadmap implications
Key findings:
- Use ScheduledPosts polling pattern, not JobServer schedulers
- Store user intent + pre-computed NextRunAt for efficient polling
- Leverage existing recap worker, extend with time_period support
- Critical: Get timezone/schema right in Phase 1 to avoid migrations
* docs: define v1 requirements
35 requirements across 5 categories:
- Scheduling (7)
- Management (8)
- Backend Infrastructure (10)
- Frontend Wizard (7)
- Frontend Scheduled Tab (7)
Topic-based recaps deferred to v2
* docs: create roadmap (5 phases)
* docs(01): capture phase context
Phase 01: Database Foundation
- OpenCode discretion on implementation patterns
- Implicit requirements from downstream phases noted
* docs(01-database-foundation): research phase domain
Phase 1: Database Foundation
- Analyzed existing Recap and ScheduledPost patterns
- Documented schema design for recurring schedules
- Identified timezone/DST handling approach using IANA zones
- Catalogued store interface patterns
- Documented common pitfalls and anti-patterns
* docs(01): create phase plan for database foundation
Phase 01: Database Foundation
- 2 plans in 2 waves
- Wave 1: Model + migration (01-01-PLAN.md)
- Wave 2: Store layer + tests (01-02-PLAN.md)
- Ready for execution
* feat(01-01): create ScheduledRecap model with DST-aware NextRunAt computation
- Add ScheduledRecap struct with all required fields for schedule configuration
- Add day-of-week bitmask constants matching Go's time.Weekday (Sunday=0)
- Add channel mode constants (specific, all_unreads)
- Add time period constants (last_24h, last_week, since_last_read)
- Implement ComputeNextRunAt with timezone-aware scheduling using time.LoadLocation
- Implement IsValid for input validation
- Add PreSave/PreUpdate lifecycle methods
- Add Auditable method for audit logging
* feat(01-01): create database migration for ScheduledRecaps table
- Create ScheduledRecaps table with all required columns
- Add index for user queries (idx_scheduled_recaps_user_id)
- Add index for scheduler polling (idx_scheduled_recaps_next_run_at)
- Add composite index for efficient scheduler query (idx_scheduled_recaps_enabled_next_run)
- Add index for user + soft delete queries (idx_scheduled_recaps_user_delete)
- Add down migration to drop all indexes and table
* test(01-01): add unit tests for ScheduledRecap with DST edge cases
- Test day-of-week bitmask constants and operations
- Test ComputeNextRunAt for Monday-only, weekday, and every-day schedules
- Test timezone handling - different timezones produce different UTC millis
- Test DST spring forward edge case (March 2024) - Go normalizes non-existent times
- Test DST fall back edge case (November 2024) - Go uses first occurrence
- Test error cases: invalid timezone, invalid time format, zero days
- Test IsValid method for all validation rules
- Test PreSave and PreUpdate lifecycle methods
- Test Auditable method returns expected fields
- Fix ComputeNextRunAt to validate time format using regex before parsing
* docs(01-01): complete ScheduledRecap model and migration plan
Tasks completed: 3/3
- Task 1: Create ScheduledRecap model with constants and NextRunAt computation
- Task 2: Create database migration for ScheduledRecaps table
- Task 3: Add unit tests for ComputeNextRunAt with DST edge cases
SUMMARY: .planning/phases/01-database-foundation/01-01-SUMMARY.md
* feat(01-02): add ScheduledRecapStore interface to store.go
- Add ScheduledRecapStore interface with CRUD operations (Save, Get, Update, Delete)
- Add query operations (GetForUser, GetDueBefore)
- Add state update methods (UpdateNextRunAt, MarkExecuted, SetEnabled)
- Register ScheduledRecap() method in main Store interface
* feat(01-02): create SqlScheduledRecapStore implementation
- Implement CRUD operations (Save, Get, Update, Delete with soft delete)
- Implement GetForUser with pagination for user's scheduled recaps
- Implement GetDueBefore for scheduler polling query
- Implement efficient state updates (UpdateNextRunAt, MarkExecuted, SetEnabled)
- Handle JSON serialization/deserialization of ChannelIds array
- Follow existing patterns from recap_store.go
* feat(01-02): register ScheduledRecapStore in SqlStore
- Add scheduledRecap field to SqlStoreStores struct
- Initialize newSqlScheduledRecapStore in NewSqlStore
- Add ScheduledRecap() accessor method to SqlStore
* test(01-02): add comprehensive tests for ScheduledRecapStore
- Test CRUD operations (Save, Get, Update, Delete)
- Test GetForUser with pagination
- Test GetDueBefore scheduler query filtering
- Test state update methods (UpdateNextRunAt, MarkExecuted, SetEnabled)
- Test ChannelIds JSON serialization (array, empty, nil)
- All 13 test cases pass
* docs(01-02): complete ScheduledRecapStore plan
Tasks completed: 4/4
- Add ScheduledRecapStore interface to store.go
- Create SqlScheduledRecapStore implementation
- Register ScheduledRecapStore in SqlStore
- Create comprehensive store tests
SUMMARY: .planning/phases/01-database-foundation/01-02-SUMMARY.md
* fix(01): regenerate store mocks for ScheduledRecapStore
* docs(01): complete Database Foundation phase
Phase 1: Database Foundation
- 2 plans executed (model + store)
- 3 requirements complete (INFRA-01, INFRA-02, INFRA-10)
- Goal verified ✓
* docs(03): research phase scheduler integration domain
Phase 03: Scheduler Integration
- Standard stack identified (Mattermost job system)
- Architecture patterns documented (Scheduler + Worker pattern)
- Cluster-safe execution via leader-only scheduling
- Pitfalls catalogued (duplicate jobs, race conditions)
- Code examples from existing codebase patterns
* docs(02): create phase 2 API layer plans
Phase 02: API Layer
- 2 plans in 2 waves
- Plan 01 (Wave 1): App layer methods for CRUD + pause/resume
- Plan 02 (Wave 2): API handlers, routes, params, audit events
- Ready for execution
* feat(02-01): create App layer CRUD methods for ScheduledRecap
- Add CreateScheduledRecap with session-based userId, validation, NextRunAt computation
- Add GetScheduledRecap to retrieve by ID
- Add GetScheduledRecapsForUser with pagination
- Add UpdateScheduledRecap with NextRunAt recomputation when enabled
- Add DeleteScheduledRecap for soft delete
- Add PauseScheduledRecap to disable without deleting
- Add ResumeScheduledRecap with NextRunAt recomputation before enabling
- Regenerate store layer files for ScheduledRecapStore interface
* docs(02-01): complete App layer CRUD methods plan
Tasks completed: 3/3
- Regenerate store mocks (already complete from Phase 1)
- Create App layer file with CRUD methods
- Verify app layer interfaces (no interface file exists)
SUMMARY: .planning/phases/02-api-layer/02-01-SUMMARY.md
* feat(02-02): add audit event constants for scheduled recaps
- AuditEventCreateScheduledRecap for recap configuration creation
- AuditEventGetScheduledRecap for viewing single recap
- AuditEventGetScheduledRecaps for listing user recaps
- AuditEventUpdateScheduledRecap for configuration updates
- AuditEventDeleteScheduledRecap for recap deletion
- AuditEventPauseScheduledRecap for pausing execution
- AuditEventResumeScheduledRecap for resuming execution
* feat(02-02): add ScheduledRecapId to params and context
- Add ScheduledRecapId field to Params struct
- Parse scheduled_recap_id from URL path variables
- Add RequireScheduledRecapId validation method
* feat(02-02): add route registration for scheduled recaps
- Add ScheduledRecaps and ScheduledRecap routes to Routes struct
- Initialize route prefixes in Init function
- Add InitScheduledRecap call in initialization
* feat(02-02): create API handlers for scheduled recaps
- InitScheduledRecap registers all 7 API routes
- createScheduledRecap validates required fields and creates recap
- getScheduledRecap retrieves with authorization check
- getScheduledRecaps lists user's recaps with pagination
- updateScheduledRecap updates with ownership verification
- deleteScheduledRecap soft deletes with authorization
- pauseScheduledRecap disables execution with ownership check
- resumeScheduledRecap re-enables with NextRunAt recomputation
- All handlers include audit logging and feature flag check
* docs(02-02): complete API handlers plan
Tasks completed: 4/4
- Add audit event constants for scheduled recaps
- Add ScheduledRecapId to params and context
- Add route registration in api.go
- Create API handler file
SUMMARY: .planning/phases/02-api-layer/02-02-SUMMARY.md
* docs(02): complete API Layer phase
Phase 2: API Layer
- 2 plans executed in 2 waves
- 16/16 must-haves verified
- INFRA-05 through INFRA-09 complete
- Ready for Phase 3: Scheduler Integration
* docs(03): create phase plan for scheduler integration
Phase 03: Scheduler Integration
- 2 plans in 2 waves
- Wave 1: Job constant, scheduler, worker
- Wave 2: Job registration, App method
- Ready for execution
* fix(03): revise plan 03-02 Task 2 for worker context compatibility
- CreateRecapFromSchedule now creates recap directly via store
- Uses sr.UserId instead of rctx.Session().UserId (unavailable in worker)
- Creates JobTypeRecap job directly instead of delegating to CreateRecap
- Updated key_links to reflect store and job linkage
* docs(04): capture phase context
Phase 04: Scheduled Tab
- Implementation decisions documented
- Phase boundary established
- Figma references captured (123:62940, 123:19772)
* feat(03-01): add JobTypeScheduledRecap constant
- Add JobTypeScheduledRecap constant with value 'scheduled_recap'
- Add to AllJobTypes slice for job type validation
* feat(03-01): create ScheduledRecap scheduler
- Add Scheduler struct wrapping PeriodicScheduler
- 1-minute polling interval (SchedulerPollingInterval constant)
- Enabled when cfg.FeatureFlags.EnableAIRecaps is true
- ScheduleJob polls GetDueBefore for due recaps
- Creates job with CreateJobOnce for deduplication
- Job data: scheduled_recap_id, user_id, channel_ids, agent_id
* feat(03-01): create ScheduledRecap worker
- Define AppIface interface with CreateRecapFromSchedule method
- Use SimpleWorker pattern following recap/worker.go pattern
- Enabled when cfg.FeatureFlags.EnableAIRecaps is true
- Extract job data: scheduled_recap_id, user_id, channel_ids, agent_id
- Verify ScheduledRecap exists and is enabled before execution
- Call app.CreateRecapFromSchedule to create the actual recap
- Compute next run time using sr.ComputeNextRunAt
- Call MarkExecuted to update LastRunAt, NextRunAt, RunCount atomically
- Disable non-recurring schedules after execution
* docs(03-01): complete job system components plan
Tasks completed: 3/3
- Add JobTypeScheduledRecap constant
- Create scheduler implementation
- Create worker implementation
SUMMARY: .planning/phases/03-scheduler-integration/03-01-SUMMARY.md
* docs(04): create phase 4 plans - Scheduled Tab UI
Phase 04: Frontend - Scheduled Tab
- 4 plans in 4 waves
- Wave 1: TypeScript types + Client4 API methods
- Wave 2: Redux layer (action types, actions, reducer, selectors)
- Wave 3: ScheduledRecapItem component (card UI with toggle, menu)
- Wave 4: Scheduled tab integration with human verification
Covers requirements TAB-01 through TAB-07 and MGMT-01 through MGMT-08
* feat(03-02): add ScheduledRecap job registration in initJobs
- Register JobTypeScheduledRecap with worker and scheduler
- Import scheduled_recap package for job components
- Worker uses App interface for CreateRecapFromSchedule
- Scheduler polls for due recaps at 1-minute intervals
* feat(03-02): implement CreateRecapFromSchedule App method
- Create Recap from ScheduledRecap configuration
- Use sr.UserId instead of session (worker context has no session)
- Create recap record directly via store
- Create JobTypeRecap job to trigger processing
- Handle both specific channels and all_unreads mode
* docs(03-02): complete app integration plan
Tasks completed: 3/3
- Add job registration in initJobs
- Implement CreateRecapFromSchedule App method
- Verify full integration compiles
SUMMARY: .planning/phases/03-scheduler-integration/03-02-SUMMARY.md
* docs(phase-3): complete scheduler integration phase
* feat(04-01): add ScheduledRecap TypeScript types
- Add ScheduledRecap type matching Go model fields
- Add ScheduledRecapInput type for create/update operations
- Types exported via @mattermost/types/recaps
* feat(04-01): add Client4 scheduled recap route and imports
- Add getScheduledRecapsRoute() method returning /scheduled_recaps endpoint
- Import ScheduledRecap and ScheduledRecapInput types
* feat(04-01): add Client4 scheduled recap API methods
- createScheduledRecap: POST /scheduled_recaps
- getScheduledRecaps: GET /scheduled_recaps (paginated)
- getScheduledRecap: GET /scheduled_recaps/:id
- updateScheduledRecap: PUT /scheduled_recaps/:id
- deleteScheduledRecap: DELETE /scheduled_recaps/:id
- pauseScheduledRecap: POST /scheduled_recaps/:id/pause
- resumeScheduledRecap: POST /scheduled_recaps/:id/resume
* docs(04-01): complete TypeScript types and Client4 methods plan
Tasks completed: 3/3
- Add ScheduledRecap TypeScript type
- Add Client4 scheduled recap route helper
- Add Client4 scheduled recap API methods
SUMMARY: .planning/phases/04-scheduled-tab/04-01-SUMMARY.md
* feat(04-02): add scheduled recap action types
- GET_SCHEDULED_RECAPS_REQUEST/SUCCESS/FAILURE
- RECEIVED_SCHEDULED_RECAP and RECEIVED_SCHEDULED_RECAPS
- PAUSE_SCHEDULED_RECAP_REQUEST/SUCCESS/FAILURE
- RESUME_SCHEDULED_RECAP_REQUEST/SUCCESS/FAILURE
- DELETE_SCHEDULED_RECAP_REQUEST/SUCCESS/FAILURE
* feat(04-02): add scheduled recap Redux actions
- getScheduledRecaps: fetches paginated scheduled recaps
- pauseScheduledRecap: pauses a scheduled recap
- resumeScheduledRecap: resumes a paused scheduled recap
- deleteScheduledRecap: deletes a scheduled recap
* feat(04-02): add scheduled recaps to reducer
- Add scheduledRecaps to RecapsState type
- Handle RECEIVED_SCHEDULED_RECAP for single recap
- Handle RECEIVED_SCHEDULED_RECAPS for bulk updates
- Handle DELETE_SCHEDULED_RECAP_SUCCESS for removal
* feat(04-02): add scheduled recap selectors
- getScheduledRecapsState: base selector for raw state
- getAllScheduledRecaps: returns all scheduled recaps as array
- getActiveScheduledRecaps: filters enabled, non-deleted recaps
- getPausedScheduledRecaps: filters disabled, non-deleted recaps
- getScheduledRecapById: returns single recap by ID
* feat(04-02): update GlobalState type for scheduled recaps
- Import ScheduledRecap type from recaps
- Add scheduledRecaps field to recaps entity state
* docs(04-02): complete Redux store and actions plan
Tasks completed: 5/5
- Add scheduled recap action types
- Add scheduled recap Redux actions
- Add scheduled recaps to reducer
- Add scheduled recap selectors
- Update GlobalState type for scheduled recaps
SUMMARY: .planning/phases/04-scheduled-tab/04-02-SUMMARY.md
* feat(04-03): add i18n strings for scheduled recap UI
- Add scheduled tab label
- Add active/paused toggle states
- Add run stats strings (last run, run count, never run, next run)
- Add toast messages for pause/resume/delete
- Add kebab menu labels (edit, delete)
- Add delete confirmation modal strings
- Add empty state strings (title, description, cta)
- Add day formatting strings (weekdays, weekend, everyday, individual days)
- Add schedule format string
* feat(04-03): create useScheduleDisplay hook for schedule formatting
- Add bitmask constants matching Go model (Sun=1, Mon=2, etc.)
- formatDaysOfWeek: smart groupings (Every day, Weekdays, Weekends) or comma-separated
- formatTimeOfDay: locale-appropriate 12/24hr time from HH:MM
- formatSchedule: combines days and time with i18n format string
- formatNextRun: smart relative formatting (Today, Tomorrow, Day name, Date)
- formatLastRun: formatted date or 'Never run'
- formatRunCount: pluralized run count
* feat(04-03): create ScheduledRecapItem component
- Render card with title and schedule pattern subtitle
- Show next run time when schedule is active
- Toggle between Active/Paused states with pause/resume actions
- Run stats (last run, run count) appear on hover
- Kebab menu with Edit and Delete options
- Delete confirmation modal with FormattedMessage
- Use useScheduleDisplay hook for all formatting
* feat(04-03): add ScheduledRecapItem styles
- Card with border, radius, and hover state
- Flexbox layout with title/subtitle and actions
- Title with truncation (ellipsis) for long names
- Subtitle with metadata separator styling
- Run stats with opacity transition on hover
- Toggle button min-width for consistent sizing
- Kebab menu button hover state
* docs(04-03): complete ScheduledRecapItem component plan
Tasks completed: 4/4
- Add i18n strings for scheduled recap UI
- Create useScheduleDisplay hook for schedule formatting
- Create ScheduledRecapItem component
- Add ScheduledRecapItem styles
SUMMARY: .planning/phases/04-scheduled-tab/04-03-SUMMARY.md
* feat(04-04): create ScheduledRecapsEmptyState component
- Empty state displays when no scheduled recaps exist
- Shows illustration with icons, title, description
- CTA button to create first recap
- Supports disabled state when agents bridge is disabled
* feat(04-04): create ScheduledRecapsList component
- Renders empty state when no scheduled recaps exist
- Maps over scheduled recaps to render ScheduledRecapItem
- Passes edit and create handlers through to children
* feat(04-04): add Scheduled tab to main Recaps component
- Add Scheduled tab after Unread and Read tabs
- Fetch scheduled recaps on mount with getScheduledRecaps
- Display ScheduledRecapsList when on scheduled tab
- Wire up edit handler (opens create modal - Phase 5 adds pre-fill)
- Import scheduled_recap_item.scss for styling
* style(04-04): add SCSS styles for scheduled recaps
- Add .scheduled-recaps-list styles (flex column, centered, gap)
- Add .scheduled-recaps-empty-state styles (centered, illustration, text)
- Consistent with existing recap UI styling patterns
* docs(04-04): complete Scheduled tab integration plan
* docs(04): update STATE.md for phase 4 completion
* docs(04): complete Scheduled Tab phase
Phase 4: Scheduled Tab
- 4 plans executed across 4 waves
- 15/15 requirements verified
- Human verified UI works correctly
* docs(05): capture phase context
Phase 05: Enhanced Wizard
- Implementation decisions documented
- Phase boundary established
* docs(05): add research hints for component discovery
* docs(05): research phase domain for enhanced wizard
Phase 05: Enhanced Wizard - Frontend Implementation
- Standard stack identified (existing codebase components)
- Architecture patterns documented (multi-step modal, bitmask days)
- Pitfalls catalogued (timezone, validation, edit mode)
- Code examples from codebase referenced
* docs(05): create phase plan for enhanced wizard
Phase 05: Frontend - Enhanced Wizard
- 6 plans in 3 waves
- Wave 1: Redux actions, DayOfWeekSelector
- Wave 2: ScheduleConfiguration, Run once toggle
- Wave 3: Modal integration, Edit wiring
- Ready for execution
* feat(05-01): add action type constants for create/update scheduled recap
- CREATE_SCHEDULED_RECAP_REQUEST/SUCCESS/FAILURE
- UPDATE_SCHEDULED_RECAP_REQUEST/SUCCESS/FAILURE
* feat(05-02): create DayOfWeekSelector component
- Bitmask-based day selection matching server model
- Monday-first ordering for work schedule intuition
- XOR toggle for clean state management
- aria-pressed accessibility support
* feat(05-01): add createScheduledRecap async action
- Takes ScheduledRecapInput parameter
- Calls Client4.createScheduledRecap
- Dispatches RECEIVED_SCHEDULED_RECAP on success
- Follows existing pauseScheduledRecap pattern
* feat(05-02): add DayOfWeekSelector styles
- Flexbox layout with 8px gap between buttons
- 40x40px day buttons with hover states
- Selected state uses button-bg color
- Error and disabled state styling
* feat(05-01): add updateScheduledRecap async action
- Takes id and ScheduledRecapInput parameters
- Calls Client4.updateScheduledRecap
- Dispatches RECEIVED_SCHEDULED_RECAP on success
- Follows existing action patterns
* docs(05-02): complete DayOfWeekSelector plan
Tasks completed: 2/2
- DayOfWeekSelector component with bitmask state
- Styled button group with toggle/hover/error states
SUMMARY: .planning/phases/05-enhanced-wizard/05-02-SUMMARY.md
* docs(05-01): complete Redux actions for scheduled recaps plan
Tasks completed: 3/3
- Add action type constants for create/update scheduled recap
- Add createScheduledRecap async action
- Add updateScheduledRecap async action
SUMMARY: .planning/phases/05-enhanced-wizard/05-01-SUMMARY.md
* feat(05-04): add run once toggle to RecapConfiguration
- Add runOnce, setRunOnce, and isEditMode props to Props type
- Import Toggle component
- Add run once toggle section at bottom of Step 1
- Toggle hidden when isEditMode is true
- Include descriptive text below toggle
* feat(05-03): create ScheduleConfiguration component for Step 3
- Add day-of-week selection using DayOfWeekSelector
- Add time picker with 30-minute intervals and locale-aware formatting
- Add time period dropdown (Previous day, Last 3 days, Last 7 days)
- Add custom instructions textarea with 500 char limit
- Add next run preview with timezone support
- Use getCurrentTimezone selector for user timezone
* feat(05-04): add run once toggle styles
- Add .run-once-group with top separator border
- Style toggle and label with proper alignment
- Add description text with left margin for alignment
- Use consistent spacing and typography
* feat(05-03): add Step 3 schedule configuration styles
- Add step-three layout with vertical flex and gap
- Add form-group styling with label and error states
- Add next-run-preview styling with background and subtle text
- Add textarea overrides for custom instructions input
* docs(05-04): complete run once toggle plan
Tasks completed: 2/2
- Add run once toggle to RecapConfiguration
- Add run once toggle styles
SUMMARY: .planning/phases/05-enhanced-wizard/05-04-SUMMARY.md
* docs(05-03): complete ScheduleConfiguration plan
Tasks completed: 2/2
- Create ScheduleConfiguration component
- Add Step 3 styles to SCSS
SUMMARY: .planning/phases/05-enhanced-wizard/05-03-SUMMARY.md
* feat(05-05): add schedule state and edit mode props to modal
- Add editScheduledRecap prop for edit mode detection
- Add schedule state (daysOfWeek, timeOfDay, timePeriod, customInstructions)
- Add runOnce state and validation state (daysError, timeError)
- Add useEffect to pre-fill form in edit mode
- Import createScheduledRecap, updateScheduledRecap actions
- Import ScheduleConfiguration component and getCurrentTimezone selector
* feat(05-06): update handleEditScheduledRecap to pass scheduled recap to modal
- Find scheduled recap by ID from scheduledRecaps array
- Pass editScheduledRecap via dialogProps to CreateRecapModal
- Early return if scheduled recap not found
* feat(05-05): update step navigation for run once and schedule flows
- Update handleNext to clear validation errors on navigation
- Update handlePrevious to clear validation errors on navigation
- Update getTotalSteps for run once (2-3 steps) vs scheduled (always 3)
- Update getActualStep for proper step indicator mapping
* feat(05-05): update renderStep for schedule vs run once flows
- Pass runOnce, setRunOnce, and isEditMode props to RecapConfiguration
- Show ChannelSummary for run once mode at step 3
- Show ScheduleConfiguration for scheduled mode at step 3
- Pass all schedule state props to ScheduleConfiguration component
* feat(05-05): update handleSubmit for immediate and scheduled recaps
- Add schedule field validation for non-run-once mode
- Dispatch createRecap for run once mode (existing behavior)
- Dispatch updateScheduledRecap for edit mode
- Dispatch createScheduledRecap for new scheduled recaps
- Navigate to ?tab=scheduled after creating/editing scheduled recap
- Add proper error messages for schedule validation failures
* feat(05-05): update modal header and button text for edit mode
- Update canProceed to validate schedule fields in step 3
- Add getConfirmButtonText helper for context-aware button text
- Show 'Start recap' for run once, 'Save changes' for edit mode
- Show 'Create schedule' for new scheduled recaps
- Update headerText to show 'Edit your recap' in edit mode
* docs(05-05): complete wizard integration plan
Tasks completed: 5/5
- Add schedule state and edit mode props
- Update step navigation for run once and schedule flows
- Update renderStep for schedule vs run once flows
- Update handleSubmit for immediate and scheduled recaps
- Update modal header and button text for edit mode
SUMMARY: .planning/phases/05-enhanced-wizard/05-05-SUMMARY.md
* fix(05-06): JSON.stringify body in scheduled recap API calls
createScheduledRecap and updateScheduledRecap were passing objects
directly to doFetch body, causing '[object Object]' to be sent instead
of JSON. Fixed to match createRecap pattern.
* fix(05-06): align time period values with server model
Frontend was using 'last_3_days' and 'last_7_days' but server expects
'last_24h', 'last_week', and 'since_last_read'. Updated options to match.
* fix(05-06): remove duplicate border on custom instructions textarea
- GenericModal adds a border to all .form-control elements
- Input widget's Input_fieldset already provides a border container
- This caused a double-border visual glitch on the textarea
- Added border: none to the inner textarea to fix the issue
* fix(05-06): reserve space for next run preview to prevent modal height jump
- Always render next-run-preview container (previously conditionally rendered)
- Use visibility:hidden instead of not rendering when no preview available
- Add non-breaking space placeholder to maintain consistent element height
- Prevents jarring visual jump when user selects a day of the week
* fix(05-06): remove border/background from next-run-preview
- Remove padding, border-radius, and background-color from .next-run-preview
- Style as plain text with subtle color and smaller font size
- Keep margin for appropriate spacing from time selector
* fix(05-06): use abbreviated timezone in next recap preview
- Use Intl.DateTimeFormat with timeZoneName: 'short' to get timezone
abbreviation (e.g., EST, PST, EDT) instead of full label
- Remove unused getCurrentTimezoneLabel selector import
- Preview now shows 'Monday at 9:00 AM (EST)' instead of
'Monday at 9:00 AM ((UTC-05:00) Eastern Time (US & Canada))'
* fix(05-06): prevent modal height jump when next run preview appears
- Move next-run-preview inside time-selection-group as helper text
- Add min-height: 16px to reserve space when preview is hidden
- Use visibility: hidden instead of display: none for consistent height
- Reduce step-three gap from 20px to 16px for better spacing
- Add margin-bottom: 0 to form-group to override default spacing
* fix(05-06): add section titles and fix spacing in schedule configuration
- Add section titles per Figma design (Heading 100 style):
- 'When would you like your summary sent?' as main header
- 'On which days should your recap run?' for days section
- 'At what time?' for time section
- 'Select a time period for your recap to cover' for time period
- 'Additional instructions for {agentName}' for custom instructions
- Pass agentName prop from parent to show selected agent name
- Fix spacing: reserve space for next-run preview with container
to prevent time period section from jumping when preview appears
- Update SCSS with schedule-section groups and proper spacing
* fix(05-06): remove duplicate title and fix subtitle-dropdown spacing
- Remove 'When would you like your summary sent?' duplicate title
- Add scoped CSS rule for 12px total spacing between subtitle and dropdown
* fix(05-06): use standard Toggle without text labels for active/paused state
- Remove onText/offText props from Toggle component
- Add ariaLabel for accessibility (describes toggle state and action)
- Update SCSS to remove min-width constraint that was for text display
- Navigation to scheduled tab after creating scheduled recap already works correctly
* fix(05-06): toggle color and tab navigation after creating scheduled recap
- Use btn-toggle-primary class for scheduled recap toggle to display proper button-bg color
- Add useQuery hook to read tab query parameter from URL
- Sync activeTab state with URL tab parameter to enable navigation after modal close
* fix(05-06): sync tab state with URL bidirectionally
- Add handleTabChange callback that updates both state and URL
- Use history.replace() to update URL without polluting browser history
- Remove tab param from URL when switching to 'unread' (default tab)
- Simplify URL sync useEffect to always update from tabParam
- This enables proper navigation after creating scheduled recaps
* fix(05-06): fix navigation to scheduled tab after creating scheduled recap
- Replace useRouteMatch() with getCurrentRelativeTeamUrl selector
- Modal was using route match which returned wrong URL context (modal is rendered at root level)
- Use team selector to get correct team URL for navigation
- Update test to remove unnecessary useRouteMatch mock
* fix(05-06): sort scheduled recaps by newest first
- Update getAllScheduledRecaps selector to sort by create_at descending
- Follows same pattern as other recap selectors (getUnreadRecaps, getReadRecaps)
- Derived selectors (getActiveScheduledRecaps, getPausedScheduledRecaps) inherit sort order
* docs(05-06): complete edit wiring and UI polish plan
* docs(phase-05): complete Enhanced Wizard phase
Phase 5: Enhanced Wizard
- 6 plans executed across 3 waves
- 13 requirements verified
- Multi-step wizard for creating/editing scheduled recaps
- Run once and scheduled flows
- Full edit mode with pre-fill
- Extensive UI polish based on human feedback
All 39 requirements complete. Milestone ready for audit.
* docs(v1): milestone audit complete - all requirements satisfied
- 39/39 requirements verified
- 5/5 phases passed
- 100% cross-phase integration
- 5/5 E2E flows complete
- 2 minor tech debt items (non-blocking)
* chore: remove .planning from git tracking
- Add .planning to .gitignore
- Remove .planning files from git index (kept locally)
- Planning files are for local development only
* feat(06-01): add AIRecapSettings and RecapLimitSettings structs
- RecapLimitSettings with 7 limit fields (recaps/day, scheduled, channels, posts, tokens, posts/day, cooldown)
- AIRecapSettings with master toggle and per-limit enforcement toggles
- SetDefaults methods with sensible defaults (10 recaps/day, 5 scheduled, etc.)
- isValid/IsValid validation methods enforcing natural minimums
* feat(06-01): integrate AIRecapSettings into Config struct
- Add AIRecapSettings field to Config struct
- Call AIRecapSettings.SetDefaults() in Config.SetDefaults()
- Call AIRecapSettings.IsValid() in Config.IsValid()
* test(06-01): add tests for AIRecapSettings and RecapLimitSettings
- TestAIRecapSettingsSetDefaults: verifies all defaults match spec
- TestRecapLimitSettingsValidation: verifies validation rejects invalid values
- TestAIRecapSettingsPreservesExistingValues: verifies SetDefaults preserves existing
- TestAIRecapSettingsIsValid: verifies IsValid delegates to DefaultLimits
* feat(06-02): create EffectiveRecapLimits struct
- Add EffectiveRecapLimits struct with 7 resolved limit fields
- Add LimitSource type with system/group/user constants
- Add UnlimitedValue constant (-1) for disabled limits
- Add IsLimitEnabled helper function for enforcement code
* feat(06-02): create GetEffectiveLimits resolution function
- Add GetEffectiveLimits(userID) returning resolved limits for any user
- Resolve limits from AIRecapSettings.DefaultLimits config
- Apply per-limit enforcement toggles (disabled = -1 unlimited)
- Add helper functions getValueOrDefault and getBoolOrDefault
- Structure for Phase 8 group/user resolution with TODOs
* test(06-02): add tests for GetEffectiveLimits function
- TestGetEffectiveLimitsDefaults verifies system defaults returned
- TestGetEffectiveLimitsWithDisabledToggle verifies -1 returned for disabled limits
- TestGetEffectiveLimitsWithCustomDefaults verifies custom config honored
- TestGetEffectiveLimitsAllTogglesDisabled verifies all -1 when all disabled
- TestGetEffectiveLimitsUnlimitedConfigValue verifies -1 config value honored
- TestIsLimitEnabled verifies helper correctly identifies enabled limits
* feat(07-01): add RecapStatusSkipped constant and SkipReason field
- Add RecapStatusSkipped constant for recaps skipped due to limit violations
- Add SkipReasonDailyLimit and SkipReasonCooldown skip reason constants
- Add ScheduledRecapId field to Recap struct for tracking scheduled recaps
- Add SkipReason field to Recap struct for tracking why recap was skipped
- Update Auditable() method to include new fields
- Update recapColumns and recapToMap to include new fields
* feat(07-01): add store interface methods for limit enforcement
- Add CountForUserSince to RecapStore for daily limit enforcement
- Add GetLastCompletedManualRecap to RecapStore for cooldown checking
- Add CountForUser to ScheduledRecapStore for max scheduled recaps limit
- Update mock implementations for both stores
* feat(07-01): implement store methods in SQL stores
- Implement CountForUser in SqlScheduledRecapStore
- Counts active (non-deleted, enabled) scheduled recaps for a user
- Implement CountForUserSince in SqlRecapStore
- Counts recaps since timestamp, excluding skipped recaps
- Implement GetLastCompletedManualRecap in SqlRecapStore
- Returns most recent completed manual recap (no ScheduledRecapId)
- Returns nil, nil when no manual recap exists
* feat(07-03): add daily limit check to scheduled recap worker
- Add GetEffectiveLimits and GetUser to AppIface for limit checking
- Check MaxRecapsPerDay before executing scheduled recap
- Create skipped recap record when daily limit exceeded
- Use user's timezone for midnight calculation
- Update next run time even when skipping (scheduler moves on)
* feat(07-03): add cooldown check to manual recap creation
- Check CooldownMinutes before allowing manual recap creation
- Return HTTP 429 with retry-after info when cooldown active
- Only checks against completed manual recaps (per CONTEXT.md)
- Failed recaps don't consume cooldown (checks completed only)
* feat(07-03): add i18n messages for cooldown errors
- Add cooldown_active error with retry info template
- Add cooldown_check_failed error message
- Uses "Your organization's policy limits..." pattern
* feat(07-02): add limit checks to CreateScheduledRecap
- Add max scheduled recaps limit check using CountForUser store method
- Add max channels per recap limit check against ChannelIds length
- Return HTTP 400 with clear error messages when limits exceeded
- Uses GetEffectiveLimits for limit resolution (ENF-01, ENF-02, ENF-08)
* feat(07-02): add i18n error messages for scheduled recap limits
- Add max_scheduled_reached message with "Your organization's policy limits..." pattern
- Add max_channels_exceeded message with limit and requested count
- Add count_failed internal error message
* test(07-04): add unit tests for post/token truncation
- Test proportional post distribution across channels
- Test minimum 1 post per channel guarantee
- Test empty channel handling
- Test token estimation (4 chars/token heuristic)
- Test token limit truncation removes from largest channels
Verifies ENF-05, ENF-06 truncation implementation.
* test(07-05): add ENF-07 permission preservation tests
- Add tests verifying over-limit users can view/edit/delete existing recaps
- Tests confirm management operations do NOT check limits (grandfathering)
- Tests confirm creation IS still blocked when over limit
- Add migration 000151 for missing ScheduledRecapId/SkipReason columns
ENF-07: Limits only block creation, not management of existing resources
* feat(08-01): create UnlimitedNumberSetting component
- Number input with Unlimited checkbox for admin console settings
- When checked: disables input and sets value to -1 (unlimited)
- When unchecked: enables input and sets value to defaultValue
- Supports disabled state and setByEnv footer
* test(08-01): add unit tests for UnlimitedNumberSetting
- Tests rendering with numeric and unlimited values
- Tests checkbox toggle behavior (check/uncheck)
- Tests number input changes
- Tests disabled state and setByEnv footer
- Tests custom unlimited label and placeholder
- 11 test cases covering core functionality
* feat(08-02): add Recaps subsection to admin_definition.tsx
- Import UnlimitedNumberSetting component
- Add 'recaps' subsection under site configuration section
- Include master enable toggle for AI Recap Limits
- Add 3 grouped sections: Quota Limits, Content Limits, Time Limits
- Configure all 7 limit settings with proper config keys
- Add AIRecapSettings and RecapLimitSettings TypeScript types
- All settings disabled when master toggle is off
* feat(08-02): add i18n strings for Recaps admin section
- Add admin.sidebar.recaps for navigation
- Add admin.site.recaps for section title
- Add admin.recaps.enable.* for master toggle
- Add admin.recaps.sections.* for section descriptions
- Add admin.recaps.max*.* for all limit field labels/descriptions
- Add admin.recaps.cooldownMinutes.* for time limit settings
- Add admin.recaps.unlimited for checkbox label
- Total: 24 new i18n strings
* style(08-02): remove section comments to fix lint errors
Remove inline comments that triggered lines-around-comment lint rule
* feat(09-01): add RecapLimitStatus model and App layer logic
- Add RecapLimitStatus, DailyUsageStatus, CooldownStatus structs
- Implement App.GetRecapLimitStatus with daily usage count and cooldown calculation
* feat(09-01): add GET /api/v4/recaps/limit_status endpoint
- Register route and handler
- Return structured limit status
- Add error translation
* feat(09-01): add TypeScript types for limit status
- Export RecapLimitStatus and related types
* feat(09-02): add recap limit status redux integration
- Add Client4.getRecapLimitStatus method
- Add Redux action, reducer, and selector for limit status
- Update GlobalState and initial state to include limitStatus
* fix(09-02): update CreateRecapModal error handling and tests
- Check dispatch result.error instead of try/catch to handle server errors
- Display server error message (e.g. policy limits) inline
- Fix TypeScript errors: displayName property and missing props in tests
* feat(09-03): implement user-facing limit status UI
* Verify Phase 9: User-Facing UX
* Remove UAT artifact
* fix: UI/UX issues (badge, toggle, input, tooltip)
* fix: Increase RecapUsageBadge tooltip z-index
* blank lines
* [MM-67163] checkpoint: scheduled recaps feature working
All phases (01-09) complete and verified:
- Database foundation with DST-aware scheduling
- API layer for CRUD operations
- Job scheduler and worker
- Scheduled tab UI with list/create/edit/delete
- Enhanced wizard with schedule configuration
- Config settings and admin console section
- Limit enforcement (daily, cooldown, token, post)
- User-facing limit status badge
* [MM-67163] simplify: reduce duplication and fix issues across scheduled recaps
- Extract requireScheduledRecapOwnership helper for 5 API handlers
- Consolidate ResumeScheduledRecap from 4 store calls to 2
- Remove redundant Get() in PauseScheduledRecap
- Extract advanceSchedule helper in worker to deduplicate skip/success paths
- Remove double PreSave() in store Save method
- Remove dead code fallback in GetEffectiveLimits
- Deduplicate ScheduledRecapInput construction in create modal
- Fix missing fetchRecapLimitStatus import (TS error)
- Extract day-of-week bitmask constants to @mattermost/types/recaps
- Fix hardcoded English strings in schedule_display formatNextRun
* Address CodeRabbit review feedback on scheduled recaps
Server fixes:
- Gate getRecapLimitStatus with requireRecapsEnabled guard
- Normalize error mapping (404 vs 500) in scheduled recap handlers
- Enforce MaxChannelsPerRecap limit in UpdateScheduledRecap
- Return explicit error for unsupported all_unreads mode in scheduled recaps
- Add compensation logic to clean up orphan recaps on job creation failure
- Fix proportional post truncation to strictly enforce maxPosts cap
- Add missing CountForUser retry wrapper in RetryLayerScheduledRecapStore
- Handle NULL ScheduledRecapId in manual recap cooldown lookup
- Exclude soft-deleted rows in scheduled recap Get query
Frontend fixes:
- Pass isCreationBlocked to ScheduledRecapsList empty state
- Fix same-day nextRunAt mislabeled as "Tomorrow" in schedule display
- Handle thunk error results in scheduled recap item actions
- Replace scheduled recaps map on full refresh instead of merging
Made-with: Cursor
* Handle thunk error results in scheduled recap toggle handler
Check dispatch result for errors in handleToggle to prevent
false-success UI flows when pause/resume operations fail.
Made-with: Cursor
* Fix CI failures: mock store, permissions, migrations, lint, and Playwright config
- Add ScheduledRecap() to storetest.Store mock and retrylayer test setup
- Register sysconsole_read_ai_recaps / sysconsole_write_ai_recaps permissions
- Renumber scheduled_recaps migration from 150→156 and recap_skip_fields from 151→157 to resolve version conflicts
- Fix ESLint errors in recap components (operator-linebreak, import order, labels, headers, etc.)
- Add AIRecapSettings to Playwright default_config.ts
Made-with: Cursor
* Address CodeRabbit Round 2 review feedback
- Add OpenAPI spec for GET /api/v4/recaps/limit_status with schema
definitions for RecapLimitStatus, EffectiveRecapLimits,
DailyUsageStatus, and CooldownStatus
- Set ScheduledRecapId when creating recaps from schedules to prevent
cooldown logic from treating scheduled recaps as manual
- Handle past nextRunAt timestamps in schedule display: show "Yesterday"
for -1 day and full date for older past dates
Made-with: Cursor
* Add missing variable declarations for AI recaps permissions
Made-with: Cursor
* Fix jsx-max-props-per-line lint errors in schedule_configuration.tsx
Made-with: Cursor
* Fix stylelint property order in recap SCSS files
Made-with: Cursor
* Update admin sidebar snapshots to include Recaps section
Made-with: Cursor
* Fix Go lint issues and add OpenAPI specs for scheduled_recap endpoints
- Use max/min builtins instead of if-statements (modernize/minmax)
- Use range-over-int syntax for for-loops (modernize/rangeint)
- Fix tautological Monday&Monday test (staticcheck/SA4000)
- Add OpenAPI specs for all 7 scheduled_recap API routes
- Add ScheduledRecap model definition to definitions.yaml
Made-with: Cursor
* Regenerate i18n en.json for recap-related strings
Made-with: Cursor
* Fix gofmt indentation in recap.go
Made-with: Cursor
* Fix scheduled recap CI regressions
Align the scheduled recap soft-delete store test with the current Get behavior and add the missing scheduled recap i18n strings so server and enterprise checks stay in sync.
Made-with: Cursor
* Address CodeRabbit review feedback on scheduled recaps
- Use session user ID instead of client-controlled recap.UserId for
limit enforcement in UpdateScheduledRecap (security hardening)
- Add missing i18n entry for app.recap.fetch_posts.app_error
- All other review comments were already addressed in prior commits
Made-with: Cursor
* Fix schedule configuration import order
Reorder the moment import so the webapp lint job passes again on the scheduled recap PR.
Made-with: Cursor
* Allow selecting "all unreads" recap type when no current unreads exist
With scheduled recaps, users should be able to select "all unreads" even
without current unread channels since unreads will exist when the schedule
runs. The "run once" toggle is now disabled when all unreads is selected
with no current unreads, preserving the pre-scheduling behavior of
preventing an immediate recap with nothing to summarize.
Made-with: Cursor
* Fix indentation in recap_configuration.tsx to satisfy eslint
* Fix Recaps test mocks for scheduled recap state.
Keep the webapp test shard green by mirroring the new selectors and mount-time actions used by the Recaps page.
Made-with: Cursor
* Update server/channels/app/recap.go
* Update server/channels/app/recap.go
* Update server/channels/app/recap_limits.go
* Update server/channels/app/recap_limits.go
* Update server/channels/app/scheduled_recap.go
* Update server/channels/app/scheduled_recap.go
* Update webapp/channels/src/components/recaps/scheduled_recaps_empty_state.tsx
* Update server/channels/app/scheduled_recap.go
* Fix gofmt formatting in recap limits
Made-with: Cursor
* Fix recap limits for soft-deleted recaps
Keep deleted recaps in quota and cooldown checks so soft deletion cannot bypass AI usage enforcement.
Made-with: Cursor
* Fix translation
* Fix server check-style: concurrent indexes and lint cleanups
- Use CREATE/DROP INDEX CONCURRENTLY in 000168 scheduled recaps migrations
(required by mattermost-govet concurrentIndex check).
- gofmt validation constants in scheduled_recap.go.
- Replace string += loops in recap tests with strings.Repeat for modernize linter.
Made-with: Cursor
* Fix 000168 migration: run CONCURRENTLY indexes outside transaction
PostgreSQL rejects CREATE/DROP INDEX CONCURRENTLY inside a transaction.
Morph requires -- morph:nontransactional for these migrations, matching
other index migrations in the repo.
Made-with: Cursor
* Stabilize scheduled Recaps for review
Bring the scheduled Recaps work back into a shippable state by tightening backend scheduling and limit semantics, cleaning up the UI flows, and adding focused Recaps E2E coverage.
Made-with: Cursor
* Fix scheduled recaps lint failures
Made-with: Cursor
* Fix scheduled recap Go lint
Made-with: Cursor
* Fix scheduled recaps Playwright check
Made-with: Cursor
* Sync scheduled recaps i18n catalog
Made-with: Cursor
* Fix server recaps CI checks
Made-with: Cursor
* Stabilize recap server CI setup
Made-with: Cursor
* Recaps: enforce token limit, remove dead truncation code, simplify scheduled worker
- Delete unused multi-channel truncation subsystem (FetchAndTruncatePostsForRecap, truncatePostsProportionally)
- Enforce MaxTokensPerRecap in the live recap path (previously a silent no-op)
- Drop redundant non-atomic daily-limit pre-check in the scheduled worker; rely on the atomic check in CreateRecapFromSchedule
- Disable non-recurring schedules on all terminal paths (including daily-limit skips) via finalizeSchedule
- Trim scheduled-recap job payload to scheduled_recap_id
- Exclude skipped recaps from GetRecapsForUser
Co-authored-by: nick.misasi <[email protected]>
* Recaps webapp: format schedule times in the schedule timezone, static day i18n, typed schedule fields
- Display next-run and schedule times using the scheduled recap's timezone (shared schedule_time_format helper) instead of browser-local time
- Replace dynamic day-of-week i18n message IDs with static descriptors so strings are extractable/translatable
- Add ScheduledRecapTimePeriod/ScheduledRecapChannelMode union types
- Use shared Button in the scheduled recaps empty state
Co-authored-by: nick.misasi <[email protected]>
* Recaps e2e: cover scheduled edit/delete/all-unreads/empty-state and token-limit enforcement
- Add UI coverage for editing, deleting, and the empty state of scheduled recaps
- Add all-unreads scheduled recap modal flow
- Add immediate-recap token-limit enforcement tests (single and per-channel), validating MaxTokensPerRecap truncation end-to-end
- Extend recaps page object and helpers
Co-authored-by: nick.misasi <[email protected]>
* Recaps: regenerate store layers/mocks and i18n after cleanup; fix lint
- Regenerate timerlayer/retrylayer/RecapStore mock to canonical order
- Drop orphaned app.recap.fetch_posts.app_error i18n key (removed with dead fetch helper)
- Add missing semicolon in UnlimitedNumberSetting props
Co-authored-by: nick.misasi <[email protected]>
* Recaps: regenerate default roles permissions and admin sidebar snapshot
- Regenerate Cypress default_roles_permissions fixture to include AI Recaps sysconsole permissions
- Update admin sidebar snapshot for the new Recaps section
Co-authored-by: nick.misasi <[email protected]>
* Recaps: stabilize flaky cooldown round-up test
The cooldown round-up subtest placed the prior recap 30s before a 2-minute
cooldown, leaving only ~30s of slack before the rounded remaining time would
flip from 2 to 1 minute. Under heavily loaded CI this could intermittently fail
the exact-minute assertion. Move the prior recap to 1s ago so the remaining time
sits near the top of the 2-minute band (~59s slack) while still exercising
ceiling rounding.
Co-authored-by: nick.misasi <[email protected]>
* Recaps: store ScheduledRecap.ChannelIds as jsonb
Postgres is the only supported database, so the prior TEXT+JSON-string
workaround for MySQL compatibility is unnecessary. Store ChannelIds in a
jsonb column and type the model field as model.StringArray, which removes the
bespoke marshal/unmarshal and intermediate scan struct in the store.
Co-authored-by: nick.misasi <[email protected]>
* Recaps: gate recap limit settings on the ai_recaps permission
Add access:"ai_recaps" to DefaultLimits and all RecapLimitSettings fields so a
delegated admin with sysconsole_write_ai_recaps can save the limit values,
instead of falling back to requiring manage_system.
Co-authored-by: nick.misasi <[email protected]>
* Recaps: drop bespoke Users row lock for limit enforcement
The recap limit savers were the only SELECT ... FOR UPDATE in the sqlstore.
Conform to the prevailing pattern (e.g. channel_bookmark_store.Save): enforce
MaxScheduledRecaps / MaxRecapsPerDay with a transactional count + insert and no
row lock, accepting the same best-effort behavior under concurrency as channels,
team members, and bookmarks.
Co-authored-by: nick.misasi <[email protected]>
* Recaps: document SaveOnceByTypeAndData dedup semantics
Co-authored-by: nick.misasi <[email protected]>
* Recaps: use SERIALIZABLE isolation for limit-check inserts
Co-authored-by: nick.misasi <[email protected]>
* Recaps: mark orphaned recap skipped when job enqueue fails
When CreateJob fails after the recap row is committed, flag the recap
skipped with reason job_creation_failed instead of leaving it pending.
Skipped recaps are excluded from the daily-limit count, so this frees
the quota slot for a recap that will never run, and keeps CreateRecap
consistent with CreateRecapFromSchedule.
Co-authored-by: nick.misasi <[email protected]>
* ci: retrigger CI (flaky enterprise npm cache EEXIST)
Co-authored-by: nick.misasi <[email protected]>
* ci: retrigger CI (flaky Vet API container init)
Co-authored-by: nick.misasi <[email protected]>
* Recaps: re-check channel read permission at recap execution time
Co-authored-by: nick.misasi <[email protected]>
* fix scheduled recap job server test setup
Co-authored-by: nick.misasi <[email protected]>
* ci: retry flaky webapp test
Co-authored-by: nick.misasi <[email protected]>
* ci: retry documentation impact review
Co-authored-by: nick.misasi <[email protected]>
* MM-67163: Address review feedback: remove unused userID param, return AppError from GetRecapLimitStatus
Co-authored-by: nick.misasi <[email protected]>
* MM-67163: Bulk channel permission check for recap creation, bounded by channel limit
Co-authored-by: nick.misasi <[email protected]>
* chore: rerun CI
Co-authored-by: nick.misasi <[email protected]>
* chore: rerun CI after network failure
Co-authored-by: nick.misasi <[email protected]>
---------
Co-authored-by: Mattermost Build <[email protected]>
Co-authored-by: Hanzo Dev <[email protected]>
|
||
|
|
3d2ad310dd |
Add admin-locked profile fields for email users and pre-provisioned names on invites (#37458)
* Add TeamSettings.LockProfileFieldsForEmailUsers with server-side enforcement Co-authored-by: Nick Misasi <[email protected]> * Add API tests for LockProfileFieldsForEmailUsers enforcement Co-authored-by: Nick Misasi <[email protected]> * Hide admin-locked profile fields in user settings and add System Console dropdown Co-authored-by: Nick Misasi <[email protected]> * Support pre-set username and name on team email invites Co-authored-by: Nick Misasi <[email protected]> * Add tests for invite profiles; fix resend worker channel-list parsing Co-authored-by: Nick Misasi <[email protected]> * Add pre-set profile inputs to member invite modal Co-authored-by: Nick Misasi <[email protected]> * Prefill and lock pre-set username on signup page Co-authored-by: Nick Misasi <[email protected]> * Add first/last name editing to System Console user detail and document new setting Co-authored-by: Nick Misasi <[email protected]> * Fix lint issues in invite modal profile inputs Co-authored-by: Nick Misasi <[email protected]> * Fix double outline on invite modal profile inputs inside GenericModal Co-authored-by: Nick Misasi <[email protected]> * Refactor invite emails to InviteEmailData struct and harden invite profile validation Co-authored-by: nick.misasi <[email protected]> * Centralize profile-lock permission exemption in app layer and add config coverage Co-authored-by: nick.misasi <[email protected]> * Per-field name locking in profile settings, typed lock setting, and shared invite profile helpers Co-authored-by: nick.misasi <[email protected]> * Add Playwright E2E coverage for locked profile fields and pre-set invite profiles Co-authored-by: nick.misasi <[email protected]> * Sync playwright package-lock with merged workspace versions Co-authored-by: nick.misasi <[email protected]> * Restore upstream playwright package-lock (fix npm ci drift) Co-authored-by: nick.misasi <[email protected]> * Assert invite input cleared instead of chip text after adding email Co-authored-by: nick.misasi <[email protected]> * Fix invite modal scroll, username error layout, and clipped autocomplete Keep the footer pinned while tall profile rows scroll, show username validation full-width after blur, and portal select menus so they are not clipped by the scroll container. * fixes for autocomplete items not aligning properly * Make invite autocomplete menu portal opt-in and fix modal chrome Confine document.body menu portaling to the invite modal via a menuPortal prop, and restore click-away, slide-in animation, and header alignment for the scrolling invite modal layout. * Fix eslint lines-around-comment on menuPortal props * Fix stylelint property order in invitation modal SCSS. * Fix stylelint property order in invitation_modal.scss Co-authored-by: nick.misasi <[email protected]> * Adapt invite modal E2E to portaled autocomplete menus Co-authored-by: nick.misasi <[email protected]> * Simplify locked profile invite implementation Co-authored-by: nick.misasi <[email protected]> * Fix invite modal review and E2E feedback Co-authored-by: nick.misasi <[email protected]> * Retry flaky enterprise CI Co-authored-by: nick.misasi <[email protected]> * Retry Docker image export CI Co-authored-by: nick.misasi <[email protected]> * Minimize locked profile fields diff Co-authored-by: nick.misasi <[email protected]> * Fix locked profile E2E documentation Co-authored-by: nick.misasi <[email protected]> * Address minimized test review feedback Co-authored-by: nick.misasi <[email protected]> * Fix email test whitespace Co-authored-by: nick.misasi <[email protected]> * Retry OpenSearch download CI Co-authored-by: nick.misasi <[email protected]> * Retry flaky Cypress thread navigation Co-authored-by: nick.misasi <[email protected]> * Preserve legacy invite behavior without profiles Co-authored-by: nick.misasi <[email protected]> * Clarify invite profile validation Co-authored-by: nick.misasi <[email protected]> * Retry flaky enterprise E2E Co-authored-by: nick.misasi <[email protected]> --------- Co-authored-by: Matthew Birtch <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
a582f03570 |
MM 69100 - Team ABAC Membership - members sync and end user surfaces (#37054)
* MM-69063 - Add team ABAC model and constants foundation * Add team ABAC store EXISTS, channel Type retrofit, policy count split, and index migration * Add team ABAC app layer: access gate, hydrators, assign/unassign, cleanup, and GetTeamMembersToRemove store * Enforce team membership ABAC on join and hide policy governed teams from non-qualifying users in the directory * Add team_ids to access policy assign/unassign, expose per-team policy GET, and support abac_match_only for not_in_team user listing * Add team ABAC client methods, websocket handler, per-team System Console policy UI, and hide policy-governed teams from non-qualifying users * Make team ABAC mode-aware: advisory on public teams, strict on private, and surface governed private teams to qualifying users in directory listings * Flag-gate team ABAC mutation/read APIs and fix policy-save error handling, member-removal limit, team-id validation, export, and audit cleanup * coderabbit feedback; Broadcast team policy enforcement updates on policy create/update and activation, not only on delete * Update team access control policy schema to allow nullable policies and enhance test cases with channel counts * Enhance access control policy tests to include team policy search alongside channel policy search * Add team membership access control feature flag to docker-compose generation * Implement team access control policy checks and refactor related components * MM-69100 - add team membership ABAC sync worker with mode-aware removal and auto-add * Add team ABAC removal/auto-add notifications, cascade audit, and team custom-rules save backend * Add Team Settings team membership tab with custom-rules editor, auto-add toggle, save confirmation, and mode-flip sync trigger * Add team access control policy panels, job details team list, and e2e coverage for membership tab and discoverability cards * ix save double-submit with loading state, team privacy via updateTeamPrivacy, mode-flip count accuracy, sync trigger logic, i18n keys, and e2e test cleanup * Add team membership policy notices to invite and add-to-team flows, team access control system messages, and a team policy attributes endpoint * Add team membership recommended tag for qualifying users and team requirements notice in the members modal * temp * adjust styling, add new e2e, fix team admin job permissions issues * e2e clean items created * fix directory and team integration e2e tests * Surface team sync results in the Sync Job Details modal via a Teams tab linked from the chained channel job * code clean up, adjust styling * Add access control attributes endpoint and enhance E2E tests for team membership * Enhance team access control by updating permission checks and syncing jobs * Add API-level enforcement gate tests for ABAC team membership * Fix formatting in team channel settings component by adding missing semicolon * Fix formatting in Client4 class by adjusting type annotations for clarity * revert unwanted changes in package-lock * Refactor job permission test and simplify error handling in team details component * Update test assertions and enhance auto-add functionality in team membership tab * Update onboarding tests to use 'Public Team' card for team access settings * Refactor AccessSettings and TeamPolicyEditor components to replace allowOpenInviteCheckbox with public and private team buttons, and update access control job dispatching logic. * Remove redundant state updates for channel assignments in access control policy reducer * Implement email suppression for team membership notifications and add related tests * Authorize ABAC team self-join by attribute match instead of the join_private_teams role * comments clean up * Gate team privacy type normalization behind active ABAC, keep legacy allow_open_invite-only path otherwise * adjust job details styles * Add team membership policy disconnect confirmation, fix linked-policy affected-member count, and polish job details modal * Combine custom rules with system policy expressions for accurate confirm counts * Implement child resource count stamping and deletion gating for access control policies * Enhance team sync job triggering logic for membership changes without auto-add * Refactor team privacy handling to align with open-directory model; update comments for clarity on allow_open_invite logic. * Deduplicate team ABAC sync jobs per policy to prevent concurrent runs emitting duplicate DMs and audit records on HA clusters * fix linter warnings * Add confirmation for policy removal in TeamDetails tests * Update delete policy message to include Teams in the warning * Add linked teams warning to policy deletion and update translations * Add Auto-add feature for policy selection and team management - Enhanced PolicySelectionModal to include an Auto-add checkbox for each policy. - Updated PolicyList to manage Auto-add state and reflect changes in the UI. - Modified TeamAccessControl to handle Auto-add functionality and persist changes. - Added tests to ensure Auto-add behavior works as expected in various scenarios. - Updated translations for Auto-add related strings. * Add team membership sync footer and enhance job fetching with policy ID * Refactor team membership policy handling and sync footer implementation * Add team sync channel cascade test for private channel membership --------- Co-authored-by: Mattermost Build <[email protected]> |
||
|
|
86a0063cf2 | Property owners for property fields and values, new audit logs for cpa value changes and new pluginapis (#37299) | ||
|
|
c53ede92cb |
Session Attributes: System Console management page and permission-policy picker integration (#37362)
* Add Session Attributes system console page shell Register the gated System Console "Session Attributes" subsection backed by a component page that loads the session_attributes property fields, plus a feature discovery upsell. Removes the former config toggles from the UI. Includes display helpers and unit tests. * Add Session Attributes listing table Render the read-only session attributes table with Display Name, Name (with server-sourced label), derived Type, platform availability icons, TTL, grace period, and status columns, plus loading and empty states. Includes status chip, platform icon, and table unit tests. * Add Session Attributes row actions and staged save Add a per-row actions menu to tune TTL and grace period via presets and to disable/enable each attribute, with a destructive disable confirmation modal. Edits stage locally and persist on save via a per-field property patch thunk, with an unsaved-changes guard. Includes the patch thunk and component tests. * Surface session attributes in permission policy editors only Include session attributes in the permission policy attribute picker (both advanced and table editor modes) and group them under a dedicated section, while excluding them from membership and parent access-rule editors via a shared helper. Surface the server rejection message when a membership rule references session attributes. * Fix lint formatting in session attributes table test Split single-line renderWithContext JSX in session_attributes_table.test.tsx onto multiple lines to satisfy react/jsx-max-props-per-line, matching the existing test formatting convention. Surfaced by the pre-commit ESLint gate. * Add Session Attributes E2E specs Add Playwright end-to-end coverage for the Session Attributes console page: listing render, staged TTL/grace/enable edits with save persistence, disable confirmation modal, dirty navigation guard with cancel revert, and permission-versus-membership picker polarity. Includes the page object and sidebar wiring. * Fix playwright-lib build to resolve @/* tsconfig path alias The lib rollup build (@rollup/plugin-typescript) compiled the @/* tsconfig path alias but never rewrote it, so with preserveModules the emitted dist files still imported the unresolved @/ specifier. Node could not load these, failing Playwright config loading for the entire suite (Cannot find module @/ui/components). Add a minimal inline rollup resolveId plugin that maps @/* to the real src file so emitted dist uses correct relative imports. No new dependency required. * Fix session attribute group identity to use real group id The webapp conflated the property group NAME (session_attributes) with the group's real DB id. The server keys every field under a 26-char group UUID, so the listing page selected fields under the name and always rendered "No session attributes found." on a real server, and the ABAC editors compared field.group_id against the name, which never matched live data. Resolve the group properly instead: - fetchPropertyFields now also dispatches RECEIVED_PROPERTY_GROUP (mirroring the managed-channel-categories flow) so the group name -> id mapping is available, and the listing page selects fields by the resolved group id via getPropertyGroupByName. - Add isSessionAttributeField, keyed on the session object_type, and use it in the ABAC consumers (attribute picker, excludeSessionAttributes, permission policy picker, simulate-access modal). object_type is intrinsic to the autocomplete fields and avoids the name/UUID conflation entirely. - Relax UserPropertyField.group_id to string since the server returns a UUID; the group name is only ever a client-side placeholder for an unsaved field. Harden the unit tests to use realistic group UUIDs distinct from the group name and correct object_type values, so fixtures can no longer pass with a group_id the server never produces. * Fix SaveChangesPanel footer Cancel to revert in place The admin SaveChangesPanel rendered its onCancel handler through a BlockableButton, which intercepts clicks while navigation is blocked and defers the action via deferNavigation instead of invoking it. On the Session Attributes page this meant footer Cancel never reverted staged edits directly: it re-opened the Discard Changes guard and only reverted if a deferred navigation was later confirmed. BlockableButton is only appropriate for the cancelLink (navigation) path. For an in-place revert handler, wire onCancel to a plain button so it calls the hook cancel() directly, reverting staged edits, clearing the dirty state, releasing setNavigationBlocked, and never showing the discard modal. Repair the page-level jest test to exercise the real footer Cancel and assert direct revert plus nav-guard release without confirming a deferred navigation. * Merge enabled session attributes into permission policy editors Inject enabled session attributes into the two permission-policy attribute pickers (CEL and table editors) while keeping membership/parent-policy surfaces restricted to long-lived user attributes via excludeSessionAttributes. Adds a useEnabledSessionAttributeFields hook plus the shared mergeSessionAttributes/celPrefixForField helpers so the editors can build CEL across the user.attributes.* and user.session.* namespaces. Addresses two review nits: - Resolve the picked attribute by its unique field id (not bare name) so a session attribute sharing a name with a CPA attribute still maps to object_type 'session' and emits user.session.<name>; the picker also uses id-based DOM ids to avoid collisions. - Make celPrefixForField the single source of CEL-prefix truth by consuming it in rowToCEL instead of inlining the namespace check. * Fix Session Attributes E2E spec: id-based picker selectors and per-test state isolation The attribute picker menu items are keyed by field id (#attribute-<id>) rather than name, so the permission/membership picker assertions now resolve session attribute ids from the property-fields API and the Department user-attribute id from the CPA fields API instead of targeting stale name-based ids. Add an afterEach that restores every seeded session attribute to a baseline captured at suite start, replacing the per-test finally restores. This keeps the suite order-independent so the listing test "Disabled by default" assertions no longer depend on whether another test has enabled a field first. * UX Polish * Linting * Removed styled components * Fix session attributes unit tests Co-authored-by: maria.nunez <[email protected]> * Fix invalid Sass '&&&' selector breaking production build Co-authored-by: maria.nunez <[email protected]> * Stabilize session attributes duration-preset e2e interaction Co-authored-by: maria.nunez <[email protected]> * Fix broken session attributes doc link in feature discovery Co-authored-by: maria.nunez <[email protected]> * Fix session attributes test type error from non-union group id Co-authored-by: maria.nunez <[email protected]> * Revert unnecessary playwright-lib rollup @/* alias resolver @rollup/plugin-typescript already resolves the @/* tsconfig path alias; building with and without the added resolveSrcAlias plugin produces byte-identical dist JS, so the change was a no-op. Co-authored-by: maria.nunez <[email protected]> * Allow session-attribute row defaults and resolve table-editor lookups by namespace findFirstAvailableAttributeFromList now treats session attributes as selectable (mirroring AttributeSelectorMenu), so a session-only attribute set no longer fails addRow. The operator menu reads row.attribute_type and the value options match on name + object_type, so a user/session name collision resolves the correct namespace. Co-authored-by: maria.nunez <[email protected]> * Type session-attribute platforms on the shared property attrs Add platforms?: string[] to UserPropertyField.attrs so the attribute picker reads it without an unsafe cast, and narrow the platform filter in getSessionAttrs with a type guard. Co-authored-by: maria.nunez <[email protected]> * Session Attributes page: surface load errors, propagate read-only, and improve a11y Handle a failed property-fields fetch with an explicit error state instead of leaving the rejection uncaught and showing the empty state. Thread the page's disabled flag through to the row dot menus so read-only admins cannot stage edits. Convey each platform icon's active/inactive state in its accessible name, and document the table border override. Co-authored-by: maria.nunez <[email protected]> * SaveChangesPanel: use shared Button for the in-place cancel control Co-authored-by: maria.nunez <[email protected]> * Cover fetchPropertyFields group name to uuid mapping Co-authored-by: maria.nunez <[email protected]> * E2E: scope session-attribute submenu trigger to its row and document single-field save Co-authored-by: maria.nunez <[email protected]> * Comment linting * scss cleanup * linting * PR feedback * PR Feedback. Removed IP + version icons * PR feedback * PR feedback * Fix session attributes table test after IP/Version display types were removed Co-authored-by: maria.nunez <[email protected]> * Session Attributes admin console UX fixes - Reduce table row height and shrink-wrap the panel to the table - Reorder LHS sidebar so Session Attributes follows User Attributes - Cap attribute selector popover at 400px max-height with scroll * Fix native attribute selection by assigning synthetic field IDs The ABAC editors resolve the selected attribute by field ID, but the synthetic native user attribute fields were created without an ID. Every built-in attribute therefore resolved to the first empty-ID field (Email), so no other built-in could be selected. Assign each native field a stable, unique, non-empty ID so id-based resolution works for all built-ins. --------- Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
3cb57f2409 | MM-69798: Add AccessControlSettings setting to disable channel access indicators (#37519) | ||
|
|
ea7f053622 |
MM-69782: let plugins open core modals by id (#37339)
* feat(plugins): let plugins open core modals by id Add a small, type-safe allowlist so plugins can open a curated set of core modals by id via window.WebappUtils.modals.openModalById(id, props). - @mattermost/types exports PublishedModalProps (id -> props) and PublishedModalId as a hand-authored, plugin-facing contract. - published_modals.ts holds the runtime allowlist, lazy-loads each modal into its own chunk under a Suspense boundary, and a build-time guard fails the web app build if a modal's real props drift from the contract. - openUserSettings now delegates through the same path, moving the user settings modal out of the bootstrap bundle. Host-controlled props (team_settings' isOpen) are pinned at the allowlist boundary so they stay out of the plugin contract. Context: /longshot --minimal — webapp-only published-modal contract; supports the Docs plugin (separate repo). * feat(plugins): move published-modal contract to shared, add canOpenModalId Move the plugin-facing published-modal contract out of @mattermost/types (a data-model package) into @mattermost/shared/types/global, where a plugin-consumable UI contract belongs. Add WindowShared as the forward- facing slice of window that shared owns, to grow as more of channels' WindowWithLibraries surface migrates. Add canOpenModalId(modalId) feature detection so a plugin can check whether the running web app actually publishes a modal id before opening it. It tests the runtime allowlist rather than every ModalIdentifiers entry, and accepts any string so a newer plugin can probe ids an older web app does not publish yet. Context: /longshot --continue — migrate published modal types to @mattermost/shared and add canOpenModalId feature detection. * [MM-69774] webapp: expose core WYSIWYG editor and suggestion providers to plugins via window.Editor (#37514) * [MM-69774] webapp: expose core WYSIWYG editor and suggestion providers to plugins via window.Editor * PR feedback * webapp: expose forwardRef handles for WysiwygEditor and FormattingBar --------- Co-authored-by: Nevyana Angelova <[email protected]> --------- Co-authored-by: nang2049 <[email protected]> Co-authored-by: Nevyana Angelova <[email protected]> |
||
|
|
a3252206ad |
[MM-69778] Changes needed to NewChannelModal for plugins (#37452)
* pass defaultCategoryName, classificationId, and bannerText through * add test for classification fields->plugin onCreate callback |
||
|
|
ae56750319 |
[MM-69561] Add ability to rotate (regenerate) Personal Access Tokens (#37295)
* MM-69561: Add ability to rotate (regenerate) Personal Access Tokens - Add UpdateTokenRotate to UserAccessTokenStore interface and implement in sqlstore: deletes sessions on the old secret, then updates the token row with the new secret and expiry in one transaction - Regenerate store retrylayer, timerlayer, and mocks - Add RotateUserAccessToken app method: validates expiry (bot-exempt), captures old session for cache eviction, generates new secret, and sends a notification email - Register POST /api/v4/users/tokens/rotate handler with full permission checks (create_user_access_token + edit_other_users + manage_system for sysadmin targets); rejects OAuth sessions and disabled tokens - Add RotateUserAccessToken to the Go client (client4.go) - Add storetest covering secret rotation and old-session cleanup - Add API4 tests: happy path, permission denials, OAuth rejection, and max-lifetime enforcement * MM-69561: Add dedicated rotate email and i18n strings - Add SendUserAccessTokenRotatedEmail (subject/body distinct from the 'added' email so users aren't confused by a rotation event) - Add SendUserAccessTokenRotatedEmail to ServiceInterface + mock - Add en.json strings for the rotate email and the two new error ids (rotate.app_error, disabled_token.app_error) - Switch RotateUserAccessToken to call SendUserAccessTokenRotatedEmail * MM-69561: Add mmctl token rotate subcommand - Add RotateUserAccessToken to the mmctl Client interface and mock - Add 'mmctl token rotate <token-id> [--expires-in <duration>]' command reusing the existing resolveTokenExpiry/parseExpiresIn helpers from 'generate'; prints the new secret once on success - Add unit tests: happy path, --expires-in passed through, server error, invalid --expires-in * MM-69561: Regenerate mmctl docs for token rotate * MM-69561: Add API docs for POST /users/tokens/rotate * MM-69561: Fix i18n string ordering after extract * MM-69561: Add missing API4 test cases for token rotate Cover the three untested access-control branches flagged by the test analysis bot: - Rotating a disabled token returns 400 - Non-system-admin rotating a sysadmin's token returns 403 - Rotating a remote user's token returns 403 * MM-69561: Address review comments - Fix handler authorization order: check SessionHasPermissionToUserOrBot and manage_system before IsRemote/IsActive to avoid leaking token state to unauthorized callers; matches revokeUserAccessToken/disableUserAccessToken - Fix API docs minimum server version: 10.8 -> 10.10 * MM-69561: Restore i18n strings accidentally deleted by extract The earlier i18n-extract run stripped ~164 unrelated translation keys (mostly enterprise-only strings like app.pap.* and api.ldap.*) because the enterprise codebase isn't present in this checkout, so the extractor treated them as unused. Restore them while keeping the 5 new keys added for token rotation. * comment * [MM-69561] Add webapp Regenerate option for Personal Access Tokens Adds a "Regenerate" link to Account Settings > Security > Personal Access Tokens that calls the POST /users/tokens/rotate endpoint added in the server-side rotate PAT work. Regenerating shows a confirmation modal naming the token, then reveals the new secret via the existing one-time-copy flow used for token creation. - webapp Client4.rotateUserAccessToken - mattermost-redux rotateUserAccessToken action - Regenerate link/confirm modal/reveal flow in user_access_token_section - i18n strings - Playwright e2e coverage * Fix regenerate PAT e2e test: confirm modal is not nested in the Profile dialog ConfirmModal renders via react-bootstrap's Modal, which portals to document.body as a sibling of the Profile dialog rather than a descendant, so it must be located via #confirmModal on the page instead of scoped to the Profile dialog locator. * Let users pick a new expiry when regenerating a Personal Access Token Previously, regenerating a token always called rotateUserAccessToken with no expiresAt, so the rotated secret never expired even if the original token did. The Regenerate confirmation modal now includes the same expiry picker used by token creation (extracted into a shared renderExpiryPicker helper), enforces MaximumPersonalAccessTokenLifetimeDays the same way, and disables the confirm button until a valid expiry is selected. * Scope the red background in the Regenerate modal to the warning text only The confirmation question, expiry picker, and its hints were sitting inside the same alert-danger box as the warning, making the whole modal read as an error. Only the warning paragraph keeps the red background now. * Move the regenerate confirmation question below the expiry picker * Align rotate-token wording with UI: use 'regenerate/regenerated' * [MM-69561] rename pat_expiry_notify job to notify_expiring_access_tokens Unifies naming with the sibling cleanup_expired_access_tokens job and fixes the "expiry" vs "expiring" ambiguity: this job warns about tokens approaching expiry, not ones that have already expired. Safe to rename outright since the job hasn't shipped yet. * [MM-69561] remove dead session lookup from EnableUserAccessToken The GetSessionContext call and its result were never used: both branches returned nil regardless. Leftover from mirroring DisableUserAccessToken's shape, which does use its session (to revoke it) unlike Enable. * [MM-69561] rename remaining PAT identifiers to match AccessToken convention The job-level rename (pat_expiry_notify -> notify_expiring_access_tokens) left the app-layer function and its helpers using the old PAT/ PersonalAccessToken naming. Rename them to match: - NotifyPersonalAccessTokensExpiring -> NotifyExpiringAccessTokens - patExpiryBucket -> accessTokenExpiryBucket - sendPATExpiryNotification -> sendAccessTokenExpiryNotification - patExpiryNotifyBatchLimit -> expiringAccessTokenBatchLimit - patExpiryThresholds -> expiringAccessTokenThresholds - maxPersonalAccessTokenExpiry -> maxUserAccessTokenExpiry Also reword the package doc on notify_expiring_access_tokens, which no longer needs to explain a PAT/UserAccessToken naming split now that the app-layer method matches the job name. Pure rename, no behavior change. --------- Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
baddf24ba4 |
[MM-69403] Push WebSocket events on job status changes (#37130)
* Add WebSocket push for job status changes, remove System Console polling Server now broadcasts a job_updated WebSocket event whenever a job transitions state (pending, in_progress, success, error, warning, canceled). The webapp handles this event by updating the Redux store immediately, so the LDAP (and any other) job table in the System Console reflects changes in real time without a polling interval. * Address security review: strip job Data from WS event, guard JSON.parse - publishJobStatus now strips job.Data before broadcasting so sensitive job-type-specific fields (requesting_user_id, team_id, policy_id, role) are not exposed to WebSocket clients without permission checks; status, progress, and timestamps are sufficient for real-time UI updates - handleJobUpdated wraps JSON.parse in try/catch to prevent a malformed payload from breaking WebSocket event handling * Fix import/order ESLint errors for @mattermost/types/jobs The Job type import must appear after @mattermost/types/integrations alphabetically in both websocket_actions.ts and websocket_messages.ts. * Add unit tests for job status WebSocket broadcasting - Server: TestPublishJobStatus covers nil callback safety, Data stripping, and per-method status verification (ClaimJob→in_progress, SetJobWarning→ warning, SetJobSuccess→success, SetJobError nil/non-nil→error, SetJobCanceled→canceled, SetJobPending→pending) - Reducer: jobs.test.ts covers RECEIVED_JOB on jobsByTypeList (type absent, id absent, id found/updated, multi-entry list preserves other entries) - Webapp: handleJobUpdated tests cover valid JSON dispatch and malformed JSON no-op * Fix run time display by using DB-returned job for WebSocket publish UpdateStatus now returns the full job via RETURNING (same pattern as UpdateStatusOptimistically), so SetJob* methods can pass the DB result to publishJobStatus. This ensures last_activity_at and start_at are populated from the DB in the broadcasted event rather than from the potentially-stale in-memory worker struct. Before this change, polling masked the issue by re-fetching jobs via REST API after completion. Without polling, the WebSocket event was the only update and could carry zero timestamps → "--" run time. * Return ErrNotFound from UpdateStatus when no rows matched and remove nil guards UpdateStatus targets a specific job by ID unconditionally, so zero rows updated means the job doesn't exist — an error, not a silent no-op. Returning store.ErrNotFound makes callers fail loudly (rather than silently falling back to stale in-memory data) and removes the need for nil-guard boilerplate in every SetJob* caller. * Re-fetch job list on terminal status to populate run time and error details The WebSocket payload intentionally omits job Data for security, so fields like the error message and accurate last_activity_at are only available via the REST API. On receiving a terminal status (success/error/warning/canceled), dispatch getJobsByType to fill in those details immediately after the status update lands in the store. Also set job.LastActivityAt before publishing in SetJobError's non-nil branch so the WebSocket event carries a reasonable run time approximation even before the re-fetch completes (UpdateOptimistically updates last_activity_at in the DB but not the in-memory struct). * Change UpdateOptimistically to return (*model.Job, error) via SQL RETURNING Returns the full updated row on success; returns nil, nil when the precondition status doesn't match (expected outcome, not an error). Drops the manual LastActivityAt assignment in SetJobError — the DB timestamp is now authoritative via the returned job. * Tighten UpdateOptimistically precondition-fail assertion in storetest The previous require.False(t, err != nil && ret != nil) would incorrectly pass if the function returned (job, nil) instead of the expected (nil, nil). Use explicit require.NoError + require.Nil to pin both return values. * Scope job_updated WebSocket events to system admins only Set ContainsSensitiveData=true so the hub only delivers the event to connections where the user holds manage_system permission. The job system is admin-only; regular users have no UI that consumes this event. * Pass publish func through NewJobServer, remove SetPublish SetPublish was called immediately after NewJobServer in its only production call site with no initialization ordering constraint. Passing it as a constructor parameter removes the unnecessary setter. * Fix four gaps in job status WebSocket push coverage - Remove ContainsSensitiveData=true from publishJobStatus: the payload carries no sensitive fields (Data is stripped), so sysadmins with delegated roles (e.g. sysconsole_read_authentication_ldap) receive events instead of seeing a frozen table. - SetJobProgress now publishes in_progress events so the progress bar advances via WebSocket rather than staying frozen at 0%. - RequestCancellation pending→canceled path now publishes a canceled event, matching the behavior of all other terminal-state transitions. - jobsByTypeList RECEIVED_JOB reducer now prepends a newly-seen job when the type list is already loaded, so brand-new jobs appear immediately via WebSocket push without waiting for a page reload. * Revert finding 1: restore ContainsSensitiveData=true on job WebSocket events * Add non-admin polling fallback and fix missing WS publish in RequestCancellation - JobTable now polls every 15s as a fallback for non-system-admin viewers, since job_updated WebSocket events are scoped to system admins only (addresses PR #37130 review comment r3462905643). - RequestCancellation now publishes a job_updated event when a job transitions from in_progress to cancel_requested, matching the existing publish on the pending->canceled branch (addresses r3462940359). - Add test coverage for both. * Stop stripping Data from job_updated WebSocket event publishJobStatus broadcasts are already restricted to system admins via ContainsSensitiveData, and system admins can already read job Data through the permissioned REST API. Stripping it here provided no additional protection. MM-69695 tracks scoping the broadcast to per-job-type permissions instead of system-admin-only; that follow-up needs to reintroduce appropriate Data filtering once the audience widens. --------- Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
8872a2e5b5 |
MM-69585: remove AttributeBasedAccessControl feature flag (#37366)
* MM-69585: remove AttributeBasedAccessControl feature flag * Gate channel ABAC UI on EnableAttributeBasedAccessControl config |
||
|
|
cc62faffa6 |
Add file upload element to interactive dialogs (#36881)
* Add file upload element to interactive dialogs
Adds a new file element type to interactive dialogs, letting users
upload files in a dialog and forward the file IDs to the integration.
Server:
- model: new file DialogElement type with validation, AllowMultiple
field, and SubmitDialogRequest.FileIds.
- SubmitInteractiveDialog validates submitted file IDs (existence +
ownership) from both file_ids and any file IDs referenced in
submission values; bounded and batched.
- client4.getFileInfo / Client4.GetFileInfo.
Webapp:
- AppsFormFileUpload component: upload, progress, removal, and
hydration of pre-set file IDs; single vs allow_multiple selection.
- Wired into the dialog -> apps-form conversion (file field type).
- Submit is blocked while any field has an upload in progress
(per-field pending tracking).
Tests:
- Go unit tests for model + submit-time file-ID validation.
- Jest tests for the upload component.
- Cypress e2e spec (file_upload_spec.js) + webhook fixtures.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* coderabbit review fixes, linter fixes
* update openAPI spec for file upload
* revert changes to package-lock.json
* review fixes, add correct ids to E2Etests
* fix dryrun security issue
* lint fixes
* Address dialog file upload review feedback and UI file cap
* test fixes
* add several unit tests
* lint fix
---------
Co-authored-by: Scott Bishel <[email protected]>
|
||
|
|
8cec72fbae |
MM-69219: Add multiple concurrent dialogs via action_button element type (#37119)
* Add multiple concurrent dialogs support with action_button element type Enable plugins to open child dialogs from within an existing dialog via a new action_button dialog element and POST /actions/dialogs/execute endpoint. Parent dialogs stay open while children stack (cap of 3). Dialog handlers derive the team permission and forwarded team from the server-loaded channel, not the client-supplied TeamId. * update docs, code rabbit review fixes * update comment per coderabbit * fix tests * increase test coverage * fix bad merge * Replace PostActionAPIResponse with ExecuteDialogActionResponse; replace single-dialog Redux slot with dialogs map --------- Co-authored-by: Mattermost Build <[email protected]> |
||
|
|
02539c3197 |
[MM-69591] Remove WebSocketEventScope feature flag (#37384)
* Remove WebSocketEventScope feature flag gating on server Leave the typing/reaction WebSocket event scoping permanently enabled. Co-authored-by: mattermost-code <[email protected]> * Remove WebSocketEventScope feature flag usage in webapp Always update the active channel/team/thread over the WebSocket so the event scoping stays permanently enabled. Co-authored-by: mattermost-code <[email protected]> * Remove WebSocketEventScope feature flag from e2e default config Co-authored-by: mattermost-code <[email protected]> --------- Co-authored-by: mattermost-code <[email protected]> Co-authored-by: Jesse Hallam <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
20857d4a2f |
Update latest minor version to 11.10.0 (#37397)
Automatic Merge |
||
|
|
5755293a7b | [MM-69337] Surface native user attributes in ABAC webapp editors (Phase 6) (#37174) | ||
|
|
1bbdea1216 |
MM-67412: System Console — Board Attributes screen (PSAv2-based) (#36518)
Co-authored-by: Mattermost Build <[email protected]> Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
ec05aabd72 |
[MM-69075] Revoke non-compliant personal access tokens (#37030)
* [MM-68421] Frontend: PAT creation UI — expiry picker and status display Wires the webapp UI to the server-side support added in PR #36706 (and the MM-68419 model changes): - Extends UserAccessToken type with expires_at, and ClientConfig with EnforcePersonalAccessTokenExpiry / MaximumPersonalAccessTokenLifetimeDays. - Threads expires_at through Client4.createUserAccessToken and the mattermost-redux createUserAccessToken action. - Adds a date/time picker to the PAT creation form (reuses DateTimePickerModal). Honors enforcement and clamps to the max-lifetime setting; maps server error ids (expires_at_required/in_past/too_far) to localized messages. - Displays expiry, derived status badge (active/expired/disabled), and an approaching-expiry warning (<7 days) in the account settings token list. Mirrors expiry + status in the admin Manage Tokens modal. - Adds the supporting i18n keys. * [MM-68421] Address review: i18n, ServerError type, scss, explicit guards - Inject intl so the "Expires in N days" tooltip and the picker ariaLabel are localized instead of hard-coded English. - Use the canonical ServerError type from @mattermost/types/errors instead of an ad-hoc inline cast. - Dedupe the new i18n ids — keep a single "Expires: " string per namespace and drop the duplicates introduced in the first pass. - Add scss for setting-box__token-expiry / __token-status / __token-expiry-warning so the new status pill and warning render with the expected styling. - Replace truthy checks on the numeric expiresAt with explicit > 0 comparisons for readability. * [MM-68421] Replace expiry datetime picker with preset chooser + custom date Per UX discussion: PATs are long-lived so sub-day precision is noise. Swap the DateTimePickerModal for a native <select> of presets (No expiry, 7d, 30d, 90d, 1 year, Custom date) with a date-only <input type="date"> revealed when Custom is selected. Effective time is end-of-local-day, which matches how users think about expiry. - Drops the DateTimePickerModal import and the picker open/close handlers; removes the moment-timezone import. - Adds isPresetAllowed() that hides presets exceeding MaximumPersonalAccessTokenLifetimeDays, and a defaultExpiryPreset() that picks 30d (then 7d, then Custom) when enforcement is on, No expiry otherwise. - The custom <input type="date"> uses min=today and max=now+maxDays for native bounds; submit-time validation still maps server error ids if the user bypasses the bounds. - i18n: adds preset labels; drops the now-unused picker/clear/change strings. * [MM-68421] Fix CI: playwright config and ESLint blank line - Add EnforcePersonalAccessTokenExpiry / MaximumPersonalAccessTokenLifetimeDays to e2e-tests/playwright/lib/src/server/default_config.ts so its tsc -b matches the updated ServiceSettings shape. - Drop a stray double blank line in user_access_token_section.tsx that tripped no-multiple-empty-lines. * [MM-68421] Sort i18n keys to match formatjs extract output ci/i18n-extract diffs en.json against the formatjs extractor's sorted output and fails on any difference. Re-run the extract so the new PAT-expiry keys land in alphabetical position. * [MM-68421] Address CodeRabbit findings - Clamp the default custom-expiry date to maxLifetimeDays when set so the form doesn't open in an invalid state when defaultExpiryPreset() falls back to 'custom'. - Reject a cleared/empty custom date with expires_at_required instead of silently submitting without an expiry; only forward expiresAt to the action when it's > 0. - Use an explicit undefined check in Client4.createUserAccessToken so an intentional 0 isn't dropped from the request body. * [MM-68421] Update manage_tokens_modal snapshot for expiry + status row The admin token list now renders an Expires row and a status badge per token; refresh the jest snapshot. * [MM-68421] Add unit tests for PAT expiry helpers Per the PR Test Analysis advisory: export the pure helpers from user_access_token_section.tsx and add unit tests covering them. Coverage: - deriveTokenStatus: active / expired / inactive branches. - mapServerErrorIdToMessage: all three server error ids (short and api.user.create_user_access_token.*.app_error variants) and the default null path. - endOfLocalDayPlusDays: end-of-day on the Nth future day, 0 days. - endOfLocalDayFromIsoDate: valid ISO date and malformed inputs. - PRESET_DAYS: snapshot of the preset durations. * [MM-68421] Freeze time in PAT helper tests to avoid midnight flake The date arithmetic helpers (Date.now, new Date()) could disagree across a midnight boundary, making the tests theoretically flaky. Pin system time to a stable mid-day in 2026 via jest.useFakeTimers. * [MM-68421] Add component tests for PAT expiry creation UI Covers the create-form validation branches (missing description, empty custom date, past date, beyond maxLifetimeDays), enforceExpiry rendering (no-expiry option hidden + enforced hint), maxLifetimeDays preset filtering, and token-list status display (active/expired/disabled, never, and the "expires soon" warning). * [MM-68421] Add behavioral status/expiry tests for manage_tokens_modal Covers the admin token modal's derived status display: Active + "Never" for an active token without expiry, Expired for an active token past its expiry, Disabled for an inactive token regardless of expiry, and Active with a rendered date (not "Never") for a token expiring in the future. * [MM-68421] Align PAT expiry UI with shipped server contract The server (#36706) did not ship a separate EnforcePersonalAccessTokenExpiry flag — expiry enforcement is implied by MaximumPersonalAccessTokenLifetimeDays > 0. Two webapp gaps surfaced once the server side merged: - enforceExpiry was read from the never-sent config.EnforcePersonalAccessToken Expiry, so the "hide No-expiry / require expiry" path was dead. Derive it from maxLifetimeDays > 0 instead and drop the dead config flag from the component, redux props, ClientConfig/ServiceSettings types, and the e2e default config. - The server returns app.user_access_token.expires_at_{required,in_past,too_far} .app_error, but mapServerErrorIdToMessage matched the api.user.create_user_ access_token.* namespace, so the localized errors never fired. Map the actual ids. Unit tests updated accordingly. * [MM-68421] Add Playwright e2e for PAT expiry UI Covers, against a real server, the personal access token expiry surfaces in Account Settings > Security: - the expiry picker renders all presets and reveals the custom-date input - a custom expiry with no date is blocked client-side - MaximumPersonalAccessTokenLifetimeDays > 0 hides "No expiry" and oversized presets, shows the enforced hint, and rejects an over-the-limit custom date - the token list shows Active/Never, an "expires in N days" warning, and the Disabled badge (seeded via the API) The expired-status badge is left to the component unit tests since the server rejects creating a token whose expiry is already in the past. * [MM-68421] Use locator('option') for native select assertions getByRole('option') does not reliably match the options of a closed native <select>, which would make the absence assertions (toHaveCount(0)) pass vacuously. Query option elements by DOM instead, matching the repo's house pattern for native selects. * [MM-68421] Fix expiry overshoot vs server cap and review nits Review found that presets/custom dates resolve to end-of-local-day, which can sit up to ~24h beyond the server cap of "now + MaximumPersonalAccessTokenLife timeDays" (an exact duration from creation time). With a max configured, the default preset equals the cap, so accepting the default and saving was rejected server-side with expires_at_too_far for most of the day. - Clamp the submitted expiry to the server cap when a maximum lifetime is set. Validation still runs on the raw end-of-day value so an explicitly out-of-range custom date is still rejected; only the in-range end-of-day overshoot is clamped. - Count "expires in N days" from the start of today and floor it, so an end-of-day expiry no longer over-reports by one (a 7-day token reads "7 days", not "8"). - Add an aria-label to the custom expiry date input. Tests: unit coverage for clampExpiresAtToMaxLifetime; a Playwright spec that creates a token with the default preset under a 30-day cap and asserts success. * [MM-68421] Apply prettier formatting to PAT e2e spec The ci/playwright/npm-check job (lint + prettier + tsc + lint:test-docs) failed on prettier formatting. eslint and tsc were clean; reformat the spec to satisfy prettier as well. * [MM-68421] Add PAT max-lifetime System Console setting and creation-form UX fixes - Add ServiceSettings.MaximumPersonalAccessTokenLifetimeDays number field to System Console > Integrations > Integration Management, after Enable Personal Access Tokens (disabled when tokens are disabled). - Disable the token creation Save button until a non-empty description is entered (description input is now controlled; whitespace-only rejected). - Render the token creation form as a distinct "Create New Token" card so it no longer blends into the existing token list. * Fix stylelint property order in new-token card * [MM-68421] Validate PAT expiry inline before submit Surface the expiry validation error in the create-token form and disable Save while the selection is invalid, instead of only failing inside the create-confirmation flow. Previously a system admin had to click Save then "Yes, Create" before seeing "An expiry date is required." for an empty custom date. Extracts the expiry checks into getExpiryValidationError(), reuses it as the handleCreateToken guard, and renders the result inline + in the Save button's disabled condition. * [MM-68421] Fix PAT expiry e2e specs for inline validation The "blocks submitting a custom expiry with no date chosen" and "enforces expiry when a maximum lifetime is configured" specs clicked the Save button and expected an inline error afterward. Since 7ccd65ea surfaces the expiry error inline and disables Save while the selection is invalid, the click timed out on a disabled button. Assert the inline error is visible and that Save is disabled, instead of clicking it. * MM-69075: server-side revoke non-compliant PATs Adds store queries (GetNonCompliantExpiry/CountNonCompliantExpiry), app methods to count and bulk-revoke (hard-delete) non-compliant PATs in batches with session-cache invalidation, sysadmin-gated api4 endpoints with audit logging, Client4 methods, and the OpenAPI spec. * MM-69075: System Console button to revoke non-compliant PATs Adds a 'Revoke non-compliant tokens' control under Integrations > Integration Management (next to Maximum Personal Access Token Lifetime). It shows the current non-compliant count (refreshed on load, save, and revoke), disables when there is nothing to revoke, and confirms the irreversible delete with the blast radius. Wires up the TS Client4 methods and i18n strings. * MM-69075: Address PR review feedback - Rename route from /tokens/revoke_non_compliant to /tokens/non_compliant/revoke for consistency with /tokens/non_compliant/count - Remove redundant c.LogAudit("") before permission check - Remove redundant c.LogAudit on success (structured audit record is sufficient) * MM-69075: Simplify non-compliant token revocation with single store call - Add DeleteNonCompliantExpiry store method: atomically deletes non-compliant tokens and their sessions in a single Postgres CTE, returning affected user IDs for session cache clearing - Replace the get->extract IDs->delete dance in RevokeNonCompliantUserAccessTokens with the new single store call per batch - Switch post-loop partial completion check to CountNonCompliantExpiry - Add partial completion error i18n string - Clear stale error banner in refreshCount on successful fetch * MM-69075: Remove GetNonCompliantExpiry, migrate tests to DeleteNonCompliantExpiry GetNonCompliantExpiry is now unused — DeleteNonCompliantExpiry supersedes it. Remove it from the store interface, sqlstore, retrylayer, timerlayer, and mock. Migrate the store test to exercise DeleteNonCompliantExpiry instead, adding session-deletion verification. * MM-69075: Fix DISTINCT undercounting bug and add multi-token-per-user test - Remove SELECT DISTINCT from DeleteNonCompliantExpiry CTE so each deleted token row is returned, not collapsed per user; totalRevoked now counts tokens, not users, and batch-continuation is correct - Deduplicate userIDs in app layer before ClearSessionCacheForUser calls - Add multi-token-per-user fixture to store test: two non-compliant tokens sharing a UserId verify len(userIDs)==4 and catch any future DISTINCT regression * MM-69075: Fix generated store layers and mock ordering Regenerate retrylayer/timerlayer via make store-layers and fix DeleteNonCompliantExpiry alphabetical position in mock file. * MM-69075: Inline nonCompliantExpiryWhere into its sole caller The helper was extracted to share the predicate between GetNonCompliantExpiry and CountNonCompliantExpiry. GetNonCompliantExpiry is gone; with a single call site the extracted function adds no value. * MM-69075: Fix flaky test — relax global count assertions Global count assertions (require.Equal against baseline) are fragile when other concurrent tests hold non-compliant tokens. Replace exact equality with GreaterOrEqual/LessOrEqual for global counts. The DISTINCT regression is still caught precisely: sharedUserID must appear exactly twice in the returned slice (once per token, not once per user). * MM-69075: Fix false partial-revoke error caused by read-replica lag After DeleteNonCompliantExpiry writes to master, the post-delete CountNonCompliantExpiry read targets the replica, which may not have caught up yet. This made the first revoke call return a spurious HTTP 500 even though all tokens were actually deleted, and a second click then showed 'Revoked 0' because nothing remained. Fix: track whether the batch loop exited via a natural break (all done) vs. exhausted revokeNonCompliantMaxBatches (genuinely incomplete), and signal the partial-revoke error only in the latter case. This removes the racy replica read entirely — the loop's own exit conditions prove completion on master. * MM-69075: Add Playwright e2e coverage for revoke non-compliant tokens UI Covers the gap flagged in PR review (#37030): button/disabled states, AlertBanner states, confirmation modal open/cancel/confirm, count refresh after policy save, and token auth invalidation (compliant and bot tokens survive a revoke). * MM-69075: Fix testid for number-type Maximum PAT Lifetime field Number-type TextSetting inputs use ${id}number as their test id, not ${id}input (only text-type inputs get the 'input' suffix). Found by actually running the spec locally against a server built from this branch - all 4 tests now pass. * MM-69075: Fix prettier formatting in e2e spec CI's prettier --check flagged this file; ran prettier --write to match project style. The other CI lint warnings (max-lines, no-warning-comments) are pre-existing, in files this branch never touched. * MM-69075: Fix TS2345 - UserAccessToken.token is optional The token secret field is only populated on the object returned from CreateUserAccessToken, so its type is string | undefined. Guard for that in tokenIsUsable instead of asserting non-null at every call site. --------- Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
9bc68acb8e |
E2E/Playwright: Upgrade Playwright to 1.61 and its deps (#37277)
* chore: upgrade playwright to 1.61 and its deps * fix failed tests * address comments * use assignTeamsToPolicy helper method |
||
|
|
2bb8d01100 |
Mattermost Blocks (#36338)
* Mattermost Blocks * Rethink some block props and styles * Remove obsolete comment * Style fixes and add examples * Add mention, search, and image metadata support * Several UI improvements * Validate blocks by translating them * Add initial e2e tests * Fix lint * Fix tests and lint * Fix lint * Improve e2e and simplify doPostAction * server side cleanup * Fix typo * Fix style * Fix some tests * Fix more tests * Fix cypress tests * Fix playwhright tests * Fix flaky system console test * Frontend cleanup * Add blocks editor * Fix CI * Limit all blocks width * Fix test * Enable webhook service on playwhright CI * fix flaky test * Fix style * Cleanups * Address coderabbit comments * Address coderabbit comments * Address CI errors * Fix tests * Address changes in valid actions * UX fixes * Fix test * Fix danger buttons and add allow emojis in buttons * style tweaks use semantic color tokens for buttons and accents, update max-heights for scollables, adjustments to containers, removed btn-sm (using default button size for mm blocks with height and padding adjustments for use within blocks. removed hover selector from utils to prevent hover state issue with primary button, refined semantic-color-warning. * Address feedback * Add interactive content to post previews * Add collapse transitions * Address autocomplete position * Handle columns within column sets * Fix css lint * Fix tests * Use feature flag * Address feedback * Fix lint and add missing tests * Fix e2e tests * Fix e2e test * Allow interactive posts without a message * Fix lint * Address feedback * Fix ci * Fix lint * Add limits to block translations * Fix lint * Address feedback * Address feedback * Update snapshot * Fix collapsible not defaulting to the right value * Add total text limit * Sync translations with mobile * Fix snapshot --------- Co-authored-by: Matthew Birtch <[email protected]> Co-authored-by: Mattermost Build <[email protected]> |
||
|
|
0faee9ef75 |
Migrate most remaining files to TypeScript (#36954)
* Migrate actions/emoji_actions to TS * Migrate i18n/i18n to TS * Migrate selectors/admin_console and selectors/admin_console.test to TS * Migrate actions/views/cookie to TS * Migrate actions/views/group to TS * Migrate actions/admin_actions to TS * Migrate actions/telemetry_actions, actions/announcement_bar, actions/notice, actions/search, and actions/system to TS * Migrate actions/views/mfa to TS * Migrate actions/views/posts and actions/views/textbox to TS * Migrate suggestion_box/index to TS * Rename create_selector files to TS files * Migrate create_selector files to TS * Move langmap out of channels and change it to an ES Module * Revert accidentally committed changes to authorize.test.tsx * Remove unused recomputations and resetRecomputations from createSelector * Fix lint * Merge ESLint changes |