Skip to content

Releases: wekan/wekan

v9.89

Choose a tag to compare

@github-actions github-actions released this 13 Jul 16:33

v9.89 2026-07-13 WeKan ® release

This release fixes the following CRITICAL SECURITY ISSUE of
SortBleed:

  • SortBleed:
    a low-privilege (comment-only / read-only) board member could escalate to board admin and take
    over a private board via the board sort collection-allow rule

    (GHSA-xm8x-c8wg-jhmf,
    CWE-863 Incorrect Authorization, CWE-269 Improper Privilege Management). Same broken-access-control
    class as BoardBleed (CVE-2026-55234) — a Meteor
    collection allow-rule field conflation — but on the Board document itself. To support drag-to-reorder
    on the All Boards / Public Boards pages, a second Boards.allow({ update }) rule returned true for
    any board member whenever the update touched the sort field. Meteor evaluates allow rules with OR
    semantics and does not scope an approving rule to the field that satisfied it: once any allow
    callback returns true and no deny callback returns true, the entire modifier is applied.
    Because canUpdateBoardSort only checked that sort was among the modified fields (not that it
    was the only one), a comment-only / read-only member could smuggle arbitrary board mutations into
    the same $set as sort in a single unprivileged DDP Boards.update call:
    {$set: {sort: 99, members: [...only themselves as admin...], permission: 'public', title: '...'}}.
    The member could therefore make themselves board admin, flip a private board to public (world-readable
    in Wekan), rename it, and evict the legitimate owner. The last-admin deny rule did not help because it
    only inspected $pull, so a wholesale $set of the members array bypassed it entirely.
    • Fixed by restricting canUpdateBoardSort (server/lib/utils.js) so the sort-reorder rule
      approves an update only when sort is the sole modified field (fieldNames is exactly
      ['sort']) — it can no longer approve a modifier that also mutates members, permission, title
      or anything else. As defense in depth, the last-admin deny rule (server/permissions/boards.js) now
      also rejects a $set rewrite of the members array that would drop the last active admin, not just
      a $pull. A regression test covers the multi-field smuggling case
      (server/lib/tests/boards.security.tests.js). The legitimate All Boards drag-reorder is unaffected:
      it persists the order per-user in profile.boardSortIndex (Users.setBoardSortIndex), not in the
      board document. CVSS:3.1 8.8 High (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H).
    • Affected Wekan v9.85 and earlier through the current release; fixed at the upcoming WeKan release.
      Reported by 5ud0 / Tarmo Technologies.
      Thanks to 5ud0 / Tarmo Technologies and xet7.

and adds the following updates:

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

v9.88

Choose a tag to compare

@github-actions github-actions released this 13 Jul 02:19

v9.88 2026-07-13 WeKan ® release

This release adds the following new features:

