Wekan logo

Wekan

Open source kanban board application built with Meteor

Alternative to: trello


About Versions (100)

v9.69

2026-06-23

v9.69 2026-06-23 WeKan ® release

This release fixes the following bugs:

  • Can’t edit a card’s members in the UI after removing them via the REST API, #3697: clearing a card’s members (or assignees) over REST left the field uneditable in the UI. The card PUT handler guarded with if (req.body.members) — falsy for the two natural clear payloads (null and "") — so “remove the last member” was a silent no-op, and its string branch was written to store null rather than []; a card with members: null then breaks UI code that treats it as an array. The handler now uses an !== undefined guard plus a shared coercion helper so any clear payload (null / "" / []) stores a clean String[] (never null), a single id is wrapped into an array, and stray non-string entries are dropped; getMembers()/getAssignees() also coerce a legacy null to [] on read so existing corrupted documents edit cleanly. Pure logic in models/lib/restArrayParam.js with a Meteor-free Node unit test (tests/restArrayParam.test.cjs, npm run test:unit:node, incl. a negative test reproducing the old null-writing logic), plus a REST regression in tests/playwright/specs/17-rest-api.e2e.js.
  • Can’t create a card with no member via the REST API, #2875: the card-create endpoints (POST .../cards and POST .../cards/bulk) wrote req.body.members/assignees straight to the insert with no normalization — the create-side twin of #3697 — so a null/"" payload persisted as null (breaking later UI editing). Both handlers now run the same coerceRestArrayParam helper: the field is omitted when not provided (so the schema default [] applies), any clear payload becomes [] (never null), and a single id is wrapped into an array. REST regression in tests/playwright/specs/17-rest-api.e2e.js.
  • Board created through the REST API shows in the API but not in the browser UI, #5650: POST /api/boards set the board’s sole member’s userId from req.body.owner with no fallback, so a request that omits owner created a board whose only member had userId: undefined. The board-list publication matches members.$elemMatch: { userId, isActive: true }, which can never match an undefined id — so the board was returned by the REST API but invisible in the browser. owner now falls back to the authenticated caller (req.body.owner || req.userId), mirroring the Meteor create method. REST + DB regression in tests/playwright/specs/17-rest-api.e2e.js.
  • Copying a card to another board left its comments on the wrong board, #5166: CardComments.copy() cloned a comment but only changed its cardId, so a card copied to another board produced comments that kept the source board’s boardId. Comment permission checks key off the comment’s boardId, so edit/delete on a copied comment was validated against the wrong board, and any board-scoped query saw it on the old board. copy() now also sets the destination boardId (the author userId is intentionally preserved). Cross-board copy regression in tests/playwright/specs/17-rest-api.e2e.js. Note: the related “wrong author shown” symptom is a separate display issue — when a copied comment’s author is not a member of the destination board, their user document isn’t published there, so the UI can’t resolve the name; adding those users to the board resolves it. A broader fix (publishing comment authors’ minimal profile on boards where their comments appear) is left as a follow-up.
  • Exception “Removed nonexistent document” when deleting a card detail, #3252 (partial): deleting a comment or checklist/checklist-item could throw Removed nonexistent document on the client. The delete handlers called Collection.remove(_id) directly, but under heavy archive/delete churn the target doc can already have been evicted from the client’s Minimongo cache, and removing a missing _id throws. The comment / checklist / checklist-item delete handlers now check the doc still exists in the local cache before removing it (client/components/activities/comments.js, client/components/cards/checklists.js). The server-side before.remove hooks were already hardened to guard + log instead of throwing. (No automated regression for the thrown exception — the eviction race is not deterministically reproducible; the guard itself is a self-evident findOne-then-remove.) The high CPU on bulk delete the issue also reports is reduced by the cascade change below; the remaining cost is on the archive path (a bulk update, not a delete) and is a separate follow-up.
  • Performance: deleting a card no longer fans out per-child activity churn (#3252, #5322). cardRemover removed a card’s checklist items, checklists and comments with the hooked removeAsync, so collection-hooks fired each child’s before.remove once per document — every one doing a getCard and inserting an activity (e.g. a removedChecklistItem per item), which then triggered the notification + publication observers. Deleting (or bulk-deleting) a card with many children produced an activity-insert storm and pegged the CPU. cardRemover now removes those children with .direct (skipping the per-child hooks — they only logged activities that are unviewable once the card is gone) and clears all of the card’s activities in a single bulk op, which also removes the activities that a delete previously left orphaned. The deleteCard activity is still logged afterward (so the outgoing webhook fires), and attachments still go through the normal remove so their files are deleted. Cascade + activity-cleanup regression (also a negative test) in tests/playwright/specs/17-rest-api.e2e.js.

and this issue is verified resolved in current code (could not reproduce / no error observed here; re-test on the reporter’s data requested):

  • #2292 (archiving a swimlane appeared to delete all its cards): Swimlane.archive() (models/swimlanes.js) only sets archived: true on the swimlane — it does not touch or delete the cards, and restore() brings the swimlane and its cards back. While a swimlane is archived its cards are merely hidden from the board view (board queries filter to archived: false swimlanes), not lost — the v2.27-era cascade described in the report no longer happens. (A dedicated way to view an archived swimlane’s cards before restoring would be a separate UX improvement.)