Skip to content

wekan

v9.81 Breaking

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

Published 18d 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

ReleasePort's take

Light signal
editorial:auto 10d

Wekan v9.81 introduces several UI enhancements and API improvements, including alphabetically sorted link‑card dropdowns, validated color editing via REST, native color pickers with contrast calculation, and expanded rule date granularity.

Why it matters: These changes improve usability (sorted menus, clearer icons) and reliability (validated API payloads, consistent webhook triggers), affecting all board users and integrators; no migration deadline or CVE is involved.

Summary

AI summary

Updates https://github.com/wekan/wekan/commit/b07c4d0a7bed0f03ba2d7d1ad1f030e7edf3dfff, https://github.com/wekan/wekan/commit/7ce71dbb2e1eae5159503eefac7e6f349dfb59c8, and https://github.com/wekan/wekan/commit/30f36a7f319675bd3aac7a4ba0700427000726b9 across a mixed release.

Changes in this release

Feature Medium

Link-card popup's Cards dropdown is now sorted alphabetically by card title.

Link-card popup's Cards dropdown is now sorted alphabetically by card title.

Source: llm_adapter@2026-07-16

Confidence: high

Feature Medium

Lists can be edited via REST API `PUT /api/boards/:boardId/lists/:listId` with validated color values.

Lists can be edited via REST API `PUT /api/boards/:boardId/lists/:listId` with validated color values.

Source: llm_adapter@2026-07-16

Confidence: high

Feature Medium

Color pickers now include a native `<input type="color">` wheel and compute readable text contrast.

Color pickers now include a native `<input type="color">` wheel and compute readable text contrast.

Source: llm_adapter@2026-07-16

Confidence: high

Feature Medium

Rules can set a date field with custom time units (minute, hour, day, week, month).

Rules can set a date field with custom time units (minute, hour, day, week, month).

Source: llm_adapter@2026-07-16

Confidence: high

Bugfix Medium

Users are auto‑added to organizations matching their email domain on sign‑up.

Users are auto‑added to organizations matching their email domain on sign‑up.

Source: llm_adapter@2026-07-16

Confidence: high

Bugfix Medium

Activities show/hide control now uses clear eye/eye‑slash icons with proper tooltips.

Activities show/hide control now uses clear eye/eye‑slash icons with proper tooltips.

Source: llm_adapter@2026-07-16

Confidence: high

Bugfix Medium

Outgoing webhooks now include the label name on add/remove label events.

Outgoing webhooks now include the label name on add/remove label events.

Source: llm_adapter@2026-07-16

Confidence: high

Bugfix Medium

Editing a card description now triggers outgoing webhooks.

Editing a card description now triggers outgoing webhooks.

Source: llm_adapter@2026-07-16

Confidence: high

Bugfix Medium

Rules Workflow view is now fully translatable via i18n.

Rules Workflow view is now fully translatable via i18n.

Source: llm_adapter@2026-07-16

Confidence: high

Bugfix Medium

Deleting a board rule no longer fails with "Access denied [403]".

Deleting a board rule no longer fails with "Access denied [403]".

Source: llm_adapter@2026-07-16

Confidence: high

Bugfix Medium

REST API endpoint PUT /api/boards/:boardId/labels now always returns JSON responses and validates input.

REST API endpoint PUT /api/boards/:boardId/labels now always returns JSON responses and validates input.

Source: granite4.1:30b@2026-07-16-audit

Confidence: low

Bugfix Low

CSV export no longer crashes on boards with dangling references; missing data yields blank cells.

CSV export no longer crashes on boards with dangling references; missing data yields blank cells.

Source: granite4.1:30b@2026-07-16-audit

Confidence: low

Bugfix Low

Calendar view now honors active board filters (member, assignee, due date, label, custom field).

Calendar view now honors active board filters (member, assignee, due date, label, custom field).

Source: granite4.1:30b@2026-07-16-audit