and fixes the following bugs:

  • Sandstorm build: fixed "not writing through dangling symlink" that failed the
    sandstorm.yml workflow at sandstorm-src/build-deps.sh step [3/7]
    . The
    meteor-spk 0.6.0 base ships some runtime libs in meteor-spk.deps/lib as
    DANGLING symlinks (e.g. libstdc++.so.6 → a library that no longer exists), so
    refreshing them with the host's newer libs failed: cp -fL refuses to write
    through a dangling destination symlink. Added --remove-destination so cp
    deletes the existing destination (dangling symlink included) before copying the
    host's real library. Thanks to xet7.
  • Sandstorm build: write the signing keyring to the path meteor-spk pack
    actually reads
    . The sandstorm.yml "Restore the Sandstorm signing keyring"
    step wrote the decoded SANDSTORM_KEYRING secret to ~/.sandstorm/sandstorm-keyring,
    but meteor-spk / spk read the app private key from ~/.sandstorm-keyring (a
    file directly in $HOME), so packing aborted with
    open(~/.sandstorm-keyring): No such file or directory even when the secret was
    set. Now it writes ~/.sandstorm-keyring, and — since the .spk cannot be signed
    without it — fails early with an actionable message when the secret is missing,
    instead of the cryptic later crash. Thanks to xet7.
  • Sandstorm .spk: trim it back toward Cloudflare's 100 MB upload limit. The
    Sandstorm build no longer bundles the ~300 MB of modern MongoDB Database Tools
    (wekan/mongo-tools) — they are unused in Sandstorm (grains back up/restore
    themselves; the MongoDB 3 → FerretDB migration uses the legacy migratemongo CLIs
    • the .mjs importer). Also set PUPPETEER_SKIP_DOWNLOAD=1 for the Meteor build
      (puppeteer is a test-only devDependency, so its ~150 MB Chromium never belongs in
      the .spk), and the pack step now prints the .spk size and warns if it exceeds
      100 MB. (The Database Tools remain in the WeKan .zip bundle / Docker / Snap,
      which do use them.) Thanks to xet7.
  • Sandstorm .spk: bundle the matching glibc dynamic loader so grains start:
    the .spk bundled the host's new glibc libc.so.6 (Ubuntu 24.04, glibc 2.39)
    but kept the old glibc 2.31 ld-linux from the meteor-spk 0.6.0 base, so node
    failed at startup with libc.so.6: undefined symbol: _dl_audit_symbind_alt, version GLIBC_PRIVATE (HTTP-BRIDGE exit 127) and the grain crash-looped.
    sandstorm-src/build-deps.sh now also copies the host's ld-linux-x86-64.so.2
    so the loader and libc are the same glibc, and adds a [verify] gate that fails
    the build if they differ or the bundled node cannot run under them — turning a
    silent grain crash-loop into a loud build failure. Thanks to xet7.
  • Sandstorm build workflow: enable unprivileged user namespaces and build the dispatched branch:
    Ubuntu 24.04 defaults kernel.apparmor_restrict_unprivileged_userns=1, which
    blocks the unprivileged user namespaces the Sandstorm install and the spk
    supervisor rely on, so sandstorm.yml now relaxes it on the runner. A new ref
    workflow_dispatch input also lets the workflow build a fix branch before it is
    merged, instead of a hardcoded main. Thanks to xet7.
  • Meteor unit tests: fix server-boot crash from __dirname in an ESM test file:
    server/lib/tests/dependencies.openapi.tests.js is an ES module that referenced
    the bare __dirname global; under Node 24 / Meteor 3.5 the compiler injects
    const __dirname = fileURLToPath(import.meta.url), colliding with the __dirname
    the CommonJS module wrapper already provides, so the server bundle failed to boot
    with Identifier '__dirname' has already been declared and the "Meteor unit
    tests" CI job died before any test ran. Drop the __dirname seed
    (process.env.PWD already reaches the repo root under meteor test). Thanks to xet7.
  • Sandstorm .spk: point FerretDB state dir to writable /var so the grain starts:
    FerretDB persists a state.json (version/UUID) via its state provider even with
    telemetry disabled, and its --state-dir defaults to . — which in a Sandstorm
    grain is /, read-only. So FerretDB failed with Failed to create state provider: failed to persist state: open /state.json: read-only file system,
    exited (code 1), and the grain crash-looped. sandstorm-src/start.js now creates
    /var/ferretdb and passes --state-dir=/var/ferretdb (plus FERRETDB_STATE_DIR)
    in startFerret(), covering both the migration and steady-state FerretDB launches.
    Thanks to xet7.
  • Sandstorm .spk: don't crash the grain when capnp.node can't load on Node 24:
    WeKan on Sandstorm bundles Node 24, but capnp.node (node-capnp) is built for an
    older Node ABI (NODE_MODULE_VERSION 83 = Node 14), so Npm.require('capnp') in
    sandstorm.js failed with ERR_DLOPEN_FAILED and crash-looped the whole grain on
    boot. Cap'n Proto is now loaded lazily in a try/catch and degrades gracefully:
    the grain boots and core WeKan works, because login and user identity come from
    the sandstorm-http-bridge X-Sandstorm-* HTTP headers (wekan-accounts-sandstorm)
    and need no Cap'n Proto. Only the two capnp-only features are skipped when the
    addon cannot load — the Powerbox identity-claim method and Sandstorm activity
    notifications — with a clear warning. They can be restored by rebuilding node-capnp
    for Node 24, or reimplemented over the bridge's HTTP/JSON API. Thanks to xet7.
  • Snap release: build amd64+arm64 natively on GitHub, exotic arches non-blocking on Launchpad:
    the single snap job ran snapcraft remote-build for all 5 platforms on Launchpad
    and blocked until the slowest resolved, so the release hung ~3.5h on riscv64's
    Launchpad queue even though amd64+arm64 finished in minutes. It is now split in
    two: snap-native builds amd64 (ubuntu-24.04) and arm64 (ubuntu-24.04-arm)
    natively on GitHub runners via snapcore/action-build; snap-launchpad builds
    s390x/ppc64el/riscv64 on Launchpad in a non-blocking (continue-on-error),
    per-arch matrix (remote-build --build-for <arch>) so a slow or failed exotic
    arch never delays the release or the other arches. Each arch publishes to the
    Snap Store (candidate,beta,edge) and attaches to the GitHub Release the moment it
    finishes — all 5 arches still ship. Thanks to xet7.
  • Sandstorm .spk: fix server-boot crashes from stale globals (Users, HTTP) in sandstorm.js:
    once the capnp load was made non-fatal, the grain reached the rest of
    sandstorm.js and hit two latent Meteor-2.x-isms the Meteor 3.x migration missed
    (this file only runs on Sandstorm, so it was not exercised): it referenced the
    Users/Boards/Swimlanes/Activities collections as implicit globals, but
    those are now ES module default exports, so Users.after.insert threw
    Users is not defined at boot; and it monkey-patched HTTP.methods from the
    removed meteor/http package, so HTTP was undefined. The collections (and
    Accounts) are now imported explicitly, and the obsolete HTTP.methods patch is
    removed. Thanks to xet7.
  • Sandstorm .spk: use boolean index options so FerretDB accepts the users index:
    wekan-accounts-sandstorm created the unique index on services.sandstorm.id
    with {unique: 1, sparse: 1}. Real MongoDB accepts the truthy 1, but FerretDB
    (used by the Sandstorm .spk) is strict and rejects it with The field 'unique' has value unique: 1, which is not convertible to bool, crashing the grain at boot
    during index creation. Now uses real booleans (unique: true, sparse: true).
    Thanks to xet7.
  • Sandstorm .spk: strip Accept-Encoding so the grain doesn't serve corrupted (gzip) content:
    the grain boots, but the page failed to load with a browser "Corrupted Content
    Error" (NS_ERROR_NET_CORRUPTED_CONTENT). sandstorm-http-bridge advertises
    Accept-Encoding: gzip to the app regardless of what the browser actually sent,
    so Meteor served gzip/brotli-encoded responses the browser could not decode. A
    WebApp.rawHandlers middleware now strips Accept-Encoding (it runs before
    Meteor's static/boilerplate serving) so responses go out uncompressed; bandwidth
    is a non-issue behind the local bridge. Thanks to xet7.
  • Sandstorm .spk: bundle a modern sandstorm-http-bridge to fix "Corrupted Content":
    the grain boots, but the page failed with a browser "Corrupted Content Error"
    (NS_ERROR_NET_CORRUPTED_CONTENT) on WeKan's / redirect. The meteor-spk 0.6.0
    base bundles an ancient (~2016) sandstorm-http-bridge that mangles responses
    (it always advertises Accept-Encoding: gzip to the app and mishandles
    redirect/encoding). build-deps.sh now overwrites the bundled
    /sandstorm-http-bridge with the modern one from a Sandstorm install
    (/opt/sandstorm/latest/bin/sandstorm-http-bridge; override with
    SANDSTORM_HTTP_BRIDGE), and sandstorm.yml installs Sandstorm before assembling
    the deps so the bridge is available on the CI ru...
Read more

v9.87

Choose a tag to compare

@github-actions github-actions released this 11 Jul 19:50

v9.87 2026-07-11 WeKan ® release

This release fixes the following bugs:

  • Normal users cannot move cards between swimlanes:
    in the swimlanes view the .js-swimlanes sortable — which also carries moving a
    card from one swimlane to another — was disabled for every non-admin
    (!isBoardAdmin()), even though its own comment said it should be disabled only
    for non-members. So ordinary board members could move a card within a swimlane
    but not between swimlanes, and could not reorder swimlanes. Now it is disabled
    only for users without write access (!canModifyCard(): comment-only, worker,
    read-only), so board members can move cards between swimlanes again.
    Thanks to xet7.

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

v9.86

Choose a tag to compare

@github-actions github-actions released this 11 Jul 18:00

v9.86 2026-07-11 WeKan ® release

This release fixes the following bugs:

  • At default docker-compose.yml, changed DDP_TRANSPORT from uws to sockjs, because uws does not work at s390x.
    Thanks to xet7.
  • FerretDB v1 is now downloaded as an individual per-arch binary, not ferretdb.zip:
    the wekan/FerretDB release now attaches one ferretdb-<arch> (.exe on Windows)
    asset per platform instead of a single multi-platform ferretdb.zip. Every WeKan
    build path now downloads only the one binary for the platform it targets from
    https://github.com/wekan/FerretDB/releases/latest/download/ferretdb-<arch>:
    release-all.yml (the amd64/arm64/win64/mac/ppc64le/s390x/riscv64 bundle jobs —
    the separate build-ferretdb job and its ferretdb-zip artifact are removed),
    the default docker-compose.yml, and the Sandstorm sandstorm-src/build-deps.sh.
    FerretDB itself also moved its Go toolchain to 1.25.11 to clear the Quay.io
    stdlib security advisories. Thanks to xet7.
  • Drop the mongosh binary; use bundled Node.js 24 + the mongodb driver instead:
    WeKan no longer bundles or downloads the MongoDB Shell anywhere (snap, Windows
    bundle). Every scripted database operation it was used for — readiness ping,
    replica-set initiate/status, and the v8.43 schema migration — now runs
    through the new snap-src/bin/db-eval (a tiny wrapper around the bundled Node.js
    24 + the mongodb driver), so the snap control scripts (wekan-control,
    mongodb-control, migration-control), the start-wekan.sh/start-wekan.bat
    launchers, and the ported migrate-schema-v843.mjs are all mongosh-free. This
    removes a large, CVE-prone binary and works identically on every architecture
    (including s390x/ppc64le/riscv64, which have no prebuilt mongosh). The legacy
    MongoDB 3.2 mongo shell (migratemongo, amd64) stays for migration-time reads
    only. Thanks to xet7.
  • MongoDB Database Tools now come from wekan/mongo-tools, not the MongoDB website:
    every WeKan build downloads the per-arch <tool>-<arch> binaries (bsondump,
    mongodump, mongoexport, mongofiles, mongoimport, mongorestore, mongostat,
    mongotop) built by the wekan/mongo-tools
    fork (pure Go, cross-compiled for every architecture) from its newest release,
    replacing the fastdl.mongodb.org / downloads.mongodb.com downloads. They are
    embedded in the Linux .zip bundles (amd64/arm64/s390x/ppc64le/riscv64) — and
    therefore in the Docker image, which is built from those bundles — the Windows
    and macOS bundles, the Snap (mongotools part), and the Sandstorm .spk. The
    MongoDB 7 server (mongod) is still fetched from MongoDB (amd64/arm64 only), since
    MongoDB ships no server for the other architectures; the legacy MongoDB 3.2 CLIs
    (migratemongo, amd64) remain only for the one-time MongoDB 3 migration. Thanks to xet7.
  • Snap: the default snapcraft.yaml is now the base: core24, grade: stable build:
    the previous snapcraft.yaml (core26, grade: devel) is renamed to
    snapcraft-core26.yaml, and the former snapcraft-core24.yaml becomes
    snapcraft.yaml. Because the default is base: core24 (a released base), it can
    be published to the Snap candidate channel, which core26 cannot. The automated
    release-all.yml workflow publishes the snap to the candidate + beta + edge
    channels; the stable channel is published manually later, once it is proven
    stable enough. Thanks to xet7.

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

v9.85

Choose a tag to compare

@github-actions github-actions released this 11 Jul 09:12

v9.85 2026-07-11 WeKan ® release

This release adds the following features and fixes:

  • Docker: FerretDB v1 + SQLite is now the default docker-compose.yml:

    The default database for docker compose up -d is now FerretDB v1 with embedded
    SQLite
    (from https://github.com/wekan/FerretDB) — light and self-contained, no
    separate database server. The compose files were renamed accordingly:
    docker-compose-ferretdb-v1-sqlite.yml -> docker-compose.yml (the new default),
    and the previous MongoDB default docker-compose.yml -> docker-compose-mongodb-v7.yml.
    The other files are unchanged: docker-compose-ferretdb-v2-postgresql.yml (FerretDB 2

    • PostgreSQL) and docker-compose-multitenancy.yml (MongoDB multitenancy). To use
      MongoDB 7, run docker compose -f docker-compose-mongodb-v7.yml up -d or rename that
      file to docker-compose.yml. rebuild-wekan.sh / rebuild-wekan.bat Docker menus now
      list FerretDB v1 SQLite first (default) and point at the new filenames, the compose
      files' own header comments were updated, and the Docker docs now describe which
      compose file maps to which database. The Docker menus also gained a Build from
      source & start (up -d --build)
      action per compose file, which builds the wekan-app
      image from the local Dockerfile (tagged as the image the compose file references)
      and starts that freshly built container instead of a possibly-stale prebuilt image —
      useful when a change (e.g. the FerretDB Version-page detection) isn't in the pulled
      image yet. Finally, the obsolete version: attribute (which Docker Compose v2 warns
      about and ignores) was removed from all compose files (docker-compose.yml,
      docker-compose-mongodb-v7.yml, docker-compose-ferretdb-v2-postgresql.yml,
      docker-compose-multitenancy.yml, .devcontainer/docker-compose.yml, and the
      ToroDB docs example).
  • rebuild-wekan.sh / rebuild-wekan.bat: reorganized into category submenus + Docker start/logs/stop:

    The long flat menu is now grouped into a short top-level menu — Setup,
    Dev server, Tests, Docker, Tools, Quit — each opening a small
    submenu with 0) Back, so you read only a handful of items at a time and labels
    are shorter (the category provides the context). Docker is a two-stage submenu:
    pick a backend (MongoDB docker-compose.yml, FerretDB v1 SQLite, FerretDB v2
    PostgreSQL, MongoDB Multitenancy), then an action — Start (up -d),
    Follow logs (logs -f) or Stop (down) — which removes the previous
    repetition of 12 near-identical entries. The .sh auto-detects docker compose
    vs legacy docker-compose. All existing actions are unchanged, just regrouped.

  • rebuild-wekan.sh / rebuild-wekan.bat: Dev server options now stop the previous
    server (including the rspack :8080 dev server) before starting, plus a new "Kill
    all dev servers" option, and docs updated for the new menu
    :

    Every Dev server option (localhost:3000, + trace warnings, + bundle visualizer, CURRENT-IP:3000, CURRENT-IP:3000 + MONGO_URL 27019, and
    CUSTOM-IP:PORT) now stops any Meteor dev server already running before starting a
    fresh one, so re-running a dev option no longer fails because a port is taken — no
    need to hunt down and kill the old processes yourself. Crucially it frees both
    the app port and the rspack dev-server port 8080: meteor run starts an rspack
    dev server on 8080 that can outlive the meteor parent, and a leftover one made the
    restart crash with Error: listen EADDRINUSE ... :8080. Port detection now checks the
    listening socket directly (ss/lsof, with a bash /dev/tcp fallback that needs no
    external tools, and netstat on .bat) instead of an HTTP probe, so it also catches
    a server that is still building. A new Dev server -> Kill all dev servers option
    frees every dev/test port the scripts use at once — the dev app (3000) and its Mongo
    (3001), the Mocha test server (3100) and its Mongo (3101), a Sandstorm standalone dev
    server (4000) and its Mongo (4001), and the rspack dev server (8080) — killing meteor,
    the rspack watcher and Meteor's bundled --replSet meteor Mongos (never a
    production/system Mongo). Both scripts escalate to SIGKILL if a port does not free up.
    Also updated the build-from-source docs (README.md, Build-from-source.md,
    Build-and-Create-Pull-Request.md, Emoji.md, and the two Sandstorm developer docs) to
    the new two-level menu (Setup -> Install dependencies, Setup -> Build WeKan,
    Dev server -> localhost:3000) and fixed a stale dev-server port
    (localhost:4000 -> 3000).

  • FerretDB: quieter logs, and removed a dead MongoDB-driver-selection subsystem:

    On FerretDB (SQLite), the driver debug logs revealed a second, TLS-enabled Mongo
    monitor connection retrying every ~0.5s and being rejected by the plaintext FerretDB
    port, which FerretDB logged at WARN (Connection stopped … invalid message length /
    before secure TLS connection was established) — harmless (WeKan runs fine on the real
    plaintext connection) but very noisy. FerretDB is now started with --log-level=error
    in all bundled launch points (Docker entrypoint, snap ferretdb-control, the release
    start-wekan.sh, and the docker-compose-ferretdb-v1-sqlite.yml example), which drops
    the per-connection WARN spam. Separately removed a dead, unused "MongoDB Driver System"
    (server/mongodb-driver-startup.js + models/lib/{meteorMongoIntegration,mongodbConnectionManager,mongodbDriverManager}.js)
    — an abandoned attempt to auto-detect MongoDB 3.0–8.0 and pick versioned driver packages
    that were never even installed; WeKan uses the mongodb-7 driver via Meteor.

  • Fix snap build failing on the caddy part (Cloudsmith unreachable on Launchpad):

    The snap installed Caddy from the Cloudsmith apt repo
    (curl … dl.cloudsmith.io … | gpg --dearmor), which fails on the Launchpad remote
    builders — their network is restricted to a fetch proxy that can't reach Cloudsmith, so
    gpg got no key (no valid OpenPGP data found) and the caddy override-build failed
    with code 2 on both arches, all attempts. Both snapcraft.yaml and snapcraft-core24.yaml
    now download the official prebuilt Caddy static binary from GitHub releases (per-arch,
    latest stable with a pinned fallback) instead. WeKan uses only built-in Caddy directives,
    so vanilla Caddy is sufficient — no apt repo, no gpg, no xcaddy/custom-module build.

  • Fix Admin Panel / Version showing "MongoDB" when running on FerretDB:

    The database detection only recognised a buildInfo.ferretdb sub-document, but the
    wekan/FerretDB v1 fork reports its identity as a top-level ferretdbVersion string
    (e.g. v1.24.2-60-gb5523566) plus ferretdbFeatures, with its git commit in gitVersion.
    So the Version page showed Database type: MongoDB and hid the FerretDB rows. Detection now
    handles both shapes (server/statistics.js),
    so it shows Database type: FerretDB, the FerretDB version and FerretDB commit rows, and
    the SQLite storage engine. (FerretDB v1's version: 7.0.42 is the MongoDB version it emulates.)

  • Design doc: WeKan on Sandstorm (Meteor 3.5 / Node 24) with MongoDB 3 → FerretDB migration:

    Added docs/Platforms/FOSS/Sandstorm/Meteor3/Migration.md
    describing how to build a modern Sandstorm .spk (Node 24, replacing meteor-spk
    0.6.0's Node 14) that runs on FerretDB v1 (embedded SQLite) instead of MongoDB 3.0,
    migrating an existing grain's MongoDB 3.0 data on first launch — reusing the snap's
    proven migrate-mongo3-to-ferretdb logic (mongoexport read → FerretDB insert;
    CollectionFS/Meteor-Files GridFS attachments+avatars → filesystem). Includes the
    grain sandbox (seccomp) compatibility analysis, the rewritten start.js, and a new
    isSandstorm-only Admin Panel / Attachments / Sandstorm section (migration status,
    raw-MongoDB disk usage, and a guarded delete-raw-MongoDB-files action). Implementation
    of the in-app pieces follows.

  • Sandstorm: Admin Panel / Attachments / Sandstorm (migration status + free raw-MongoDB disk space):

    Implemented the in-app pieces from the design above. When WeKan runs inside a
    Sandstorm grain (isSandstorm), a new Sandstorm section appears in Admin
    Panel / Attachments showing whether the one-time MongoDB 3 → FerretDB v1
    migration succeeded, and the disk space the raw MongoDB 3 database files, the
    FerretDB SQLite, and the attachments/avatars currently use inside the grain.
    An admin can delete the now-redundant raw MongoDB files to free disk space —
    guarded so it only runs after a confirmed-successful migration, behind a
    confirmation. New admin-gated server methods sandstormMigrationStatus /
    sandstormDeleteRawMongo (server/methods/sandstormMigration.js);
    the migration importer now writes a migration-status.json the panel reads.

  • Sandstorm: grain launcher + spk build tooling (Node 24 / FerretDB, no releases.wekan.team):

    Added the grain launcher sandstorm-src/start.js:
    on first launch it runs the migration chain for whatever an existing grain holds —
    niscu (MongoDB 2.x) → MongoDB 3.0 (the preserved legacy path for very old grains) then
    MongoDB 3.0 → FerretDB v1 — then runs WeKan (Node 24) on FerretDB. Migration support from
    old versions is permanent (niscud + mongod 3.0 are kept). Added the deps-assembly script
    sandstorm-src/build-deps.sh
    which builds a modern meteor-spk.deps on top of upstream meteor-spk 0.6.0
    ...

Read more

v9.84

Choose a tag to compare

@github-actions github-actions released this 11 Jul 00:28

v9.84 2026-07-11 WeKan ® release

This release adds the following features and fixes:

  • Fix scheduled backup cron init crashing at server startup on Meteor 3:

    The scheduled-backup cron used Meteor's synchronous Mongo API
    (BackupSettings.findOne/upsert), which Meteor 3 no longer allows on the
    server — startup logged findOne is not available on the server. Please use findOneAsync() instead. and the cron silently never registered, so scheduled
    backups did not run. registerCron() is now async and uses findOneAsync,
    the getBackupSchedule/saveBackupSchedule methods use findOneAsync/
    upsertAsync, and every registerCron() caller awaits it. Added
    tests/backupCron.test.cjs (positive + negative cron-registration tests and a
    source guard that fails if the synchronous server-forbidden API is
    reintroduced), wired into the test:unit:node suite.

  • Snap: show the correct writable path for parallel installs in backup instructions:

    The backup/migration help text printed by wekan.help hardcoded
    /var/snap/wekan/common/files, which is wrong for a parallel snap install
    (e.g. wekan_customer, whose data lives under /var/snap/wekan_customer/common).
    It now prints $SNAP_COMMON/files, which snapd sets per instance. Display-only;
    all functional snap paths already derive from $SNAP_COMMON/$SNAP_DATA, so
    parallel installs were already fully supported.

  • Fix flaky server-side Mocha i18n test (TAPi18n .loadLanguage):

    The .loadLanguage suite stubbed addResourceBundle on the shared
    TAPi18n.i18n singleton and asserted the call count. Because loadLanguage()
    reads this.i18n late and each test re-init()s the singleton, cross-test
    state could leave the stub watching a different i18next instance than the one
    the bundle was registered on, so the count read 0 and the run intermittently
    reported expected addResourceBundle to be called once. The tests now assert
    on observable i18next state (hasResourceBundle/getResourceBundle under the
    normalised toI18nCode), which is instance-agnostic and deterministic and
    mirrors the reliable .setLanguage suite. Also stopped a leaked
    Tracker.autorun in the .getLanguage reactive test (the cross-test hazard).

  • Fix rebuild-all.yml s390x build; add bundled FerretDB v1 for ppc64le, s390x, riscv64 and to the Docker image:

    The extra-arch bundle build ran node:24-slim under QEMU, but the official
    node:24 image publishes no linux/s390x manifest (only amd64/arm64/ppc64le),
    so the s390x leg failed with "no matching manifest for linux/s390x". The
    emulated native-module rebuild now runs on an ubuntu:26.04 base (which
    publishes every arch) and installs Node.js from nodejs.org; riscv64 uses
    unofficial-builds.nodejs.org (nodejs.org ships no riscv64).

    MongoDB Community only ships amd64/arm64 server binaries, so on ppc64le, s390x
    and riscv64 — the other architectures with a Node.js 24 build — WeKan now
    bundles FerretDB v1 (the wekan/FerretDB
    fork with its embedded pure-Go SQLite backend, which speaks the MongoDB wire
    protocol) instead of requiring MongoDB. The FerretDB binary is cross-compiled
    once for all five architectures (CGO off, static, no QEMU) and embedded in
    every .zip next to main.js, so amd64/arm64 users can opt in too with
    WEKAN_DB=ferretdb. The Docker image now covers all five architectures and
    auto-starts FerretDB on the MongoDB-less ones. FerretDB telemetry is disabled
    and locked (--telemetry=disable, plus DO_NOT_TRACK). armv7l/32-bit ARM is
    still not built: there is no Node.js 24 build for it anywhere, so nothing could
    run WeKan there regardless of RAM.

  • Snap: choose MongoDB or FerretDB v1 with "snap set wekan database=ferretdb"; FerretDB default for new installs; disable mongosh telemetry:

    The WeKan snap gains a database setting (mongodb or ferretdb). A new
    ferretdb service runs FerretDB v1 (SQLite) on the same port MongoDB would
    use, so MONGO_URL is unchanged; only one database runs at a time, and the
    configure hook stops one and starts the other when the setting changes. Switch
    with snap set wekan database=ferretdb (or back with database=mongodb).

    New snap installs default to FerretDB on all platforms (via the install hook,
    which runs only on fresh installs — upgrades keep MongoDB, and a pre-existing
    MongoDB data directory is detected and kept so no data is lost). There is no
    snap install --db=ferretdb flag (snapd has no custom install options); use
    the two-step snap install wekan --channel=latest/beta then
    snap set wekan database=ferretdb.

    mongosh collects anonymized usage analytics by default and the snap invokes it
    several times; it is now disabled so nothing phones home. mongod itself has no
    phone-home telemetry (Cloud Free Monitoring is opt-in and stays off).

  • Admin Panel / Version: show database type, version, commit, storage engine and reactivity mode:

    Admin Panel / Version now shows whether WeKan is using MongoDB or FerretDB v1
    (SQLite), the MongoDB-compatible version and the server git commit, FerretDB's
    own version and commit when FerretDB is in use, the storage engine, and which
    reactivity mechanism is currently in use — changeStreams / oplog / polling
    (FerretDB has no oplog, so it uses polling).

  • Standalone ferretdb.zip for all platforms, and rebuild-all.yml uses the prebuilt one from wekan/FerretDB releases:

    FerretDB v1 (the wekan/FerretDB fork, pure-Go SQLite backend, CGO off, no QEMU)
    is now cross-compiled for every platform Go and modernc.org/sqlite support — far
    beyond the arches Node.js ships — and packed into a single ferretdb.zip:

    ferretdb/<arch>/ferretdb-<arch>        (Linux/macOS/BSD, executable)
    ferretdb/<arch>/ferretdb-<arch>.exe    (Windows only)
    ferretdb/README.md                     (links to https://github.com/wekan/FerretDB)
    

    That zip is produced and released in the wekan/FerretDB repo (by its build.sh,
    which gained sequential and parallel "Build ferretdb.zip" menu options). The
    WeKan release workflow no longer builds FerretDB with Go: it downloads the newest
    ferretdb.zip from https://github.com/wekan/FerretDB/releases and every WeKan
    build (the bundle .zip for each arch, and via the bundle the Docker image and
    snap, including the Windows and macOS bundles) embeds its per-arch binary from
    that one source. ferretdb.zip is not re-attached to the WeKan releases.

  • Fix #6445: dynamic-import chunks 404 under a sub-path (duplicated build-chunks/build-chunks/):

    Under a sub-path deployment (ROOT_URL like https://host/wekan, usually behind
    a reverse proxy that strips the prefix), language selection and other
    lazy-loaded features failed with ENOENT ... build-chunks/build-chunks/<id>.js.
    rspack's client runtime builds each chunk URL as public-path + chunk-name, and
    the chunk name already carries the build-chunks/ prefix, but
    client/00-startup.js set the sub-path public path to <sub-path>/build-chunks/,
    so rspack appended a second build-chunks/. It now sets the public path to just
    <sub-path>/ and lets rspack add build-chunks/ itself.

  • Add snapcraft-core24.yaml so the newest WeKan can be published to the Snap Stable channel:

    The main snapcraft.yaml uses base: core26, which (until core26 is released)
    needs build-base: devel + grade: devel, so it can only go to Snap Beta/Edge.
    snapcraft-core24.yaml builds the SAME newest WeKan (Meteor 3.5, Node.js 24,
    FerretDB v1, MongoDB 7, Caddy 2) on base: core24 (a released base, grade: stable), so the Snap Stable channel can finally be updated from the old 6.09
    snap. Only base/build-base/grade differ.

  • Self-contained release bundles: bundle Node.js + FerretDB + start-wekan.{sh,bat}:

    Each wekan-<version>-<arch>.zip is now fully offline. Its bundle/ directory
    contains the WeKan server, a Node.js binary for that platform, a FerretDB v1
    (SQLite) binary, and a start-wekan.sh (start-wekan.bat on Windows) that by
    default runs WeKan on the bundled Node against the bundled FerretDB SQLite,
    storing data and attachments/avatars on the filesystem under WRITABLE_PATH
    (as in the Windows Offline guide) — no separate Node or database install
    needed. The Docker image and snap, which have their own Node and entrypoint,
    strip the redundant bundled Node + launchers to stay small.

  • Standalone sandstorm.yml workflow to build + attach the .spk (Sandstorm removed from release-all.yml):

    Sandstorm packaging (mirroring releases/release-sandstorm.sh +
    install-sandstorm.sh: installs Meteor, meteor-spk 0.6.0 and a dev Sandstorm,
    runs meteor-spk pack) is not tested well enough yet, so it no longer runs
    as part of a full release — the build-sandstorm job was removed from
    release-all.yml. Instead a separate, manually-triggered sandstorm.yml builds
    ONLY the .spk and uploads it as wekan-sandstorm-YYYY_MM_DD-HH_MM_SS.spk to the
    newest WeKan GitHub Release (plus a workflow artifact), so it can be downloaded
    and tested for errors without affecting releases. Experimental in CI — Sandstorm
    needs unprivileged user namespaces, and signing the .spk needs th...

Read more

v9.83

Choose a tag to compare

@github-actions github-actions released this 09 Jul 12:02

v9.83 2026-07-09 WeKan ® release

This release fixes the following bugs:

  • Fix #3624 swimlane REST regression: await the async board.swimlanes():

    The swimlanes CRUD e2e test (23-rest-api-more.e2e.js:209) regressed on all
    browsers. Root cause: on the server ReactiveCache.getSwimlanes is async, so
    board.swimlanes() returns a Promise in the REST handler. The #3624 change read
    board.swimlanes().map(s => s.sort); .map on a Promise throws, the insert never
    ran, the error was swallowed by the handler's catch (returned as 200 with no
    _id), and the test read .title off a null swimlane.

    The earlier "move the require to a top-level import" commit was not the real
    cause (the same module.exports import pattern works server-side, e.g.
    ruleDeletePermission in rulesButton.js). The actual fix is to await
    board.swimlanes() before mapping. This also means API-created swimlanes now get
    a real max(existing sort)+1 sort — previously the un-awaited .length yielded
    undefined, so they were stored with no sort at all, which is the #3624 symptom.

    Verified: the swimlane test passed in every kept local run through
    2026-07-09_02-38-07 and failed starting 09-52-25 (right after the #3624 change),
    confirming the regression window; the tests/swimlaneSort.test.cjs unit test
    still passes.

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

v9.82

Choose a tag to compare

@github-actions github-actions released this 09 Jul 08:34

v9.82 2026-07-09 WeKan ® release

This release fixes the following bugs:

  • Fix #5536: automated rule can now move/link a card to a different board:
    The rules "move card to top/bottom of a list on another board" and "link
    card to another board" actions failed for boards the rule creator did not
    own. The action showed BLANK after creating the rule (the wizard's optimistic
    client inserts landed in minimongo limbo / were rejected by allow-deny for
    non-owner members) — these now create the rule through the server
    rules.createRule method, keeping the action's destination boardId. And
    execution crashed with an "Internal Server Error" because the destination
    swimlane fallback dereferenced ._id on a possibly-undefined swimlane titled
    exactly Default (renamed/translated/deleted on the destination board);
    resolution now uses the board's real default swimlane via a Meteor-free
    resolver models/lib/ruleActionResolve.js, unit tested in
    tests/ruleActionResolve.test.cjs. Thanks to DarthKillian and xet7.
  • Fix #4978: board background updates when switching boards via the favorites bar:
    Switching directly between two boards via the favorites bar reused the same
    boardBody template instance, so the one-shot setBackgroundImage() in
    onRendered never re-ran and the previous board's background stuck. It is now
    applied inside a reactive autorun and clears any stale inline background when
    the new board has no image. The decision is a Meteor-free helper
    models/lib/boardBackground.js, unit tested in tests/boardBackground.test.cjs.
    Thanks to dasarne and xet7.
  • Fix #4881: Due this week / next week filter respects the start day of week:
    The "Due this week" filter selected next week's cards and ignored the
    configured start weekday, because it derived its window from
    startOf(now(), 'week') — which the native dateUtils never implemented, so
    it returned the date unchanged. A new Meteor-free helper
    models/lib/weekStart.js computes the correct week window for any start day
    of week; the this/next-week buttons now toggle per week too. Unit tested in
    tests/weekStart.test.cjs. Thanks to mimZD and xet7.
  • Fix #4946: calendar week numbers respect the defined start day of week:
    In the Calendar view the week-number column was numbered from Sunday
    regardless of the start-day-of-week setting. The calendar now computes the
    number with weekNumberByFirstDay() (in models/lib/weekStart.js) from the
    same firstDay used to lay out the grid, unit tested in
    tests/weekStart.test.cjs. Thanks to helioguardabaxo and xet7.
  • Fix #4653: LDAP username with a hyphen no longer becomes a dot:
    With LDAP_UTF8_NAMES_SLUGIFY enabled, limax(text, { separator: '.' })
    turned every non-alphanumeric run — hyphens included — into ., so an LDAP
    username like p.parta-partb became p.parta.partb and the user could not
    log in. The username is now slugified per hyphen-separated segment and
    rejoined with -, preserving hyphens while still transliterating UTF-8. Pure
    helper packages/wekan-ldap/server/usernameSlug.js, unit tested in
    tests/ldapUsernameSlug.test.cjs. Thanks to RowhamD and xet7.
  • Fix #4236: Enter adds a new line in the card title, consistent with the description:
    The card title textarea submitted on plain Enter (only Shift+Enter made a new
    line), unlike the description field which inserts a new line on Enter and
    saves on Ctrl/Cmd+Enter. The title now uses the same shared, Meteor-free rule
    isSubmitKey() (models/lib/editorSubmitKey.js): submit only on
    Ctrl/Cmd+Enter, newline otherwise. Unit tested in
    tests/editorSubmitKey.test.cjs. Thanks to listenerri and xet7.
  • Fix #4055: ISO week-number regression test (already correct in current code):
    #4055 reported the week number was one/two weeks too high for 2021-10-25..31
    (ISO week 43). That was the old moment-based math; the current native,
    DST-safe getISOWeek() computes it correctly. Added
    tests/isoWeek.test.cjs pinning the reported dates so it cannot regress.
    Thanks to marcungeschikts and xet7.
  • Fix #4394: Register / Forgot Password links stay hidden after a failed login:
    Security. With registration / forgot-password disabled in the Admin Panel, the
    links were hidden by a one-shot .hide() that a useraccounts form re-render
    (e.g. an LDAP failed login) dropped, so the links reappeared. The disable
    state is now a class on the stable <body> ancestor with matching CSS, so the
    links are re-hidden on every re-render. Thanks to Alsterdetektive1 and xet7.
  • Fix #4494: creating a board from a template no longer breaks subtasks:
    A board created from a template inherited the template's
    subtasksDefaultBoardId / dateSettingsDefaultBoardId; when those pointed at the
    template board itself, subtasks created on the new board were dropped onto the
    TEMPLATE board and linked back across boards. Board.copy() now repoints such
    self-referential defaults to the copy and clears the paired list id so it
    self-heals on the new board. Pure helper models/lib/boardCopyDefaults.js,
    unit tested in tests/boardCopyDefaults.test.cjs. Thanks to Xilef11 and xet7.
  • Fix #4249: filter by card title now works for renamed linked cards:
    Renaming a linked card wrote the new title only to the linked target, leaving
    the linking card's own title field stale; filter-by-title queries the own
    field, so linked cards dropped out of title filters after a rename.
    Card.setTitle() now also writes the linking card's own title. Pure helper
    models/lib/linkedCardTitle.js, unit tested in
    tests/linkedCardTitle.test.cjs. Thanks to Ben0it-T and xet7.
  • Fix #3606: activity feed no longer shows "edited/deleted comment undefined":
    The feed passed the comment id (absent on old activities, and gone for a
    deleted comment) into the activity string. Edit/delete activities now store
    the comment text and render it via Activities.commentDisplayText(), which
    falls back to the live comment text and then to an empty string — never
    "undefined". Pure helper models/lib/commentActivity.js, unit tested in
    tests/commentActivity.test.cjs. Thanks to janchuelo and xet7.
  • Fix #3624: new_swimlane REST API appends the swimlane last and accepts a sort:
    POST /api/boards/:boardId/swimlanes set the sort to the swimlane count, so a
    new swimlane appeared FIRST when existing sort values were non-contiguous. It
    now appends at max(existing sort)+1 and honors an optional explicit sort in
    the body. Pure helper models/lib/swimlaneSort.js, unit tested in
    tests/swimlaneSort.test.cjs. Thanks to tamasberesoebb and xet7.
  • Fix #3185: copying a card (or template) now copies its subtasks' checklists:
    Copying a card inserted each subtask as a bare card document, so the subtasks'
    checklists (and items) were dropped and the copied subtasks came out empty.
    Card.copy() now copies each subtask's checklists onto the new subtask. Pure
    helper models/lib/subtaskCopy.js, unit tested in tests/subtaskCopy.test.cjs.
    Thanks to ramses345 and xet7.
  • Fix #5439: list scrollbar is always visible on desktop and mobile browsers:
    List bodies used overflow-y: auto, so overlay scrollbars (macOS/iOS/Android/
    Firefox) auto-hid. The list body now keeps a scrollbar visible across all
    engines — overflow-y: scroll, ::-webkit-scrollbar (Chrome/Safari/Edge/
    mobile WebKit), scrollbar-width/scrollbar-color (Firefox/Gecko) and
    scrollbar-gutter: stable. Cross-browser rules in models/lib/scrollbarCss.js,
    applied in client/components/lists/list.css, unit tested (builder contract +
    applied CSS, positive + negative) in tests/scrollbarCss.test.cjs. Thanks to xet7.
  • Fix #6444: RTL — typing a card title no longer garbles other lists' minicard titles:
    In an RTL language (e.g. Arabic) the board root is dir="rtl", and both the
    minicard title (a dir="auto" .viewer) and the add-card composer textarea
    were dir="auto" with no bidi isolation, so they shared the surrounding
    bidirectional context. Typing a strong RTL character into one list's composer
    re-resolved that shared context and visibly reflowed the displayed minicard
    titles of the OTHER lists (no data change, reverted on refresh). Each title and
    the composer now use unicode-bidi: isolate. Locked in (positive + negative)
    by tests/minicardBidiIsolation.test.cjs; browser RTL behaviour is covered by
    tests/playwright/specs/18-rtl-layout.e2e.js. Thanks to xet7.
  • Fix the Playwright CI test failures introduced by the #3624 and #4236 fixes above:
    Two browser tests failed on C...
Read more

v9.81

Choose a tag to compare

@github-actions github-actions released this 09 Jul 00:23

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-...
Read more

v9.80

Choose a tag to compare

@github-actions github-actions released this 06 Jul 15:45

v9.80 2026-07-06 WeKan ® release

This release adds the following new features:

  • Admin Panel Domains table: pagination, column sort and search (like the Board Table view):
    The Admin Panel > People > Domains table loaded every domain (aggregated from all users) into the
    browser at once, with a fixed order and no search. It now behaves like the Board Table view: the
    server aggregates the domains and returns only one small page, so the whole list is never sent to
    the browser. You can order by the Domain or Users column (click the header to toggle ascending /
    descending, with a ▲/▼ indicator) and filter with a search box; prev/next controls page through the
    results. The search + sort + slice runs in the new pure, unit-tested models/lib/domainTablePage.js
    behind a new getDomainsWithUserCountsPage admin method (server/models/users.js), and the
    domainGeneral template (client/components/settings/peopleBody.{jade,js,css}) is now
    self-contained and fetches only the current page. Covered by tests/domainTablePage.test.cjs.
    Thanks to xet7.

and adds the following tests:

  • Verified and added a regression test for the board-invitation email language:
    Confirmed that a board-invitation email is localised in the existing recipient's own profile
    language, or — when the invitee is a new account created by the invite — in the inviter's profile
    language, defaulting to en (en.i18n.json) when none is set. The behaviour was already correct;
    the language choice is now extracted into the pure, unit-tested models/lib/inviteEmailLanguage.js
    used by inviteUserToBoard, and locked in by tests/inviteEmailLanguage.test.cjs.
    Thanks to xet7.

and fixes the following bugs:

  • Linked-card minicard now shows the cover image of the real card:
    A linked card (created by "Link card to this card") on one board did not show the cover image of
    the real card it points at on another board, even though the card's other fields did. A linked
    card is only a placeholder — its real content lives on the card at linkedId — and every other
    minicard getter resolves through the real card (getTitle/getReceived/getDue/…), but the
    cover helpers read this.coverId directly, and a linked card has no coverId of its own. The
    real card's cover attachment is already published to the linking board (see the "linked cards" /
    "attachments for linked cards" children of the board publication), so this was purely a
    client-side resolution gap. Card.cover() and the minicard cover() helper now resolve the
    cover id through the real card via the pure, unit-tested models/lib/linkedCardCover.js; normal
    cards are unaffected. Covered by tests/linkedCardCover.test.cjs.
    Thanks to 32Dexter and xet7.

  • Fix date-picker calendar stays fully visible when opened low on a scrolled page:
    Opening a date field (due/start/end date, or a date custom field) low on the screen showed the
    calendar popup extending past the visible area, and — because the pop-over is position: absolute
    (document coordinates) — scrolling to reach it moved the calendar along with the page, so the full
    calendar could never be seen (the workaround was to close it, drag the field to the center and
    reopen). Popup._getOffset computed the space above/below the opener and the clamped top from
    the opener's DOCUMENT offset mixed with the VIEWPORT height, ignoring the page scroll, so on a
    scrolled page the anchored popup landed outside the visible viewport. The geometry now runs in
    viewport coordinates (subtracting the page scroll) and clamps the popup fully within the visible
    viewport, then converts back to document coordinates for the absolute style; when the page is not
    scrolled the output is unchanged. Extracted the math into the pure, unit-tested
    client/lib/popupOffset.js, used by client/lib/popup.js. Covered by tests/popupOffset.test.cjs.
    Thanks to MarcusDger and xet7.

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