Skip to content

[pull] master from mattermost:master#752

Merged
pull[bot] merged 4 commits into
code:masterfrom
mattermost:master
Jun 4, 2026
Merged

[pull] master from mattermost:master#752
pull[bot] merged 4 commits into
code:masterfrom
mattermost:master

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented Jun 4, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

hanzei and others added 4 commits June 4, 2026 11:45
* [MM-69026] Add zoom and pan to the image file preview

Enables the existing file-preview-modal zoom controls for image previews
(previously PDF-only) and adds cursor-aware wheel zoom, drag-to-pan,
keyboard shortcuts, and overflow clipping so the panned image can't
escape behind the modal header.

- Per-file default scale (1.0 for images, 1.75 stays for PDFs).
- Translate state alongside scale; auto-snaps to the origin at default scale.
- Native non-passive wheel listener so preventDefault actually fires.
- Wheel step scaled by deltaY magnitude for trackpad pinch.
- Keyboard: +/=, -, 0; skipped when an input/textarea/contentEditable is focused.

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

* Fix eslint no-mixed-operators and lines-around-comment

Parenthesise mixed +/* and -/ arithmetic to satisfy
eslint(no-mixed-operators) and add the blank line before the
inline-input guard comment for lines-around-comment.

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

* Cap image zoom at 2x and harden lifecycle / drag-pan

- Add ZoomSettings.MAX_SCALE_IMAGE = 2.0 and route image-path clamp
  sites through a new FilePreviewModal.getMaxScaleForFile. PDFs keep
  the original 3.0 ceiling.
- Reconcile scale/translate in getDerivedStateFromProps when
  props.fileInfos changes so newly appearing indexes get seeded with
  the file's default instead of reading as undefined.
- Gate drag-to-pan on currentScale > defaultScale so dragging at
  default scale (image fits the viewport) doesn't slide the image
  around in empty space.
- Tighten the e2e style-match regex so it only accepts scale values
  strictly greater than 1.

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

* Reset image zoom state on same-length file swaps

A websocket post update can replace an attachment at the same index
without changing the array length, which previously bypassed the
length-based reconciliation in getDerivedStateFromProps and let the
old file's zoom/translate state apply to the new file.

Track a per-index identity (file id, falling back to link) and trigger
the same reconciliation when any identity differs at its index;
indexes whose identity is unchanged keep their existing scale/translate.

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

* Add unit tests for zoom-related instance behavior and helpers

Covers the gaps flagged by Tests/analysis:

- Static helpers getDefaultScaleForFile, getMaxScaleForFile, and
  getFileIdentity (incl. namespace separation for file vs link
  identities).
- handleKeyDown: +/= zoom in, - zoom out, 0 reset, modifier-key
  bailout, and the INPUT-focus guard.
- handleImageMouseDown drag gate: ignored at default scale and on
  non-left-button mousedowns; sets isDragging when zoomed.
- handleImageWheel clamping at MAX_SCALE_IMAGE (2.0) and MIN_SCALE
  (0.25), plus deltaY=0 short-circuit.
- getDerivedStateFromProps identity reconciliation: same-length swap
  resets the affected index while preserving others; list growth
  seeds the new index with the file's default.

68 tests pass (was 50).

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ngs (#36706)

* MM-68417, MM-68420: API support for PAT expiry and admin policy settings

POST /users/{id}/tokens now accepts a client-supplied expires_at
(previously stripped per the TODO in api4/user.go), and the create app
method enforces two new ServiceSettings:

  - EnforcePersonalAccessTokenExpiry (bool, default false): when on,
    rejects creates with expires_at == 0
  - MaximumPersonalAccessTokenLifetimeDays (int, default 0 = unlimited):
    caps how far in the future expires_at may be

Rejections return distinct app error ids so clients can disambiguate:
expires_at_required, expires_at_in_past, expires_at_too_far.

GET /users/{id}/tokens already serializes expires_at via the model's
JSON tag added in MM-68419; clients derive token status (active /
expired / inactive) from is_active + expires_at without a separate
server-side field, keeping the response shape minimal.

The Client4 helper CreateUserAccessToken gained an optional variadic
expiresAt parameter (and the mmctl Client interface + mock match)
rather than introducing a parallel WithExpiry method.

Refs: MM-68417, MM-68420

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

* MM-68417: exempt bot accounts from PAT expiry enforcement

Mirrors the existing EnableUserAccessTokens bypass at session.go:453,
where bot tokens are allowed even when human PATs are disabled. Bots
are programmatic clients that typically need long-lived credentials,
and integrations that provision them would otherwise break the moment
an admin enables EnforcePersonalAccessTokenExpiry — turning a settings
toggle into a footgun. The expiry policy now applies only to human
users; bots can still be given a future expires_at by callers that
want it, but the server won't require one.

Locked in by a new TestCreateUserAccessToken/bot_tokens_are_exempt
subtest that enables enforcement plus a 30-day cap, creates a bot,
and asserts a non-expiring token is accepted.

Refs: MM-68417

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

* MM-68420: bound MaximumPersonalAccessTokenLifetimeDays in isValid

Negative values silently meant "unlimited" (the runtime check is `> 0`),
and very large values overflow int64 when computing
`now + days*86_400_000` at token-creation time, producing a wrap-around
that either rejects all reasonable expiries or accepts past timestamps
as valid.

Bound the setting in ServiceSettings.isValid to [0, MaxPersonalAccess
TokenLifetimeDays] where the cap is 36500 (100 years) — past any
realistic operational use and well clear of int64 overflow. Surfaces
as a config validation error rather than a silently-broken runtime
check. New TestServiceSettingsIsValid cases lock in zero, negative,
upper-bound, and above-upper-bound behavior.

Refs: MM-68420

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

* MM-68417: reject multiple expiresAt values in Client4 helper

CodeRabbit caught that the variadic CreateUserAccessToken silently used
expiresAt[0] when callers passed more than one value, masking a
mis-call instead of failing fast. Return an error in that case so the
misuse is visible at the call site rather than producing a token with
the wrong (or right-but-coincidental) expiry.

Refs: MM-68417

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

* MM-68417: make expiresAt a required parameter on Client4.CreateUserAccessToken

Drop the variadic in favor of a regular int64 parameter. The variadic
form silently dropped extra values and made a misuse undetectable at
the call site (per CodeRabbit review on PR 36706); the previous fix
guarded against >1 values at runtime, but a required parameter is
strictly better — the compiler now refuses the misuse and every caller
is forced to make a deliberate decision about expiry. Existing callers
that want the old behavior pass 0 (== never expires).

Refs: MM-68417

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

* MM-68417: add --expires-in flag to mmctl user token generate

Adds an --expires-in duration flag so operators can create expiring
PATs via the CLI. Accepts the standard Go duration syntax plus a
trailing 'd' for days (the common case for token lifetimes), e.g.
--expires-in 90d, --expires-in 12h, --expires-in 1h30m. Empty (the
default) means no expiry — matching prior behavior. Without this
flag the command was unusable once an admin enables
EnforcePersonalAccessTokenExpiry, since every create would fail with
app.user_access_token.expires_at_required.app_error.

Flag parsing now happens before the user-lookup API call so the
command fails fast on invalid input. Regenerated mmctl docs reflect
the new flag.

Refs: MM-68417

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

* MM-68417: cap --expires-in day count to prevent time.Duration overflow

parseExpiresIn returns time.Duration(days) * 24 * time.Hour, which is
int64 nanoseconds and overflows past ~106751 days (CodeRabbit caught
this). Cap at model.MaxPersonalAccessTokenLifetimeDays (36500) so the
CLI rejects values the server would reject anyway, well below the
int64-overflow point. Adds two test cases (at-cap and beyond-cap) to
TestParseExpiresIn.

Refs: MM-68417

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

* MM-68420: collapse PAT expiry settings into a single max-lifetime setting

Remove ServiceSettings.EnforcePersonalAccessTokenExpiry and fold the
policy onto MaximumPersonalAccessTokenLifetimeDays: 0 means no policy
(never-expiring tokens allowed, no cap), while a value > 0 requires every
new token to expire within that many days. The two-setting design let an
admin set a maximum but leave enforcement off, silently allowing
never-expiring tokens to sidestep the cap; the only combination the
boolean added (require expiry, no upper bound) has little practical
value. The removed field was introduced on this branch and never
released, so this is not a breaking change.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…36875)

* [MM-69115] Fixed issue where channels could end up in two categories

* Additional fix

* PR feedback
@pull pull Bot locked and limited conversation to collaborators Jun 4, 2026
@pull pull Bot added the ⤵️ pull label Jun 4, 2026
@pull pull Bot merged commit 3fc5b94 into code:master Jun 4, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants