Releases: wekan/wekan
Release list
v9.89
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 boardsortcollection-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 secondBoards.allow({ update })rule returnedtruefor
any board member whenever the update touched thesortfield. Meteor evaluates allow rules with OR
semantics and does not scope an approving rule to the field that satisfied it: once any allow
callback returnstrueand no deny callback returnstrue, the entire modifier is applied.
BecausecanUpdateBoardSortonly checked thatsortwas 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$setassortin a single unprivileged DDPBoards.updatecall:
{$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$setof themembersarray bypassed it entirely.- Fixed by restricting
canUpdateBoardSort(server/lib/utils.js) so the sort-reorder rule
approves an update only whensortis the sole modified field (fieldNamesis exactly
['sort']) — it can no longer approve a modifier that also mutatesmembers,permission,title
or anything else. As defense in depth, the last-admin deny rule (server/permissions/boards.js) now
also rejects a$setrewrite of themembersarray 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 inprofile.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.
- Fixed by restricting
and adds the following updates:
Thanks to above GitHub users for their contributions and translators for their translations.
v9.88
v9.88 2026-07-13 WeKan ® release
This release adds the following new features:
- Added docs about new Sandstorm WeKan.
Thanks to xet7.
and fixes the following bugs:
- Sandstorm build: fixed "not writing through dangling symlink" that failed the
sandstorm.ymlworkflow atsandstorm-src/build-deps.shstep [3/7]. The
meteor-spk 0.6.0 base ships some runtime libs inmeteor-spk.deps/libas
DANGLING symlinks (e.g.libstdc++.so.6→ a library that no longer exists), so
refreshing them with the host's newer libs failed:cp -fLrefuses to write
through a dangling destination symlink. Added--remove-destinationsocp
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. Thesandstorm.yml"Restore the Sandstorm signing keyring"
step wrote the decodedSANDSTORM_KEYRINGsecret to~/.sandstorm/sandstorm-keyring,
butmeteor-spk/spkread the app private key from~/.sandstorm-keyring(a
file directly in$HOME), so packing aborted with
open(~/.sandstorm-keyring): No such file or directoryeven when the secret was
set. Now it writes~/.sandstorm-keyring, and — since the.spkcannot 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
.mjsimporter). Also setPUPPETEER_SKIP_DOWNLOAD=1for 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.spksize and warns if it exceeds
100 MB. (The Database Tools remain in the WeKan.zipbundle / Docker / Snap,
which do use them.) Thanks to xet7.
- the
- Sandstorm .spk: bundle the matching glibc dynamic loader so grains start:
the.spkbundled the host's new glibclibc.so.6(Ubuntu 24.04, glibc 2.39)
but kept the old glibc 2.31ld-linuxfrom the meteor-spk 0.6.0 base, sonode
failed at startup withlibc.so.6: undefined symbol: _dl_audit_symbind_alt, version GLIBC_PRIVATE(HTTP-BRIDGE exit 127) and the grain crash-looped.
sandstorm-src/build-deps.shnow also copies the host'sld-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 bundlednodecannot 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 defaultskernel.apparmor_restrict_unprivileged_userns=1, which
blocks the unprivileged user namespaces the Sandstorm install and thespk
supervisor rely on, sosandstorm.ymlnow relaxes it on the runner. A newref
workflow_dispatchinput also lets the workflow build a fix branch before it is
merged, instead of a hardcodedmain. Thanks to xet7. - Meteor unit tests: fix server-boot crash from
__dirnamein an ESM test file:
server/lib/tests/dependencies.openapi.tests.jsis an ES module that referenced
the bare__dirnameglobal; 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
withIdentifier '__dirname' has already been declaredand the "Meteor unit
tests" CI job died before any test ran. Drop the__dirnameseed
(process.env.PWDalready reaches the repo root undermeteor test). Thanks to xet7. - Sandstorm .spk: point FerretDB state dir to writable /var so the grain starts:
FerretDB persists astate.json(version/UUID) via its state provider even with
telemetry disabled, and its--state-dirdefaults to.— which in a Sandstorm
grain is/, read-only. So FerretDB failed withFailed 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.jsnow creates
/var/ferretdband passes--state-dir=/var/ferretdb(plusFERRETDB_STATE_DIR)
instartFerret(), 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, butcapnp.node(node-capnp) is built for an
older Node ABI (NODE_MODULE_VERSION 83 = Node 14), soNpm.require('capnp')in
sandstorm.jsfailed withERR_DLOPEN_FAILEDand crash-looped the whole grain on
boot. Cap'n Proto is now loaded lazily in atry/catchand degrades gracefully:
the grain boots and core WeKan works, because login and user identity come from
the sandstorm-http-bridgeX-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 ransnapcraft remote-buildfor 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-nativebuilds amd64 (ubuntu-24.04) and arm64 (ubuntu-24.04-arm)
natively on GitHub runners viasnapcore/action-build;snap-launchpadbuilds
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.jsand 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/Activitiescollections as implicit globals, but
those are now ES module default exports, soUsers.after.insertthrew
Users is not definedat boot; and it monkey-patchedHTTP.methodsfrom the
removedmeteor/httppackage, soHTTPwas undefined. The collections (and
Accounts) are now imported explicitly, and the obsoleteHTTP.methodspatch is
removed. Thanks to xet7. - Sandstorm .spk: use boolean index options so FerretDB accepts the users index:
wekan-accounts-sandstormcreated the unique index onservices.sandstorm.id
with{unique: 1, sparse: 1}. Real MongoDB accepts the truthy1, but FerretDB
(used by the Sandstorm.spk) is strict and rejects it withThe 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-bridgeadvertises
Accept-Encoding: gzipto the app regardless of what the browser actually sent,
so Meteor served gzip/brotli-encoded responses the browser could not decode. A
WebApp.rawHandlersmiddleware now stripsAccept-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-bridgethat mangles responses
(it always advertisesAccept-Encoding: gzipto the app and mishandles
redirect/encoding).build-deps.shnow overwrites the bundled
/sandstorm-http-bridgewith the modern one from a Sandstorm install
(/opt/sandstorm/latest/bin/sandstorm-http-bridge; override with
SANDSTORM_HTTP_BRIDGE), andsandstorm.ymlinstalls Sandstorm before assembling
the deps so the bridge is available on the CI ru...
v9.87
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-swimlanessortable — 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
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 oneferretdb-<arch>(.exeon Windows)
asset per platform instead of a single multi-platformferretdb.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 separatebuild-ferretdbjob and itsferretdb-zipartifact are removed),
the defaultdocker-compose.yml, and the Sandstormsandstorm-src/build-deps.sh.
FerretDB itself also moved its Go toolchain to 1.25.11 to clear the Quay.io
stdlibsecurity advisories. Thanks to xet7. - Drop the
mongoshbinary; use bundled Node.js 24 + themongodbdriver instead:
WeKan no longer bundles or downloads the MongoDB Shell anywhere (snap, Windows
bundle). Every scripted database operation it was used for — readinessping,
replica-setinitiate/status, and the v8.43 schema migration — now runs
through the newsnap-src/bin/db-eval(a tiny wrapper around the bundled Node.js
24 + themongodbdriver), so the snap control scripts (wekan-control,
mongodb-control,migration-control), thestart-wekan.sh/start-wekan.bat
launchers, and the portedmigrate-schema-v843.mjsare 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.2mongoshell (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 thefastdl.mongodb.org/downloads.mongodb.comdownloads. They are
embedded in the Linux.zipbundles (amd64/arm64/s390x/ppc64le/riscv64) — and
therefore in the Docker image, which is built from those bundles — the Windows
and macOS bundles, the Snap (mongotoolspart), 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.yamlis now thebase: core24,grade: stablebuild:
the previoussnapcraft.yaml(core26,grade: devel) is renamed to
snapcraft-core26.yaml, and the formersnapcraft-core24.yamlbecomes
snapcraft.yaml. Because the default isbase: core24(a released base), it can
be published to the Snap candidate channel, which core26 cannot. The automated
release-all.ymlworkflow 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
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 -dis 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 defaultdocker-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, rundocker compose -f docker-compose-mongodb-v7.yml up -dor rename that
file todocker-compose.yml.rebuild-wekan.sh/rebuild-wekan.batDocker 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 localDockerfile(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 obsoleteversion: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).
- PostgreSQL) and
-
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 with0) 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 (MongoDBdocker-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.shauto-detectsdocker compose
vs legacydocker-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 port8080:meteor runstarts an rspack
dev server on 8080 that can outlive themeteorparent, and a leftover one made the
restart crash withError: listen EADDRINUSE ... :8080. Port detection now checks the
listening socket directly (ss/lsof, with a bash/dev/tcpfallback that needs no
external tools, andnetstaton.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 meteorMongos (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, snapferretdb-control, the release
start-wekan.sh, and thedocker-compose-ferretdb-v1-sqlite.ymlexample), 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
caddypart (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
gpggot no key (no valid OpenPGP data found) and thecaddyoverride-buildfailed
with code 2 on both arches, all attempts. Bothsnapcraft.yamlandsnapcraft-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, nogpg, noxcaddy/custom-module build. -
Fix Admin Panel / Version showing "MongoDB" when running on FerretDB:
The database detection only recognised a
buildInfo.ferretdbsub-document, but the
wekan/FerretDB v1 fork reports its identity as a top-levelferretdbVersionstring
(e.g.v1.24.2-60-gb5523566) plusferretdbFeatures, with its git commit ingitVersion.
So the Version page showedDatabase type: MongoDBand hid the FerretDB rows. Detection now
handles both shapes (server/statistics.js),
so it showsDatabase type: FerretDB, theFerretDB versionandFerretDB commitrows, and
theSQLitestorage engine. (FerretDB v1'sversion: 7.0.42is 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
provenmigrate-mongo3-to-ferretdblogic (mongoexport read → FerretDB insert;
CollectionFS/Meteor-Files GridFS attachments+avatars → filesystem). Includes the
grain sandbox (seccomp) compatibility analysis, the rewrittenstart.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 methodssandstormMigrationStatus/
sandstormDeleteRawMongo(server/methods/sandstormMigration.js);
the migration importer now writes amigration-status.jsonthe 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 modernmeteor-spk.depson top of upstream meteor-spk 0.6.0
...
v9.84
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 loggedfindOne is not available on the server. Please use findOneAsync() instead.and the cron silently never registered, so scheduled
backups did not run.registerCron()is nowasyncand usesfindOneAsync,
thegetBackupSchedule/saveBackupSchedulemethods usefindOneAsync/
upsertAsync, and everyregisterCron()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 thetest:unit:nodesuite. -
Snap: show the correct writable path for parallel installs in backup instructions:
The backup/migration help text printed by
wekan.helphardcoded
/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
.loadLanguagesuite stubbedaddResourceBundleon the shared
TAPi18n.i18nsingleton and asserted the call count. BecauseloadLanguage()
readsthis.i18nlate 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
reportedexpected addResourceBundle to be called once. The tests now assert
on observable i18next state (hasResourceBundle/getResourceBundleunder the
normalisedtoI18nCode), which is instance-agnostic and deterministic and
mirrors the reliable.setLanguagesuite. Also stopped a leaked
Tracker.autorunin the.getLanguagereactive test (the cross-test hazard). -
The extra-arch bundle build ran
node:24-slimunder QEMU, but the official
node:24image publishes nolinux/s390xmanifest (only amd64/arm64/ppc64le),
so the s390x leg failed with "no matching manifest for linux/s390x". The
emulated native-module rebuild now runs on anubuntu:26.04base (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.zipnext tomain.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, plusDO_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. -
The WeKan snap gains a
databasesetting (mongodborferretdb). A new
ferretdbservice runs FerretDB v1 (SQLite) on the same port MongoDB would
use, soMONGO_URLis unchanged; only one database runs at a time, and the
configure hook stops one and starts the other when the setting changes. Switch
withsnap set wekan database=ferretdb(or back withdatabase=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=ferretdbflag (snapd has no custom install options); use
the two-stepsnap install wekan --channel=latest/betathen
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). -
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 singleferretdb.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.zipfrom 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 withENOENT ... 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 thebuild-chunks/prefix, but
client/00-startup.js set the sub-path public path to<sub-path>/build-chunks/,
so rspack appended a secondbuild-chunks/. It now sets the public path to just
<sub-path>/and lets rspack addbuild-chunks/itself. -
Add snapcraft-core24.yaml so the newest WeKan can be published to the Snap Stable channel:
The main
snapcraft.yamlusesbase: core26, which (until core26 is released)
needsbuild-base: devel+grade: devel, so it can only go to Snap Beta/Edge.
snapcraft-core24.yamlbuilds the SAME newest WeKan (Meteor 3.5, Node.js 24,
FerretDB v1, MongoDB 7, Caddy 2) onbase: 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>.zipis now fully offline. Itsbundle/directory
contains the WeKan server, a Node.js binary for that platform, a FerretDB v1
(SQLite) binary, and astart-wekan.sh(start-wekan.baton Windows) that by
default runs WeKan on the bundled Node against the bundled FerretDB SQLite,
storing data and attachments/avatars on the filesystem underWRITABLE_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. -
Sandstorm packaging (mirroring
releases/release-sandstorm.sh+
install-sandstorm.sh: installs Meteor, meteor-spk 0.6.0 and a dev Sandstorm,
runsmeteor-spk pack) is not tested well enough yet, so it no longer runs
as part of a full release — thebuild-sandstormjob was removed from
release-all.yml. Instead a separate, manually-triggeredsandstorm.ymlbuilds
ONLY the.spkand uploads it aswekan-sandstorm-YYYY_MM_DD-HH_MM_SS.spkto 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.spkneeds th...
v9.83
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.lengthyielded
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
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.createRulemethod, keeping the action's destination boardId. And
execution crashed with an "Internal Server Error" because the destination
swimlane fallback dereferenced._idon a possibly-undefined swimlane titled
exactlyDefault(renamed/translated/deleted on the destination board);
resolution now uses the board's real default swimlane via a Meteor-free
resolvermodels/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-shotsetBackgroundImage()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 intests/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.jscomputes 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 withweekNumberByFirstDay()(inmodels/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:
WithLDAP_UTF8_NAMES_SLUGIFYenabled,limax(text, { separator: '.' })
turned every non-alphanumeric run — hyphens included — into., so an LDAP
username likep.parta-partbbecamep.parta.partband 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
helperpackages/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-safegetISOWeek()computes it correctly. Added
tests/isoWeek.test.cjspinning 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 helpermodels/lib/boardCopyDefaults.js,
unit tested intests/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 viaActivities.commentDisplayText(), which
falls back to the live comment text and then to an empty string — never
"undefined". Pure helpermodels/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/swimlanesset 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 explicitsortin
the body. Pure helpermodels/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
helpermodels/lib/subtaskCopy.js, unit tested intests/subtaskCopy.test.cjs.
Thanks to ramses345 and xet7. - Fix #5439: list scrollbar is always visible on desktop and mobile browsers:
List bodies usedoverflow-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 inmodels/lib/scrollbarCss.js,
applied inclient/components/lists/list.css, unit tested (builder contract +
applied CSS, positive + negative) intests/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 isdir="rtl", and both the
minicard title (adir="auto".viewer) and the add-card composer textarea
weredir="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 useunicode-bidi: isolate. Locked in (positive + negative)
bytests/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...
v9.81
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/:listIdalready acceptedtitle,color,
starredandwipLimit, but the color was stored unvalidated; it now
validates the color withnormalizeListColor(a named palette color or a
custom#rrggbbhex) and rejects an unknown color with a clear 400 instead
of silently storing None. The pure field/validation logic lives in a
Meteor-free helpermodels/lib/listApiUpdate.jsand is unit tested in
tests/listApiUpdate.test.cjs. Theapi.pyreference CLI gains two new
commands mirroringeditcard/editcardcolor:editlist BOARDID LISTID NEWLISTTITLEandeditlistcolor 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#rrggbbcolor 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 inlinebackground-colorinstead of the named
CSS class. A new pure, Meteor-free helpermodels/lib/contrastColor.jscomputes 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 bytests/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.jsused byserver/rulesHelper.jsand 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; theAccounts.onCreateUserhook 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-testedorgsToAutoAddForEmailhelper.
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 viagetLabelByIdand a pure, unit-testedlabelDisplayNamehelper (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-changedDescriptionactivity 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-testeddescriptionChangedhelper.
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,
allowIsBoardAdminreturned 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 methodrules.deleteRulethat 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
labelkey, so a body without one hung until the client timed out, and a bare-stringlabelpushed 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/.namedirectly 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-...
v9.80
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-testedmodels/lib/domainTablePage.js
behind a newgetDomainsWithUserCountsPageadmin method (server/models/users.js), and the
domainGeneraltemplate (client/components/settings/peopleBody.{jade,js,css}) is now
self-contained and fetches only the current page. Covered bytests/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 toen(en.i18n.json) when none is set. The behaviour was already correct;
the language choice is now extracted into the pure, unit-testedmodels/lib/inviteEmailLanguage.js
used byinviteUserToBoard, and locked in bytests/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 atlinkedId— and every other
minicard getter resolves through the real card (getTitle/getReceived/getDue/…), but the
cover helpers readthis.coverIddirectly, and a linked card has nocoverIdof 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 theboardpublication), so this was purely a
client-side resolution gap.Card.cover()and the minicardcover()helper now resolve the
cover id through the real card via the pure, unit-testedmodels/lib/linkedCardCover.js; normal
cards are unaffected. Covered bytests/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 isposition: 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._getOffsetcomputed the space above/below the opener and the clampedtopfrom
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 byclient/lib/popup.js. Covered bytests/popupOffset.test.cjs.
Thanks to MarcusDger and xet7.
Thanks to above GitHub users for their contributions and translators for their translations.