Wekan
Open source kanban board application built with Meteor
Alternative to: trello
About
Versions (100)
v9.70
2026-06-23v9.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 didoldBoard.labels.filter(...)/filterCopiedLabelIds(newBoard.labels, …)without guarding a missinglabelsarray. Most boards always have default labels, but a board created via the REST API (POST /api/boards) has nolabelsarray — so copying a card to/from such a board (or copying the board itself, which copies its cards) threwCannot read properties of undefined (reading 'filter')and returned HTTP 500. Both label lookups now fall back to[]. Cross-board copy regressions intests/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 —boardRemoverdeletes them byboardId).copy()now also copies the board’s Integrations, remappingboardId(URL/token/activities carry over — the copying user is a board admin with access to them). Regression intests/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
loginDisabledflag, so a deactivated user could be invited/added and assigned to cards.inviteUserToBoardnow throwserror-user-disabled, and the RESTPOST /api/boards/:boardId/members/:userId/addendpoint returns HTTP 400, when the target account is disabled (re-enabling the account allows the add again). Regression intests/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 inboard.membersfor 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 rawGET /api/boards/:boardIdresponse intentionally still returns the fullmembersarray with each entry’sisActiveflag, 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’suserId, not the user document), so orphaned card members can be removed. (No automated regression — a self-evident Blaze template guard; the underlyingunassignMemberremoval already worked. The board-members list still hides such entries becauseactiveMembers()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 toactiveMembers()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 hookedinsertAsync, so collection-hooks fired each item’safter.insert— every one doing agetCardand inserting anaddChecklistItemactivity. 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.directfor the checklist and its items (skipping the per-item activity hooks — pure churn for a wholesale copy) and setsboardIditself, the same approach as thecardRemoverdelete path. Copy regression (checklist + all items duplicated) intests/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’sparentIdto the copied card but left itsboardId/swimlaneId/listIdpointing 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 intests/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
activityObjwas 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.updatehook that logged asetCustomFieldactivity by reading(await getActivity({customFieldId})).value— with no null guard, so a field that never had a value set (no such activity) threwCannot read properties of undefinedand 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 sharedcollapsedfield 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
membersreferenced a user not present inusers): the importers already guard a missing user entry (skip the dangling member instead of dereferencingundefined) inclient/components/import/wekanMembersMapper.js,models/wekanmapper.jsandmodels/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 returnstruefor any board admin (board.hasAdmin), and the secure default invite roles includeboard-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 (remappingboardIdand the rule’striggerId/actionId); the report predates that code. Now covered by the #5592/#5627 copy regression intests/playwright/specs/17-rest-api.e2e.jsso 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-layouthandler no longer reads those fields. - #5117 (a TeX formula rendered both an SVG and a
<math>tag): already fixed by migrating math rendering frommarkdown-it-mathjax3(which emitted both SVG and MathML) to Temml, which outputs MathML only (packages/markdown/src/template-integration.js).