Confidence: low

Bugfix Low

All Boards "Custom (drag order)" drop operation now persists the reorder of boards.

All Boards "Custom (drag order)" drop operation now persists the reorder of boards.

Source: granite4.1:30b@2026-07-16-audit

Confidence: low

Bugfix Low

Cards on a deleted swimlane are now visible in swimlane view by appearing in the first swimlane.

Cards on a deleted swimlane are now visible in swimlane view by appearing in the first swimlane.

Source: granite4.1:30b@2026-07-16-audit

Confidence: low

Full changelog

v9.81 2026-07-09 WeKan ® release

This release adds the following new features:

  • Feature #5394: the Link-card popup's Cards dropdown is now sorted alphabetically:
    In the "Link to this card" popup, the Cards pull-down list is now sorted
    alphabetically by card title (case-insensitive, locale/numeric aware)
    instead of board sort order, so a card can be found on boards with many
    cards. Thanks to xet7.
  • Feature #5396: edit Lists (title, color) via the REST API + api.py commands:
    Lists can now be edited through the REST API like cards can. The endpoint
    PUT /api/boards/:boardId/lists/:listId already accepted title, color,
    starred and wipLimit, but the color was stored unvalidated; it now
    validates the color with normalizeListColor (a named palette color or a
    custom #rrggbb hex) and rejects an unknown color with a clear 400 instead
    of silently storing None. The pure field/validation logic lives in a
    Meteor-free helper models/lib/listApiUpdate.js and is unit tested in
    tests/listApiUpdate.test.cjs. The api.py reference CLI gains two new
    commands mirroring editcard/editcardcolor: editlist BOARDID LISTID NEWLISTTITLE and editlistcolor BOARDID LISTID COLOR. Thanks to C0rn3j and xet7.
  • Feature #5514: custom color-wheel (RGB/hex) picker with automatic readable text contrast:
    The color pickers that previously offered only a fixed set of named colors now also include a native
    color wheel (<input type="color">), so any #rrggbb color can be chosen. The wheel was added
    alongside the existing swatches for card labels ("categories"), swimlanes ("tabs"), lists and cards.
    The schema color fields accept a custom hex in addition to the named palette, and existing named-color
    data keeps working; a stored hex is rendered with an inline background-color instead of the named
    CSS class. A new pure, Meteor-free helper models/lib/contrastColor.js computes a readable text color
    from sRGB relative luminance (white text on dark backgrounds, black on light), maps the named palette
    to hex, and validates/normalizes hex; it is applied as an inline text color wherever a chosen color is
    a background behind text (label chips, swimlane / list headers, minicard, card details header), so text
    stays readable on any color. Covered by tests/contrastColor.test.cjs (20 assertions pass).
    Thanks to Ruyeex and xet7.
  • Feature #5621: Rules can set a date field to a custom time (value + minute/hour/day/week/month later):
    The Rules "Set date relative to now" action previously only offset a date field (Start / Due / End /
    Received) by a whole number of DAYS. It now has a unit selector, so a rule can set a date field to
    now + <value> <unit> later, where the unit is minute(s) / hour(s) / day(s) / week(s) / month(s);
    negative values move the date earlier. Months use a real calendar-month add (e.g. keeping the same
    day-of-month) rather than a fixed 30-day approximation. Existing rules created before this change
    have no stored unit and keep working exactly as before (no unit ⇒ days), so the change is fully
    backward compatible. The offset math lives in a new pure, Meteor-free helper
    models/lib/relativeDateOffset.js used by server/rulesHelper.js and covered by
    tests/relativeDateOffset.test.cjs (15 assertions pass). Note that the related "overdue" Rules
    trigger and the "set date field to now" action from the same feature request already existed in an
    earlier release; this change adds only the missing custom-time unit selector. Thanks to xet7.

and fixes the following bugs:

  • Fix #5351: users are auto-added to organizations matching their email domain on sign-up: the Organization setting "Automatically add users with the domain name" (org.orgAutoAddUsersWithDomainName) could be configured, but nothing at sign-up ever read it, so a new user whose email domain matched an organization was never added to it; the Accounts.onCreateUser hook now, on every non-admin sign-up path (password registration, LDAP, invitation code, new OIDC user), adds the user to each organization whose configured domain exactly matches the domain part of their email (case-insensitive, exact — a subdomain does not match and an empty org domain matches nobody), using the same { orgId, orgDisplayName } membership shape used everywhere else and never duplicating an existing membership, with the matching decision extracted into a Meteor-free, unit-tested orgsToAutoAddForEmail helper.
    Thanks to xet7.
  • Fix #5369: the Activities show/hide control is now a clear eye / eye-slash icon toggle: the Activities panel's show/hide control was a generic, unlabeled material toggle switch whose ON/OFF meaning was counterintuitive; it is replaced on the card details Activities panel with an eye / eye-slash icon toggle (open eye = activities shown, crossed eye = activities hidden) that mirrors the login/register password-visibility toggle, so the icon reflects the current visibility, clicking flips it, and the tooltip states the action (Show activities / Hide activities). For consistency the board sidebar Activities toggle, which drives the same showActivities state, was switched from the check / empty-square icon to the same eye / eye-slash toggle. The underlying show/hide setting and its persistence are unchanged.
    Thanks to xet7.
  • Fix #5442: outgoing webhooks now include the label name on add/remove label: the addedLabel/removedLabel outgoing webhook (and notification) text showed a bare, generic "label" with no name, because labels are embedded in the board document but the Activities label() helper looked the label id up in the Cards collection and always returned undefined, so the __label__ token was never filled; the hook now resolves the label from the already-loaded board via getLabelById and a pure, unit-tested labelDisplayName helper (name, then color for a nameless label as shown in the UI, then the id), so the webhook always carries the label's display name.
    Thanks to xet7.
  • Fix #5482: adding/editing a card description now triggers outgoing webhooks: outgoing webhooks fire only when an operation logs an activity (the Activities.after.insert hook posts to the board's webhooks), but changing a card's description created no activity — unlike title/date changes — so no webhook was sent; the Cards before.update hook now logs an a-changedDescription activity on first-time set and later edits (but not on no-op / empty-to-empty saves), so the existing webhook hook fires, with a Meteor-free unit-tested descriptionChanged helper.
    Thanks to xet7.
  • Fix: the Rules Workflow view is now fully translatable via i18n: the Rules Workflow view rendered its trigger/action palette chips ("Card is created", "Move card to top", "Set received date to now", etc.) as hardcoded English regardless of the UI language, while the rest of the page was translated; each palette entry now carries an i18n key translated at render time via TAPi18n.__ (reusing existing rule keys where they fit, plus new r-w-* keys for workflow-only labels), and the slot clear tooltip now uses the existing r-remove key, so the whole view follows the selected language.
    Thanks to xet7.
  • Fix: deleting a board rule no longer fails with "Access denied [403]": deleting a rule ran three separate client-side Collection.remove() calls (Rules + Triggers + Actions), each gated by a per-collection allow() rule that resolved the board from that document's own boardId; when a trigger/action document had no resolvable boardId (legacy docs, or docs not published to the client) the board came back null, allowIsBoardAdmin returned false, and Meteor rejected the mutation with 403 "Access denied" — so the delete failed even for a legitimate board admin. Rule deletion now goes through a new server method rules.deleteRule that authorizes once (active board admin or site admin) and removes the rule, its trigger and its action server-side, bypassing the brittle client allow/deny; the permission decision is a Meteor-free, unit-tested helper and no permission is loosened.
    Thanks to xet7.
  • Fix #5510: adding a board label via the REST API no longer errors/hangs: PUT /api/boards/:boardId/labels only sent a response when the body had a label key, so a body without one hung until the client timed out, and a bare-string label pushed a schema-invalid label and returned 200; the handler now always returns JSON (2xx on success, 4xx on bad input, real error status otherwise) via a pure, unit-tested input helper.
    Thanks to xet7.
  • Fix #5604: CSV export no longer crashes on boards with dangling references: exporting a large/old board to CSV failed with "Couldn't download - Network issue" because Exporter.buildCsv read .title/.username/.name directly on the result of looking up a card's deleted list/swimlane/owner/member/assignee/label/customField by id, throwing "Cannot read property 'title' of undefined"; the per-card row builder is now a null-safe helper that emits a blank cell for missing references while keeping identical output for well-formed cards.
    Thanks to xet7.
  • Fix #5656: the Calendar view now honors the active board filters: the Calendar view queried cards by board and date only and ignored the active Filter sidebar (member / assignee / due-date / label / custom-field), so it showed every card in the interval unlike the Board / Lists / Swimlanes views; it now ANDs the Filter selector into its query and refetches when the filter changes.
    Thanks to kerier and xet7.
  • Fix #6442: All Boards "Custom (drag order)" — drop now persists the reorder:
    Follow-up to #6439, which restored the drag preview but left the drop a no-op: dragging a board on
    the All Boards page in Custom (drag order) mode showed the dashed-border preview, but releasing it
    snapped the board back without reordering. The drop handler built the current on-screen order from
    el.classList[0] of each .js-board, but the item is li.js-board(class="{{_id}} …") and Jade
    emits the literal js-board class FIRST, so classList[0] was the string "js-board" for every
    board — never the board _id. The ordered ids were therefore ['js-board','js-board',…], the
    (correct, unit-tested) computeReorderedSortIndex helper could not find the dragged/target ids
    among them, returned null, and nothing was written to profile.boardSortIndex; the preview still
    worked because it is driven by the dragover CSS class, independent of the id. The fix reads each
    board's _id from its Blaze data context (the same source dragstart uses via this._id) instead
    of the literal class, so the real display order reaches the reorder helper and the drop persists.
    Guarded by a new case in tests/boardSortReorder.test.cjs (the wrong-class extraction yields no
    mapping; real ids reorder).
    Thanks to jullbo and xet7.
  • Fix #6443: cards on a deleted swimlane are invisible in swimlane view:
    On some old boards a swimlane was deleted while its cards kept the now-dangling swimlaneId (an
    "orphaned" card), so those cards showed no content in swimlane mode even though they worked in list
    mode. Cards with no swimlane at all (null / '' / missing) already appear in every swimlane, but
    an orphaned card matched no existing swimlane and so was visible in NO swimlane (while list view,
    which applies no swimlane scope, still showed it) — exactly the reported symptom. The fix mirrors
    the existing orphaned-list fallback (Swimlanes.orphanedSwimlaneLists, which surfaces orphaned
    lists in the first swimlane) for cards: when a list's cards are fetched for the board's FIRST
    swimlane, the swimlane-membership clause becomes a single { swimlaneId: { $nin: <otherSwimlaneIds> } }
    (everything not owned by another existing swimlane: own id, null/'', missing, or orphaned). It
    stays a single field clause with no second $or, so the #6441 board-wide label filter still holds,
    and orphaned cards appear once — in the first swimlane — without a database migration. Threaded
    through the pure models/lib/swimlaneFilter.js helpers, the in-memory filterCardsByListAndSwimlane,
    a new List.orphanedCardsSwimlaneIds helper and the cards()/cardsUnfiltered()/allCards() model
    methods plus the listBody cardsWithLimit render helper. Covered by new cases in
    tests/swimlaneFilter.test.cjs (18 assertions pass; the #6441 regression guards stay intact).
    Thanks to xet7.

Thanks to above GitHub users for their contributions and translators for their translations.

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]