Skip to content

wekan

v9.70 Breaking

This release includes breaking changes for platform teams planning a safe upgrade.

Published 1mo Productivity & Wikis
✓ No known CVEs patched
Read the diff → Tool health → What is this tool? →

✓ No known CVEs patched in this version

Topics

docker javascript kanban meteor real-time sandstorm
+2 more
snapcraft wekan

Affected surfaces

auth rbac

Summary

AI summary

Updates https://github.com/wekan/wekan/issues/5592, https://github.com/wekan/wekan/issues/1894, and https://github.com/wekan/wekan/issues/5122 across a mixed release.

Full changelog

v9.70 2026-06-23 WeKan ® release

This release fixes the following bugs:

  • Copying a card to or from a board with no labels threw. Card.copy()'s cross-board label-remap did
    oldBoard.labels.filter(...) / filterCopiedLabelIds(newBoard.labels, …) without guarding a missing labels array.
    Most boards always have default labels, but a board created via the REST API (POST /api/boards) has no labels
    array — so copying a card to/from such a board (or copying the board itself, which copies its cards) threw
    Cannot read properties of undefined (reading 'filter') and returned HTTP 500. Both label lookups now fall back to
    []. Cross-board copy regressions in tests/playwright/specs/17-rest-api.e2e.js.
  • Copying a board did not copy its webhooks,
    #5592: board.copy() duplicated swimlanes/lists/cards, custom fields
    and rules/triggers/actions, but had no loop for Integrations (outgoing webhooks), so a copied board lost all of
    them (even though they are per-board children — boardRemover deletes them by boardId). copy() now also copies
    the board's Integrations, remapping boardId (URL/token/activities carry over — the copying user is a board admin
    with access to them). Regression in tests/playwright/specs/17-rest-api.e2e.js.
  • Disabled user accounts could be added to boards,
    #1894: none of the add-member paths checked the target account's
    loginDisabled flag, so a deactivated user could be invited/added and assigned to cards. inviteUserToBoard now
    throws error-user-disabled, and the REST POST /api/boards/:boardId/members/:userId/add endpoint returns HTTP 400,
    when the target account is disabled (re-enabling the account allows the add again). Regression in
    tests/playwright/specs/17-rest-api.e2e.js.
  • Admin Panel boards report listed removed members as current members,
    #5122: removing a member from a board marks the member entry
    isActive: false (it is kept in board.members for role history / re-activation), but the Admin Panel → Reports →
    Boards member column listed all member entries, so removed users still appeared as members. The report now
    filters to active members (isActive !== false). (The raw GET /api/boards/:boardId response intentionally still
    returns the full members array with each entry's isActive flag, so API consumers can filter as they need.)
  • Could not remove a deleted user from a card's members,
    #4847 (card side): when a user account is deleted, its entry stays in a
    card's members, rendering as a blank avatar. Clicking it opened the member popup, whose template dereferenced the
    now-missing user document (user.profile.fullname / user.username) and failed to render — so there was no way to
    remove the orphaned member. The popup now detects a missing user, shows a "Deleted user" entry with the raw id, and
    keeps the Remove from Card control (the remove handler keys off the member's userId, not the user document), so
    orphaned card members can be removed. (No automated regression — a self-evident Blaze template guard; the underlying
    unassignMember removal already worked. The board-members list still hides such entries because activeMembers()
    also intentionally filters members whose user doc is merely not-yet-loaded, so surfacing board-side orphans cleanly is
    a separate follow-up.)
  • A newly added board member was missing from the card members popup,
    #4965: the card "add members" popup snapshotted the board's member list
    once when it opened, so a member added to the board afterwards (or whose user document finished loading just after
    the popup opened) did not appear until the popup was reopened. The popup now derives its candidate list reactively
    (it stores only the filter term and re-reads board.activeMembers() on each render), so newly-added members show up
    without reopening. No change to activeMembers() itself (its deleted-user filtering is unchanged).
  • Editing a linked card you cannot write to failed silently,
    #5809: a linked card whose target lives on a board the user cannot
    write to (e.g. a private board) rejected title/description edits at the server allow rule, but the card-detail edit
    handlers had no error handling, so the edit just vanished with no feedback. The title and description submit handlers
    now catch the failure and show the error (mirroring the existing label-color handler), so the user sees why the edit
    did not save. (No automated regression — surfacing a permission denial across a private linked board is a UX/error
    path that is not cleanly reproducible in the test harness.)
  • Performance: copying a card with many checklist items took minutes (#5688).
    Checklist.copy() duplicated each checklist item with the hooked insertAsync, so collection-hooks fired each item's
    after.insert — every one doing a getCard and inserting an addChecklistItem activity. Copying a card with ~100
    items meant ~100+ activity inserts (plus per-item DB round-trips), taking minutes and spiking CPU. The copy now uses
    .direct for the checklist and its items (skipping the per-item activity hooks — pure churn for a wholesale copy) and
    sets boardId itself, the same approach as the cardRemover delete path. Copy regression (checklist + all items
    duplicated) in tests/playwright/specs/17-rest-api.e2e.js.
  • Copying a card to another board orphaned its subtasks on the old board
    (#5347 data symptom): Card.copy() re-pointed each subtask's
    parentId to the copied card but left its boardId/swimlaneId/listId pointing at the source board — so a
    cross-board copy created subtasks stranded on the original board whose parent lives on another board (and it mutated
    the cached source docs in place). Copied subtasks are now re-homed onto the destination board/swimlane/list alongside
    the copied parent, via a fresh object (no cache mutation). Cross-board copy regression in
    tests/playwright/specs/17-rest-api.e2e.js. (This fixes the orphaned-cross-board-subtask data symptom noted in
    #5347; the separate "Maximum call stack" error there is not yet root-caused — see the issue.)
  • Error when clicking the notification icon,
    #5325: a notification whose referenced activity no longer existed (its
    card/board was deleted) left an entry whose activityObj was null, and the notifications drawer dereferences it
    (activity.user, activity._id, …) — so one orphaned notification threw and broke the whole popup. user.notifications()
    now drops entries whose activity can't be resolved. (No automated regression — an orphaned-notification state is not
    cleanly reproducible in the harness; the fix is a self-evident filter.)
  • Deleting a custom field from a board could throw,
    #5390: removing a (multi-board) custom field from one board runs a
    before.update hook that logged a setCustomField activity by reading (await getActivity({customFieldId})).value
    with no null guard, so a field that never had a value set (no such activity) threw Cannot read properties of undefined and aborted the removal. The lookup is now guarded. (server/models/customFields.js)
  • Board "show checklists on minicard" setting had no effect,
    #5565: the sidebar toggle writes board.allowsChecklistsOnMinicard, but
    the minicard render checked a different, UI-less field (board.allowsChecklistAtMinicard), so enabling the board-wide
    setting never showed checklists on minicards. The minicard now reads the field the toggle actually sets.
    (client/components/cards/minicard.js)

and these issues are verified resolved in current code (could not reproduce / no error observed here; re-test on the
reporter's data requested):

  • #5388 (collapsing a list affected all users): list collapse state is
    now stored per user (profile.collapsedLists[boardId][listId], or a cookie for logged-out users) instead of a
    shared collapsed field on the list document — so one user collapsing a list no longer changes it for everyone
    (resolved by commit 414b8dbf4, which postdates the report; mirrors the per-user swimlane-collapse handling).
  • #3894 (board import failed when the JSON's members referenced a user
    not present in users): the importers already guard a missing user entry (skip the dangling member instead of
    dereferencing undefined) in client/components/import/wekanMembersMapper.js, models/wekanmapper.js and
    models/wekanCreator.js, with a dedicated test (tests/wekanCreator.inconsistent.test.js).
  • #5411 (non-super-admin board admins could not see the add-member "+"):
    the sidebar add-member button is gated on currentUser.canInviteToBoard, which returns true for any board admin
    (board.hasAdmin), and the secure default invite roles include board-admin. So board admins (not just site admins)
    see the "+". This was resolved by the "Allow Invite to Board" roles feature (commit c956ab5a4), which postdates the
    report; a site admin can additionally let other board roles invite via Admin Panel → People → Roles.
  • #5627 (rules not copied when creating a board from a template):
    board.copy() already copies the board's rules + triggers + actions (remapping boardId and the rule's
    triggerId/actionId); the report predates that code. Now covered by the #5592/#5627 copy regression in
    tests/playwright/specs/17-rest-api.e2e.js so it cannot silently regress.
  • #5630 (cannot save Admin Panel Layout settings): already fixed — the
    Layout save handler had referenced form fields that were moved to the separate Accessibility settings template,
    throwing before the save; the current js-save-layout handler no longer reads those fields.
  • #5117 (a TeX formula rendered both an SVG and a <math> tag): already
    fixed by migrating math rendering from markdown-it-mathjax3 (which emitted both SVG and MathML) to Temml, which
    outputs MathML only (packages/markdown/src/template-integration.js).

Weekly OSS security release digest.

The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.

No spam, unsubscribe anytime.

Share this release

Track wekan

Get notified when new releases ship.

Sign up free

About wekan

The Open Source kanban, built with Meteor. GitHub issues/PRs are only for FLOSS Developers, not for support, support is at https://wekan.fi/commercial-support/ . New English strings for new features at imports/i18n/data/en.i18n.json . Non-English translations at https://app.transifex.com/wekan/wekan only.

All releases →

Related context

Related tools

Beta — feedback welcome: [email protected]