Compare commits

...
4162 Commits
Author SHA1 Message Date
github-actions[bot] 98c0f72aaf Bump versions for v11.26 2026-08-29 13:38:51 +00:00
Lauri Ojansivu 34482b92b0 Prepare v11.26 release 2026-08-29 16:37:37 +03:00
Lauri Ojansivu 7a4b3e7b2e Update Upcoming changelog for persistent login closure
Record the corrected issue-closing metadata, restore the required binaries-table placement, and replace the Windows CI placeholder with its commit link.

Thanks to majo1989 and xet7 !
2026-08-29 16:36:19 +03:00
Lauri Ojansivu a1b93526d5 Close persistent login delivery report
Persistent login was fixed in WeKan 11.25 by delivering FerretDB v1.63.0 or newer, refreshing cached deployment binaries, and testing the compatibility gate. This follow-up uses a correctly formatted closing footer because the original commit stored its line breaks literally.

Thanks to majo1989 and xet7 !

Fixes #6646,
2026-08-29 16:32:07 +03:00
Lauri Ojansivu 5afc0c2944 Merge pull request #6647 from wekan/copilot/fix-windows-exe-smoke-test
ci(windows): stop MongoDB service before smoke test to free ports 27017/8088
2026-08-29 16:11:18 +03:00
copilot-swe-agent[bot]andxet7 f0ecb14341 ci: stop MongoDB service before Windows EXE smoke test to free ports 27017/8088
The GitHub Actions windows-latest runner ships MongoDB Server 7.0 as a
running Windows service. The bundled FerretDB inside the packaged WeKan EXE
tries to bind to 127.0.0.1:27017 and 127.0.0.1:8088 (debug handler). When
those ports are already occupied, FerretDB exits immediately, WeKan restarts
in a tight loop, and the smoke test times out with:

  The packaged EXE did not answer on port 8080.

Failing run: https://github.com/wekan/wekan/actions/runs/33253053694

Fix: add a "Free ports used by the packaged EXE before smoke test" step in
.github/workflows/windows.yml that stops the MongoDB (and mongod) Windows
service and kills any remaining process on ports 27017 and 8088 before the
smoke EXE is launched. This is CI-only and has no effect on end-user behavior.

Thanks to xet7 !

Co-authored-by: xet7 <15545+xet7@users.noreply.github.com>
2026-08-29 13:09:13 +00:00
copilot-swe-agent[bot] 1c29a9ddb3 Initial plan 2026-08-29 13:07:35 +00:00
Lauri Ojansivu 372f0a8889 Use the archived Enigma Virtual Box installer
Download the reviewed Enigma Virtual Box 11.30 installer from the WeKan v11.25 release and retain its pinned SHA256, so a vendor-side replacement cannot silently change the Windows packaging tool.\n\nThanks to xet7 !
2026-08-29 15:37:41 +03:00
Lauri Ojansivu a57620dc1e Document the portable Windows executable
Add the Enigma Virtual Box single-file package to the upcoming release notes and allow WeKan release entries to link their companion charts commits directly.\n\nThanks to xet7 !
2026-08-29 15:33:39 +03:00
Lauri Ojansivu 5ed46d1f2e Publish a portable Windows single-file WeKan
Wrap the verified win64 release bundle with Enigma Virtual Box FREEWARE, launch its existing start-wekan.bat so Node.js and FerretDB SQLite run together, and keep persistent data beside the executable in wekan-files. Smoke-test the final executable and integrate it with full and missing-asset releases.\n\nThanks to xet7 !
2026-08-29 15:33:38 +03:00
github-actions[bot] 6bfc7591e3 Bump versions for v11.25 2026-08-29 12:01:44 +00:00
Lauri Ojansivu 6f1cd83188 Prepare v11.25 release 2026-08-29 15:00:27 +03:00
Lauri Ojansivu 59fb432916 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-29 15:00:05 +03:00
Lauri Ojansivu 474aa2a0ae Document deployment binary refresh fixes
Add the alternate Compose and Helm image-refresh changes to the upcoming WeKan release notes.\n\nThanks to xet7 !
2026-08-29 15:00:04 +03:00
Lauri Ojansivu ab8c706137 Refresh cached FerretDB in all Compose variants
Make the PostgreSQL, MySQL, MariaDB and SAP HANA FerretDB v1 services resolve the concrete latest release, verify its published checksum and replace a differently tagged cached executable atomically. Extend binary freshness regression coverage to every Compose variant.\n\nThanks to xet7 !
2026-08-29 15:00:04 +03:00
Lauri Ojansivu 6953123ba2 Document persistent login delivery fix
Describe the #6646 version-skew failure, Docker cache refresh, compatible FerretDB preflight, and newest-binary workflow guarantees in Upcoming.\n\nThanks to majo1989 and xet7 !
2026-08-29 15:00:04 +03:00
Lauri Ojansivu 2258d82289 Always resolve newest release binaries
Resolve Docker's latest FerretDB tag on every start, replace a differently tagged cached binary only after checksum verification, and pin release-workflow tests to newest FerretDB, mongo-tools-patches, and applicable node-patches assets.\n\nThanks to xet7 !\n\nFixes #6646,
2026-08-29 15:00:04 +03:00
Lauri Ojansivu 7ac9588884 Deliver persistent login support with FerretDB
Require FerretDB v1.63.0 or newer when assembling WeKan bundles so Meteor's standard nested positional resume-token projection is available. Pin Docker to that compatible release and replace cached older binaries atomically instead of retaining them forever.\n\nThanks to majo1989 and xet7 !\n\nFixes #6646,
2026-08-29 15:00:04 +03:00
github-actions[bot] 464e51d3b4 Bump versions for v11.24 2026-08-29 09:53:43 +00:00
Lauri Ojansivu b5374a041d Prepare v11.24 release 2026-08-29 12:52:20 +03:00
Lauri Ojansivu db87e70c0d Document hardcoded notification title layouts
Update the Upcoming summary and translation topic to explain that non-language board and card title structure now belongs to server code instead of locale files.\n\nThanks to xet7 !
2026-08-29 12:50:49 +03:00
Lauri Ojansivu 162740e885 Keep notification title placeholders out of translations
Remove the board-only and board/card notification layouts from every locale bundle. Format those structural titles in server code, retain translation fallback for prose titles, and cover both paths plus all locale files.\n\nThanks to xet7 !
2026-08-29 12:50:20 +03:00
Lauri Ojansivu 64ee8c408c Document translation placeholder repair
Replace the empty Upcoming text with the translation regression outcome, its verified scope, and the commit that restores the protected activity tokens.\n\nThanks to xet7 !
2026-08-29 12:45:55 +03:00
Lauri Ojansivu c9f42889af Restore protected translation placeholders
Restore activity-title placeholders to the exact executable tokens used by the English source across sixteen locale files. Existing regression suites verify their inventories and values.\n\nThanks to xet7 !
2026-08-29 12:44:57 +03:00
Lauri Ojansivu bffae719c6 Updated translations. 2026-08-29 12:42:58 +03:00
github-actions[bot] 1f814c6a05 Bump versions for v11.23 2026-08-29 09:40:54 +00:00
Lauri Ojansivu f07ca7fa7a Prepare v11.23 release 2026-08-29 12:39:57 +03:00
Lauri Ojansivu e20fdd17c7 Carry build fixes into upcoming release
Thanks to xet7 !
2026-08-29 12:38:53 +03:00
Lauri Ojansivu 934a3a0578 Document release build fixes
Thanks to xet7 !
2026-08-29 12:38:04 +03:00
Lauri Ojansivu e85d9615d8 Fix Snap assembly and architecture preflight
Copy the MongoDB-to-FerretDB migrator from the repository after Snapcraft enters .build, and retry transient base-image manifest failures without accepting partial responses.\n\nThanks to xet7 !
2026-08-29 12:38:04 +03:00
github-actions[bot] 50a2ee7efd Bump versions for v11.22 2026-08-29 06:47:32 +00:00
Lauri Ojansivu 02ca9f9781 Prepare v11.22 release 2026-08-29 09:46:10 +03:00
Lauri Ojansivu 4ea8f65244 Carry RuleBleed fixes into upcoming release
Thanks to crypto-nidh and xet7 !
2026-08-29 09:44:39 +03:00
Lauri Ojansivu f1eb971c13 Document isolated RuleBleed browser coverage
Thanks to xet7 !
2026-08-29 09:43:23 +03:00
Lauri Ojansivu 6d50564c82 Isolate RuleBleed browser regression data
Scope the denied-rule assertion to its fixture board and remove automation documents during board cleanup so interrupted or earlier browser runs cannot contaminate later projects.\n\nThanks to xet7 !
2026-08-29 09:43:23 +03:00
Lauri Ojansivu 17d74c3152 Document RuleBleed security fix
Thanks to crypto-nidh and xet7 !
2026-08-29 09:43:23 +03:00
Lauri Ojansivu dffe85cce4 Block unauthorized cross-board rule actions
Validate automation destinations before storing a rule and again before executing legacy or imported move, link, and bulk-move actions. Record denied attempts through the bounded RuleBleed canary.\n\nThanks to crypto-nidh and xet7 !
2026-08-29 09:43:23 +03:00
github-actions[bot] a4ee902981 Bump versions for v11.21 2026-08-29 03:32:19 +00:00
Lauri Ojansivu e3e9d0da65 Prepare v11.21 release 2026-08-29 06:31:09 +03:00
Lauri Ojansivu c085bde087 Document regression suite repairs
Thanks to xet7 !
2026-08-29 06:28:57 +03:00
Lauri Ojansivu 357bdc1e94 Fix failing regression suites
Restore protected activity placeholders in locale data, align source-based assertions with the current implementations, exclude diagnostic and runtime-only paths from parity checks, and allow the reactive browser test its observed bounded delivery time.\n\nThanks to xet7 !
2026-08-29 06:28:28 +03:00
Lauri Ojansivu 995db5568e CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-29 06:01:42 +03:00
Lauri Ojansivu c5b0ed6436 Record reliable WeKan FerretDB workflow
Update the upcoming release summary and database notes with the restored large-dataset result: board creation, reactive tiles, and opening boards work reliably, with a small remaining non-blocking delay.\n\nThanks to xet7 !
2026-08-29 06:01:39 +03:00
Lauri Ojansivu a9789409a8 Document immediate FerretDB OpLog batches
Record the FerretDB awaitData ordering fix that keeps reactive board lists and new-board subscriptions from waiting for another mutation.\n\nThanks to xet7 !
2026-08-29 06:01:39 +03:00
Lauri Ojansivu 9fbc2136ff Document create-board form fix
Add the shared form submission repair to the upcoming release notes and summarize its regression coverage.\n\nThanks to xet7 !
2026-08-29 06:01:39 +03:00
Lauri Ojansivu e1fa6ce71c Fix shared create-board form submission
Bind create-board events to the included form template that renders their DOM, and pass each owning template instance explicitly so visibility, template-board, and header-star behavior retain their state. Read the title from the submitted form and add positive and negative regression checks for the event ownership.\n\nThanks to xet7 !
2026-08-29 06:01:39 +03:00
Lauri Ojansivu 5827f41e42 Document minicard composer save fix 2026-08-29 06:01:39 +03:00
Lauri Ojansivu dbbc2fd9ea Save the submitted minicard composer 2026-08-29 06:01:39 +03:00
Lauri Ojansivu bdfa37c76f Document faster FerretDB startup probes 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 3b2b801750 Document faster FerretDB numeric checks 2026-08-29 06:01:39 +03:00
Lauri Ojansivu dbc1876b9b Show live FerretDB index migration progress 2026-08-29 06:01:39 +03:00
Lauri Ojansivu afebc27780 Document compound FerretDB distinct coverage 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 8361a339b4 Show branded dashboard during FerretDB preparation 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 5394b4e8f3 Wait for FerretDB before starting debug WeKan 2026-08-29 06:01:39 +03:00
Lauri Ojansivu ae04749e72 Document covering FerretDB distinct indexes 2026-08-29 06:01:39 +03:00
Lauri Ojansivu a63ee6c197 Document cached FerretDB row layouts 2026-08-29 06:01:39 +03:00
Lauri Ojansivu cb2a193370 Document linear FerretDB document construction 2026-08-29 06:01:39 +03:00
Lauri Ojansivu de3222345f Document nested FerretDB decoder optimization 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 7db4666c73 Document raw-value FerretDB distinct pushdown 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 065b8d56f9 Document linear FerretDB distinct deduplication 2026-08-29 06:01:39 +03:00
Lauri Ojansivu bdb9f3df01 Document FerretDB numeric repair pushdown 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 36ac7f0039 Document FerretDB hot-schema optimization 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 6e5941b3c7 Document lower-allocation FerretDB decoding 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 4b63b1a6b8 Document SJSON decoder optimization 2026-08-29 06:01:39 +03:00
Lauri Ojansivu fc0b632d9e Document indexed distinct diagnostics 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 7279e1b7db Document SQLite distinct pushdown 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 4d6dd22348 Document FerretDB distinct optimization 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 16c4b3cad3 Document FerretDB projection ID fix 2026-08-29 06:01:39 +03:00
Lauri Ojansivu a086fd4696 Document FerretDB projection performance fix 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 271018bda4 Document FerretDB idle OpLog fix 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 91a5911474 Document immediate bundled English startup 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 92d25f750f Render bundled English before database overrides 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 39718849cf Document write-notified FerretDB reactivity 2026-08-29 06:01:39 +03:00
Lauri Ojansivu cacf4049c9 Enable write-notified FerretDB OpLog reactivity 2026-08-29 06:01:39 +03:00
Lauri Ojansivu aa666763ff Document standard resume login restoration 2026-08-29 06:01:39 +03:00
Lauri Ojansivu aedcb600b9 Use Meteor resume login without a database override 2026-08-29 06:01:39 +03:00
Lauri Ojansivu af5f216074 Document database-independent selectors 2026-08-29 06:01:39 +03:00
Lauri Ojansivu ca2838ab2f Restore database-independent MongoDB selectors 2026-08-29 06:01:39 +03:00
Lauri Ojansivu b86a9914cc Revert "Bound empty board creation and publication scans"
This reverts commit 8229e170983c62754baee81103dd4b0bbf628378.
2026-08-29 06:01:39 +03:00
Lauri Ojansivu f05b6b48b9 Revert "Document bounded empty board startup"
This reverts commit 7b2b640a8ddd626dd771d12362b5e8d2eee101b6.
2026-08-29 06:01:39 +03:00
Lauri Ojansivu 175caec891 Document bounded empty board startup 2026-08-29 06:01:39 +03:00
Lauri Ojansivu b7dc63571f Bound empty board creation and publication scans 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 4d3fb0685a Document bounded registration lookup 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 0ea301d7ee Avoid full user scan during registration 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 5ec5c9b6d8 Document automatic DEBUGSPEED browser setup 2026-08-29 06:01:39 +03:00
Lauri Ojansivu cae03f7af9 Install DEBUGSPEED browser dependency automatically 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 0ea99b2ba2 Document FerretDB-only DEBUGSPEED launcher 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 5b9b90f52f Add FerretDB-only DEBUGSPEED launcher 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 67df46d696 Document supervised DEBUGSPEED logging 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 3240ddd1b9 Supervise DEBUGSPEED instrumentation and logs 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 1a697b0b5b Document DEBUGSPEED connection logging 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 350ec9000f Capture FerretDB diagnostic connection failures 2026-08-29 06:01:39 +03:00
Lauri Ojansivu c6d906a2f1 Update upcoming WeKan changelog 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 186035df54 Add opt-in database speed diagnostics 2026-08-29 06:01:39 +03:00
Lauri Ojansivu 3b88b285bb Document three security fixes
Add the required CRITICAL Upcoming entries and record why avatar and legacy attachment response hardening has no attributable runtime event. Preserve the earlier distinct AvatarBleed name by publishing the new response flaw as AvatarMimeBleed.

Thanks to crypto-nidh and xet7 !
2026-08-29 06:01:39 +03:00
Lauri Ojansivu 8d427583ef Harden avatar attachment and swimlane boundaries
Serve browser-executable avatar and legacy attachment MIME types as sandboxed opaque downloads, and require board write permission before creating a default swimlane. Summarize rejected swimlane creation attempts in Admin Panel Problems and add positive plus repository-wide negative regression coverage.

Thanks to crypto-nidh and xet7 !
2026-08-29 06:01:38 +03:00
Lauri Ojansivu baa618dbfb Update Upcoming changelog for snap builds
Thanks to xet7 !
2026-08-29 06:01:38 +03:00
Lauri Ojansivu 4122588b3c Fix snap bundle extraction paths
After entering .build, copy Node.js and the completed release bundle from bundle/ instead of the nonexistent .build/.build/bundle path. Apply the correction to both Core 24 and staged Core 26 definitions and pin the working-directory invariant with a regression test.

Thanks to xet7 !
2026-08-29 06:01:38 +03:00
Lauri Ojansivu f28e31f104 Update Upcoming changelog for shared ticker
Thanks to xet7 !
2026-08-29 06:01:38 +03:00
Lauri Ojansivu ebf1ca6143 Share the card date minute ticker
Replace per-date-badge intervals with one reference-counted ticker. Unsubscribing is idempotent, and destroying the final subscriber clears the shared interval so removed views leave no background timer behind.

Thanks to xet7 !
2026-08-29 06:01:38 +03:00
Lauri Ojansivu 825e086030 Update Upcoming changelog for database launchers
Thanks to xet7 !
2026-08-29 06:01:38 +03:00
Lauri Ojansivu ecfbd0bf66 Keep FerretDB launchers standalone
Run bundled and containerized FerretDB with polling and no replica-set state. Keep replica-set setup explicit for MongoDB 7 and the Meteor 3 multitenancy deployment, including an idempotent initialization helper and scoped OpLog credentials.

Thanks to xet7 !
2026-08-29 06:01:38 +03:00
github-actions[bot] 63c9e1385e Bump versions for v11.20 2026-08-28 15:20:24 +00:00
Lauri Ojansivu 1922cf2908 Prepare v11.20 release 2026-08-28 18:19:24 +03:00
Lauri Ojansivu b9d8b9028e Document live lazy-board refreshes
Add issue #6645 to the upcoming release, explain the stale snapshot regression and record the observer-driven bounded refresh plus its unit and browser coverage.

Thanks to hmeunier95 and xet7 !
2026-08-28 17:40:43 +03:00
Lauri Ojansivu 29fd503317 Refresh lazy card windows after card changes
Keep FerretDB's bounded card window as a fetched snapshot, but observe the unrestricted indexed selector and diff a fresh window whenever a matching card changes. Publish added, changed and removed DDP messages so remote edits and cross-list moves render without a browser reload. Cover additions, edits, removals, unchanged cards, cleared fields and a forced-lazy browser scenario.

Thanks to hmeunier95 and xet7 !

Fixes #6645,
2026-08-28 17:40:10 +03:00
Lauri Ojansivu 2bbbe00869 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-28 17:26:59 +03:00
Lauri Ojansivu 90977a714a Document faster Launchpad snap builds
Record reuse of completed architecture bundles, removal of duplicate npm work and compiler packages, faster LZO packing, and the regression coverage in the upcoming release notes.

Thanks to xet7 !
2026-08-28 17:26:58 +03:00
Lauri Ojansivu 6c12a9c96c Speed up Launchpad snap assembly
Reuse the architecture-specific bundle that build-extra-arches already rebuilt and pruned, instead of deleting its dependencies and repeating npm installation on Launchpad. Use the nil plugin, remove the unnecessary compiler toolchain, and select faster LZO compression for the large snap on both maintained base definitions.

Thanks to xet7 !
2026-08-28 17:26:58 +03:00
Lauri Ojansivu 15274f644b Document translation command injection fixes
Record CodeQL alerts 439 and 440, their standalone-tooling exposure boundary, the argument-based Git invocation, and positive and negative regression coverage in the upcoming release notes.

Thanks to GitHub CodeQL code scanning and xet7 !
2026-08-28 17:26:58 +03:00
Lauri Ojansivu 27395417eb Avoid shells for translation Git commands
Pass revisions and locale paths to Git as separate arguments and parse changed paths with NUL delimiters. Add positive and negative regressions proving normal restoration and reporting still work while shell syntax in a locale filename is never executed.

Thanks to GitHub CodeQL code scanning and xet7 !
2026-08-28 17:26:58 +03:00
github-actions[bot] 2e99cee6cd Bump versions for v11.19 2026-08-28 14:03:24 +00:00
Lauri Ojansivu 92f887e963 Prepare v11.19 release 2026-08-28 17:02:01 +03:00
Lauri Ojansivu e40455ca6f Document safe Transifex push removal
Record the removal of force-push translation helpers in the upcoming release notes, prevent target-translation pushes from returning, and classify the historical-human restoration script as an internal pull helper.

Thanks to xet7 !
2026-08-28 17:00:49 +03:00
Lauri Ojansivu 89698bf46a Removed translation push to transifex scripts that are not safe to use.
Thanks to xet7 !
2026-08-28 16:52:30 +03:00
Lauri Ojansivu d3168dab7a Record successful corrected translation pull
Add the final live Transifex pull counts and clean integrity results to the upcoming release notes.

Thanks to translators and xet7 !
2026-08-28 16:44:57 +03:00
Lauri Ojansivu 0f84df07f8 Document post-marker human restoration
Record the live-pull ordering edge case, restored values and regression coverage in the upcoming release notes.

Thanks to translators and xet7 !
2026-08-28 16:39:27 +03:00
Lauri Ojansivu 38ed4a0d4b Restore reviewed humans after decoding Transifex markers
Run the reviewed pre-fill restoration after protected @PH markers are decoded, because eight known bad values only acquire their recognizable final text at that stage.

Thanks to translators and xet7 !
2026-08-28 16:39:10 +03:00
Lauri Ojansivu 071c9c1158 Document recovered pre-fill human translations
Record the history boundary, reviewed restoration set, retained newer community work and future bad-value rejection in the upcoming release notes.

Thanks to translators and xet7 !
2026-08-28 16:35:22 +03:00
Lauri Ojansivu 57c1fcabf0 Restore human translations displaced after mass fills
Audit every non-English value from the pre-fill boundary against current translations. Restore fourteen reviewed human values that were replaced by wrong-language or malformed Transifex data while retaining seventeen newer valid Spanish, Kinyarwanda and Traditional Chinese translations.

Reject the exact known bad Transifex fingerprints on future pulls without preventing corrected community translations from taking precedence.

Thanks to translators and xet7 !
2026-08-28 16:34:57 +03:00
Lauri Ojansivu 5633202d85 Document safe Transifex precedence merge
Record the one-way pull workflow, the reviewed human translation merge, the Mongolian wrong-language rejection, and preserved local fallback coverage in the upcoming release notes.

Thanks to translators and xet7 !
2026-08-28 16:17:18 +03:00
Lauri Ojansivu 0e12a467a6 Merge Transifex humans over local translation fills
Snapshot translations before pulling, prefer valid target-language Transifex values per key, and restore pre-pull local machine or human translations wherever Transifex has no valid translation. Reject the known Russian-seeded Mongolian values, repair protected tokens, and never push provenance-unknown local fills back to Transifex.

Merge 47 current human translations across 29 non-English locale files while retaining every available local fallback. English variants also receive four current source keys.

Thanks to translators and xet7 !
2026-08-28 16:16:55 +03:00
Lauri Ojansivu 5f6cadea66 Keep post-release entries in Upcoming
Move the test-matrix and local Node entries out of the immutable v11.17 record because both commits followed its release boundary. Keep their summaries and details under the current Upcoming section.

Thanks to xet7 !
2026-08-28 15:39:30 +03:00
Lauri Ojansivu af81886ca7 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-28 15:37:40 +03:00
Lauri Ojansivu 8d07727828 Document exact hostname regression checks
Record the CodeQL #437 and #438 test-only cleanup and its positive and negative regression coverage in the upcoming release notes.

Thanks to GitHub CodeQL code scanning and xet7 !
2026-08-28 15:37:40 +03:00
Lauri Ojansivu 5fb0a77bfa Make hostname example assertions exact
Parse the documented hostname list into exact tokens so positive and wildcard-lookalike coverage remains explicit without triggering incomplete URL substring sanitization alerts in test-only code.

Thanks to GitHub CodeQL code scanning and xet7 !
2026-08-28 15:37:40 +03:00
github-actions[bot] 01a6fdd495 Bump versions for v11.18 2026-08-28 12:23:41 +00:00
Lauri Ojansivu 388e2822d0 Prepare v11.18 release 2026-08-28 15:22:41 +03:00
Lauri Ojansivu 811de85e95 Document authoritative location regression
Add the focused issue 6644 browser assertion correction to the Upcoming Card locations group.

Thanks to majo1989 and xet7 !
2026-08-28 15:00:19 +03:00
Lauri Ojansivu 34a1cc9e13 Check location removal on the seeded card
Name the issue in the live regression and query the authoritative seeded Alpha Card by board and title before asserting its locations array is empty.

Thanks to majo1989 and xet7 !
2026-08-28 15:00:09 +03:00
Lauri Ojansivu 927714197a Document archive and location fixes
Add issues 6643 and 6644 to the Upcoming release with their causes, behavior and regression coverage.

Thanks to majo1989 and xet7 !
2026-08-28 14:57:34 +03:00
Lauri Ojansivu 7dd3673c5f Keep archived helper boards and location removal reachable
Show archived subtask helper boards in Archive while continuing to hide active helpers from ordinary board lists. Resolve the surrounding card from the nested location row before awaiting removal.

Add positive, negative and live browser regressions for both paths.

Thanks to majo1989 and xet7 !

Fixes #6643, #6644,
2026-08-28 14:57:08 +03:00
Lauri Ojansivu fe1818daa9 Update translations. 2026-08-28 14:50:04 +03:00
Lauri Ojansivu f6b77ed810 Repoint matrix changelog links after release
Update the Upcoming entries to the current implementation commit hashes after the v11.17 release rewrite moved the commits.

Thanks to xet7 !
2026-08-28 12:01:47 +03:00
Lauri Ojansivu cfe3ba5107 Document local Node test discovery
Add the repository-local Node fallback and its Playwright version-discovery impact to the Upcoming release test-matrix group.

Thanks to xet7 !
2026-08-28 11:45:50 +03:00
Lauri Ojansivu 8122c84a48 Keep tests on an installed local Node
When the Dockerfile Node patch is newer than the toolchain already downloaded under .tools, select the newest installed architecture-matching Node so plain imports and Playwright Docker version discovery do not fall back or fail.

Thanks to xet7 !
2026-08-28 11:45:50 +03:00
Lauri Ojansivu 53b7d34c0f Document WeKan matrix reliability fixes
Add the resumed-language, deterministic speech-scroll and reusable AppImage timeout fixes to the Upcoming release.

Thanks to xet7 !
2026-08-28 11:45:50 +03:00
Lauri Ojansivu f011debe31 Fix remaining WeKan matrix regressions
Apply persisted profile languages reactively after login resume, give the speech-scroll regression a deterministic overflow region, and bound every job inside the reusable AppImage workflow while teaching the release guard about callable jobs.

Thanks to xet7 !
2026-08-28 11:45:50 +03:00
github-actions[bot] 6798e08015 Bump versions for v11.17 2026-08-28 05:31:58 +00:00
Lauri Ojansivu 705a9f0536 Prepare v11.17 release 2026-08-28 08:30:54 +03:00
Lauri Ojansivu dea88e6b85 Document current translation progress
Add the completed Swati locale, first Sesotho batch and refreshed measured completion assertions to the Upcoming release summary and translation subsection.

Thanks to xet7 !
2026-08-28 08:29:10 +03:00
Lauri Ojansivu fd694a1643 Refresh completed translation assertions
Update the checked README count to the current 197 essentially complete locales and make the Northern Sotho regression assert its measured zero-placeholder completion state.

Thanks to xet7 !
2026-08-28 08:25:36 +03:00
Lauri Ojansivu 8043eb3762 Document stable card browser regressions
Add the browser-target follow-up to the upcoming Card titles group and record the focused Chromium verification and remaining three-browser matrix boundary.

Thanks to jullbo, rmb82 and xet7 !
2026-08-28 08:25:04 +03:00
Lauri Ojansivu ce5ef7fde5 Make card interaction regressions browser-stable
Route card-opening specs through the shared wrapper-link helper now that title text intentionally edits inline. Keep the Firefox mouse-selection drag over rendered text, and replace a strict union locator with the stable board canvas when checking that a failed copy did not crash the page.

Thanks to jullbo, rmb82 and xet7 !

Fixes #6639,

Fixes #6641,
2026-08-28 08:24:49 +03:00
Lauri Ojansivu ac80064171 Begin the Sesotho translation
Translate the first 50 Sesotho placeholders covering core board activities, comments, dates, attachments, labels, checklists, custom fields, archive actions and imports. Preserve the exact format-token inventory and add language-specific progress coverage.

These direct translations are low confidence and welcome review by a Sesotho speaker.

Thanks to xet7 !
2026-08-28 07:33:30 +03:00
Lauri Ojansivu 87d527c891 Complete the Swati translation
Translate the remaining 417 Swati strings covering scheduled jobs, storage, migrations, backups, privacy, monitoring, repositories and scoped imports. Correct Zulu-style board vocabulary in the new same-script tail, preserve every code placeholder and HTML tag, and add completion and language-specific regression coverage.

These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 07:32:01 +03:00
Lauri Ojansivu 29f75595ad Document card and REST fixes
Update the upcoming release summary and grouped bug-fix entries for issues 6639, 6640 and 6641 and the integrated REST deletion work from PR 6642. Record the positive, negative, generated-documentation and live Chromium verification.

Thanks to jullbo, rmb82, AhmedLukman and xet7 !
2026-08-28 07:10:00 +03:00
Lauri Ojansivu 8031e2089f Keep card browser tests on stable targets
Retain the card id while inline editing replaces searchable title text, and have the shared open-card helper activate the enclosing card link instead of the intentional title editor target. This makes the real browser regressions exercise the fixed interactions reliably.

Thanks to jullbo, rmb82 and xet7 !

Fixes #6639,

Fixes #6641,
2026-08-28 07:09:45 +03:00
Lauri Ojansivu 6acfb396e4 Confirm REST user deletion results
Use the database removal count to distinguish a confirmed deletion from a missing user and unexpected outcomes. Extend OpenAPI generation with repeatable response contracts, document the 200 and 404 bodies, and cover authorization, failures, generated documentation, and the live REST path.

Thanks to AhmedLukman and xet7 !

Integrates PR #6642.
2026-08-28 07:09:45 +03:00
Lauri Ojansivu 73eb57cb33 Cover independent comment markdown rendering
The per-render fallback added in 75a23b76a already keeps one malformed comment from blanking the card or Activities. Add the reported French emphasis, list, and literal greater-than shapes to the real markdown renderer regression suite.

Thanks to rmb82 and xet7 !

Fixes #6640,
2026-08-28 07:09:45 +03:00
Lauri Ojansivu b8528e30ce Keep card title mouse selection native
Exclude inline title editors and their controls from card sorting and window dragging so selecting title text with the mouse stays a native editing gesture. Cover the sortable guard, opened-card drag guard, and real browser selection behavior.

Thanks to rmb82 and xet7 !

Fixes #6641,
2026-08-28 07:09:45 +03:00
Lauri Ojansivu 052aa291a4 Keep markdown links clickable in card titles
Remove the fixed-width minicard title overlay that intercepted short rendered links. Ordinary title text remains an accessible inline-edit control, while links keep their viewer click behavior.

Add positive, negative, accessibility, and browser regression coverage.

Thanks to jullbo and xet7 !

Fixes #6639,
2026-08-28 07:02:17 +03:00
Lauri Ojansivu 88246a69c9 Translate Swati lockout and scheduled jobs
Translate 50 remaining Swati placeholders covering accessibility, brute-force lockouts, people filters, scheduled jobs, storage paths, and board maintenance.

Preserve runtime tokens exactly. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:38:47 +03:00
Lauri Ojansivu 3c1768a64f Translate Swati board status and support settings
Translate 50 remaining Swati placeholders covering board status, uploads, custom translations, checklist display, support, and accessibility settings.

Preserve runtime placeholders and ISO, ZIP, JSON, and PDF literals exactly. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:37:55 +03:00
Lauri Ojansivu 7c9d0af15f Translate Swati storage migration controls
Translate 50 remaining Swati placeholders covering attachment migration and repair, storage defaults, file statistics and identifiers, and MongoDB compaction.

Preserve runtime tokens, ellipses, and storage and database literals exactly. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:37:00 +03:00
Lauri Ojansivu 582c806fe7 Translate Swati organization and attachment tools
Translate 50 remaining Swati placeholders covering teams, organizations, invitations, Node memory diagnostics, legal notices, checklists, subtasks, and attachment storage.

Preserve runtime tokens and Node, URL, GridFS, and S3 literals exactly. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:35:59 +03:00
Lauri Ojansivu e924b5cd0f Translate Swati recovery and help tickets
Translate 50 remaining Swati placeholders covering office and API reporting, recovery maintenance, wait spinners, card sizing, deletion warnings, and help tickets.

Preserve runtime tokens, newlines, and REST API, configuration, MongoDB, and mail literals exactly. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:34:47 +03:00
Lauri Ojansivu 28f3d11948 Translate Swati diagnostics and reports
Translate 50 remaining Swati placeholders covering card locations, maps, server diagnostics, sorting, custom-field string templates, and administrative reports.

Preserve runtime tokens, commands, HTML entities, newlines, and technical literals exactly. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:33:54 +03:00
Lauri Ojansivu 298bd10fd1 Translate Swati dependencies and backgrounds
Translate 50 remaining Swati placeholders covering sorting, completion, stickers, card dependencies and import, board backgrounds, and locations.

Preserve runtime tokens, sort arrows, and JSON and SVG literals exactly. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:33:03 +03:00
Lauri Ojansivu 5b79e2e20f Translate Swati advanced search help
Translate 50 remaining Swati placeholders covering search validation, pagination, operator documentation, examples, and search behavior notes.

Preserve runtime placeholders, code examples, newlines, and Markdown delimiters exactly. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:32:08 +03:00
Lauri Ojansivu 7f091b31f8 Translate Swati advanced search terms
Translate 50 remaining Swati placeholders covering result counts, not-found messages, and advanced-search operators and predicates.

Preserve percent and underscore-delimited runtime placeholders exactly. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:30:53 +03:00
Lauri Ojansivu 19a92348c6 Translate Swati templates and card views
Translate 50 remaining Swati placeholders covering domains, shared templates, checklist visibility, tasks, My Cards and Due Cards views, global search, and not-found errors.

Preserve percent placeholders and Markdown emphasis exactly. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:29:59 +03:00
Lauri Ojansivu d77d17bace Translate Swati roles and interaction settings
Translate 50 remaining Swati placeholders covering multi-card windows, inline editing, organization and user dialogs, unread controls, board roles, weekdays, and ownership metadata.

Preserve keyboard literals and runtime tokens exactly. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:29:03 +03:00
Lauri Ojansivu 007ec67aad Translate Swati date and interaction controls
Translate 50 remaining Swati placeholders covering authentication, board duplication, date reminders, deletion confirmations, positioning and desktop interaction controls.

Preserve exact HTML tags, runtime placeholders, line breaks, paths, brackets, trailing spaces and identifiers. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:27:08 +03:00
Lauri Ojansivu 1db5f67782 Translate Swati automation and metadata controls
Translate 50 remaining Swati placeholders covering concrete rule actions, authentication, custom head metadata, manifests, asset links and layout controls.

Preserve exact comma-separated examples, trailing whitespace, HTML and JSON labels and assetlinks.json. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:25:43 +03:00
Lauri Ojansivu 482db29295 Translate Swati automation conditions
Translate 50 remaining Swati placeholders covering card, list, archive, label, member, attachment and checklist conditions plus movement, color and email actions.

Preserve exact runtime syntax and key order. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:24:39 +03:00
Lauri Ojansivu 57c3945d44 Translate Swati workflow scheduling
Translate 50 remaining Swati placeholders covering visual workflow imports, scheduled and due-date triggers, buttons, sorting, completion, movement and relative dates.

Preserve exact runtime placeholders, workflow product names, weekday dash and duration marker. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:23:46 +03:00
Lauri Ojansivu 10f9eb9cdf Translate Swati automation rules
Translate 50 remaining Swati placeholders covering activity messages, rule building, triggers, schedules and JSON, CSV and Trello Butler rule transfer.

Preserve exact runtime placeholders, quoted fields and automation format names. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:22:44 +03:00
Lauri Ojansivu 3fbf638e72 Translate Swati dates and card settings
Translate 50 remaining Swati placeholders covering dates, colors, destructive actions, subtask routing, minicard display, parent cards and label activity.

Preserve exact runtime placeholders, quoted labels and checklist counters. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:21:42 +03:00
Lauri Ojansivu 05b2513855 Translate Swati diagnostics and tenancy settings
Translate 50 remaining Swati placeholders covering database and runtime diagnostics, field display, board visibility and organization and team tenancy settings.

Preserve exact product names, runtime identifiers, hostname examples and configuration literals. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:20:45 +03:00
Lauri Ojansivu 75343ddda3 Translate Swati limits and invitation settings
Translate 50 remaining Swati placeholders covering attachment and API limits, registration, SMTP invitations, authorization, webhooks and system metadata.

Preserve exact email placeholders, line breaks, protocols and runtime product names. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:19:32 +03:00
Lauri Ojansivu 37c5426ea3 Translate Swati tracking and branding settings
Translate 50 remaining Swati placeholders covering starred boards, subscriptions, time tracking, uploads, custom branding, welcome templates and WIP errors.

Preserve exact shortcut ranges, URL terminology, numeric defaults and WIP names. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:18:25 +03:00
Lauri Ojansivu bf2afdd01e Translate Swati page and shortcut controls
Translate 50 remaining Swati placeholders covering watched items, public and private pages, member removal, Sandstorm guidance, search, WIP limits, shortcuts and default boards.

Preserve exact runtime placeholders, HTML tags, product names and keyboard literals. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:17:00 +03:00
Lauri Ojansivu 9bf47c0921 Translate Swati membership and movement controls
Translate 50 remaining Swati placeholders covering board departure, archives, settings, imports, movement, multi-selection, notification states and board roles.

Preserve exact runtime placeholders, quoted navigation and import format names. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:16:00 +03:00
Lauri Ojansivu d57db8ec2b Translate Swati Trello import controls
Translate 50 remaining Swati placeholders covering secure ZIP handling, Trello API imports, cancellation, progress, imported-member mapping and validation.

Preserve exact archive extensions, API terminology, URL, year example and runtime placeholders. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:14:56 +03:00
Lauri Ojansivu a2cb3c4266 Translate Swati filters and board imports
Translate 50 remaining Swati placeholders covering advanced filters and board imports from Kanboard, Deck, OpenProject, issue trackers, Asana, ZenKit, Trello, Jira, Excel and WeKan.

Preserve exact operator grammar, regex, JSON fields, API paths, file extensions, product names and runtime placeholders. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:13:50 +03:00
Lauri Ojansivu 52f3eb608d Translate Swati exports and filters
Translate 50 remaining Swati placeholders covering account errors, PDF and Excel card exports, disk-space status, list sorting and date, label and member filters.

Preserve exact export format names and runtime syntax. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:11:52 +03:00
Lauri Ojansivu c59e322f98 Translate Swati email and validation messages
Translate 50 remaining Swati placeholders covering custom fields, permanent deletion, WIP limits, account emails and board, JSON, CSV and linked-card errors.

Preserve exact email tokens, line breaks, quoted names, format literals and trailing whitespace. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:10:48 +03:00
Lauri Ojansivu 9845ef259e Translate Swati roles and custom fields
Translate 50 remaining Swati placeholders covering comment and read-only roles, deletion confirmations, clipboard actions, bulk template cards and custom fields.

Preserve the embedded JSON structure and its title and description keys while translating only example prose. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:09:41 +03:00
Lauri Ojansivu 364961f1e3 Translate Swati settings and colors
Translate 50 remaining Swati placeholders covering starring, automatic widths, card aging, movement, accessible dialog navigation and colors.

Replace four English-identical color placeholders directly with descriptive Swati terms while preserving syntax and numeric tier notation. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:08:35 +03:00
Lauri Ojansivu 45c3c2d747 Translate Swati member and theme settings
Translate 50 remaining Swati placeholders covering member dialogs, restoration, imported-user mapping, themes, fonts, avatars, languages and permissions.

Preserve exact CAS and numeric preview literals. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:07:17 +03:00
Lauri Ojansivu 26b5099bde Translate Swati voting and import dialogs
Translate 50 remaining Swati placeholders covering membership, voting, Planning Poker, dependencies, organization and team dialogs, backgrounds, deletion and imports.

Preserve exact runtime syntax and the Planning Poker feature name. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:06:20 +03:00
Lauri Ojansivu 186391ac51 Translate Swati board views and archives
Translate 50 remaining Swati placeholders covering public boards, navigation, display modes, zoom, calendar controls, archives, deletion guidance and card editing.

Preserve exact runtime placeholders, HTML tags, percent placeholders and zoom notation. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:05:26 +03:00
Lauri Ojansivu 8ae35bf54b Translate Swati board administration
Translate 50 remaining Swati placeholders covering member administration, announcements, archives, attachments, board backgrounds, All Boards display and private-board status.

Preserve exact runtime placeholders, HTML tags and URL terminology. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:04:21 +03:00
Lauri Ojansivu e702c999fb Translate Swati workspace and layout controls
Translate 50 remaining Swati placeholders covering workspace deletion, board selection, home boards, list widths, keyboard shortcuts, swimlane height and checklist controls.

Preserve exact runtime placeholders, administration path and numeric constraints. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:03:27 +03:00
Lauri Ojansivu d39d8d73e0 Translate Swati activities and workspaces
Translate 50 remaining Swati placeholders covering card movement, activity summaries, checklist activity, dates and workspace creation and settings.

Preserve exact runtime placeholders, quoted checklist names and formatting. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:02:18 +03:00
Lauri Ojansivu 8d432437fb Begin Swati activity translations
Translate the first 50 Swati placeholders covering core board, card, list, swimlane, attachment, checklist, comment, member, import and archive activity messages.

Preserve every runtime placeholder exactly. These direct translations are low confidence and welcome review by a Swati speaker.

Thanks to xet7 !
2026-08-28 06:01:13 +03:00
Lauri Ojansivu c93552013d Complete Somali translations
Translate the final 17 Somali placeholders covering event network metadata, filesystem integrity, scoped exports, import sources and numeric search syntax.

Preserve exact runtime placeholders, code spans, metavariables, file extensions, protocol names and product names. The repository detector now reports zero Somali placeholders; these direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 06:00:03 +03:00
Lauri Ojansivu f0c7c3f7a0 Translate Somali accounts and problem reports
Translate 50 remaining Somali placeholders covering repositories, authentication, account errors, problem summaries, card repair, CPU load and event metadata.

Preserve exact runtime placeholders and OTP, API and CPU technical names. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:59:05 +03:00
Lauri Ojansivu 8788a5a403 Translate Somali migration monitoring
Translate 50 remaining Somali placeholders covering migration thresholds, logs, controls, navigation, progress, storage distribution and system resources.

Preserve exact numeric ranges, units and storage technical names. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:58:10 +03:00
Lauri Ojansivu dac27dcd4b Translate Somali repair and monitoring steps
Translate 50 remaining Somali placeholders covering board-repair steps, conversion, cleanup, recurring intervals, storage statistics and job details.

Preserve exact URL, CPU and GridFS technical names. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:57:16 +03:00
Lauri Ojansivu 82c607dca6 Translate Somali board integrity migrations
Translate 50 remaining Somali placeholders covering comprehensive board migrations, lost-card recovery, duplicate and missing-list repair, file URL fixes and progress steps.

Preserve exact data-field identifiers, URL terminology and quoted recovery names. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:56:27 +03:00
Lauri Ojansivu d23c61ef0d Translate Somali attachment migration settings
Translate 50 remaining Somali placeholders covering Google Cloud and GridFS storage, migration controls, S3 authentication and scheduled board operations.

Preserve exact storage products, region, protocol and runtime technical names. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:55:03 +03:00
Lauri Ojansivu b6c218a861 Translate Somali cloud backup settings
Translate 50 remaining Somali placeholders covering backup schedules and restore modes plus Google Cloud, S3 and Azure configuration guidance.

Preserve exact time notation, menu arrows, console labels, formats, identifiers and cloud product names. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:54:13 +03:00
Lauri Ojansivu 65bd1859d8 Translate Somali security and backup controls
Translate 50 remaining Somali placeholders covering Sandstorm cleanup, adaptive card loading, safe rendering, import and export restrictions, anonymization and backups.

Preserve exact HTML, Markdown, environment variables, anonymization examples, archive paths and product names. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:53:02 +03:00
Lauri Ojansivu dd49ca7045 Translate Somali storage and database migrations
Translate 50 remaining Somali placeholders covering scheduled migrations, filesystem, S3 and Azure storage, database migration and Sandstorm status.

Preserve exact runtime placeholders, database URLs, environment variables, commands, versions and filesystem paths. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:51:22 +03:00
Lauri Ojansivu c497ff2b44 Translate Somali account protection and scheduled jobs
Translate 50 remaining Somali placeholders covering accessibility, brute-force protection, account unlocking, scheduled jobs, storage paths and board maintenance.

Preserve exact runtime placeholders and formatting. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:50:14 +03:00
Lauri Ojansivu 5f03c43634 Translate Somali board status and support settings
Translate 50 remaining Somali placeholders covering board timing, uploads, file restrictions, custom translations, checklist display, support and accessibility.

Preserve exact workspace placeholders and file-format and standards literals. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:49:22 +03:00
Lauri Ojansivu 9bcd457ddd Translate Somali attachment storage maintenance
Translate 50 remaining Somali placeholders covering bulk attachment moves, storage repair, avatars, statistics and MongoDB GridFS compaction.

Preserve exact storage, database and runtime technical names. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:48:27 +03:00
Lauri Ojansivu 65fa6e1e52 Translate Somali administration and storage controls
Translate 50 remaining Somali placeholders covering team and organization administration, Node diagnostics, legal notices, checklists and attachment storage.

Preserve exact technical literals and checklist punctuation. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:47:34 +03:00
Lauri Ojansivu 2b0f57b053 Translate Somali recovery and help desk controls
Translate 50 remaining Somali placeholders covering office and API audits, database recovery, wait spinners, organization warnings and help-desk workflows.

Preserve exact API, environment-variable, database and email literals. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:46:34 +03:00
Lauri Ojansivu c005dc40d6 Translate Somali reports and map settings
Translate 50 remaining Somali placeholders covering map detection, server troubleshooting, string templates and administrative reports.

Preserve exact commands, runtime placeholders, HTML entities, line breaks and IP-version literals. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:45:47 +03:00
Lauri Ojansivu d0c594d177 Translate Somali dependencies and board settings
Translate 50 remaining Somali placeholders covering sorting, completion, stickers, card dependencies and imports, board backgrounds and location fields.

Preserve exact runtime placeholders, file-format names and sort arrows. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:44:49 +03:00
Lauri Ojansivu 79e9feff8b Translate Somali search instructions
Translate 50 remaining Somali placeholders covering search predicates, validation, pagination and the complete operator reference and examples.

Preserve exact runtime placeholders, angle-bracket metavariables, code spans, emphasis markers, examples and line breaks. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:43:55 +03:00
Lauri Ojansivu 15615b32a5 Translate Somali search operators
Translate 50 remaining Somali placeholders covering missing-object messages, search-result counts and localized search operators and predicates.

Preserve exact percent and range placeholders and keep search terms free of whitespace. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:42:38 +03:00
Lauri Ojansivu 389e2eb074 Translate Somali templates and card views
Translate 50 remaining Somali placeholders covering tasks, email domains, shared templates, personal and due-card views, global search and missing board-item messages.

Preserve exact percent placeholders, Markdown markers and domain literals. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:41:45 +03:00
Lauri Ojansivu 9e4df692f1 Translate Somali roles and interaction settings
Translate 50 remaining Somali placeholders covering multi-card windows, inline editing, organization and user dialogs, unread controls, board roles, weekdays and status metadata.

Preserve exact keyboard literals and runtime tokens. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:40:40 +03:00
Lauri Ojansivu 36d496ad7e Translate Somali date and interaction controls
Translate 50 remaining Somali placeholders covering authentication, board duplication, date reminders, deletion confirmations, positioning and desktop interaction controls.

Preserve exact HTML tags, runtime placeholders, line breaks, paths and identifiers. These direct translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:39:38 +03:00
Lauri Ojansivu 121c7fef69 Translate Somali rule actions and metadata
Translate 50 remaining English placeholders covering concrete rule actions, authentication, custom head metadata, web manifests, asset links and layout controls.

Preserve exact list separators, trailing-space behavior, date/time notation and HTML, JSON and assetlinks.json identifiers. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:37:27 +03:00
Lauri Ojansivu f64292f8e3 Translate Somali automation conditions
Translate 50 remaining English placeholders covering automation conditions for cards, lists, archives, labels, members, attachments and checklists and their concrete actions.

Keep the established Somali board, card, list, member, label and checklist terminology consistent for assembled rule sentences. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:36:24 +03:00
Lauri Ojansivu 55f1377c0d Translate Somali workflow schedules
Translate 50 remaining English placeholders covering n8n and Node-RED workflows, schedules, due-date triggers, buttons, sorting, completion, bulk movement and relative dates.

Preserve exact runtime placeholders, product names, unit notation, variable N and weekday range punctuation. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:35:26 +03:00
Lauri Ojansivu 1d0892f014 Translate Somali automation rules
Translate 50 remaining English placeholders covering activity labels, rule building, workflow views, card and member triggers, schedules and JSON, CSV and Trello Butler imports and exports.

Preserve exact runtime placeholders, import/export separator and format and product identifiers. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:34:08 +03:00
Lauri Ojansivu 67aad376ea Translate Somali card and subtask settings
Translate 50 remaining English placeholders covering card dates and colors, destructive actions, duplicate-list cleanup, subtask routing, minicard counters, covers, parents and label activity.

Preserve exact board and percent placeholders and checklist counters. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:32:20 +03:00
Lauri Ojansivu c8c9cb237e Translate Somali diagnostics and tenancy
Translate 50 remaining English placeholders covering database and OS diagnostics, reactivity, DDP, custom-field display, accounts and organization and team tenancy.

Preserve exact runtime modes, environment variables, protocols, product names, example hostnames and MULTITENANCY=true. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:31:09 +03:00
Lauri Ojansivu 472e7cad9a Translate Somali limits and webhooks
Translate 50 remaining English placeholders covering attachment and API limits, avatar blocking, registration, SMTP and TLS, invitation emails, webhooks and system versions.

Preserve exact invitation placeholders and line breaks plus API, SMTP, TLS, Webhook, Node and Meteor identifiers. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:29:55 +03:00
Lauri Ojansivu a8cac773ff Translate Somali tracking and welcome boards
Translate 50 remaining English placeholders covering starred boards, subscriptions, time tracking, numeric shortcuts, uploads, custom URLs, welcome templates and WIP errors.

Preserve exact numeric ranges, URL, default height and WIP identifiers while translating the visible welcome milestone into Somali. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:28:22 +03:00
Lauri Ojansivu 98bed403d0 Translate Somali access and shortcuts
Translate 50 remaining English placeholders covering notifications, public and private access, member removal, Sandstorm warnings, card rescue, search, WIP, shortcuts, signup and default boards.

Preserve exact link markup, runtime tokens, separator and Sandstorm, WeKan, Enter, WIP and emoji identifiers. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:26:52 +03:00
Lauri Ojansivu f9ad387711 Translate Somali list and role controls
Translate 50 remaining English placeholders covering last-admin protection, leaving and linking, list archives and imports, settings, multi-selection, muted notifications and normal roles.

Preserve exact board-title placeholder, quoted menu navigation and Trello, Excel, CSV and TSV identifiers. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:25:39 +03:00
Lauri Ojansivu 7cebeae97e Translate Somali Trello imports
Translate 50 remaining English placeholders covering Trello API credentials and imports, resumable jobs, cancellation safeguards, member mapping, validation, shortcuts and labels.

Preserve exact archive extension, Trello app-key URL, API identifier, year example and runtime placeholder. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:24:40 +03:00
Lauri Ojansivu a9566b3a8f Translate Somali filters and board imports
Translate 50 remaining English placeholders covering advanced filters, imported members and Kanboard, Deck, OpenProject, issue, Asana, ZenKit, Trello, CSV, Jira, Excel and WeKan imports.

Preserve exact operators, escapes, regex, JSON fields, API endpoints, file extensions, product names and runtime placeholders. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:23:20 +03:00
Lauri Ojansivu 00c2c57205 Translate Somali exports and filters
Translate 50 remaining English placeholders covering account conflicts, PDF and Excel card export, attachment metadata, disk-space failures, list sorting and date, label and member filters.

Preserve exact PDF and Excel identifiers and the distinction between free and needed storage. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:21:23 +03:00
Lauri Ojansivu fe424c8ce3 Translate Somali emails and import errors
Translate 50 remaining English placeholders covering custom fields, permanent deletion, WIP limits, profile dialogs, account and invitation emails and board, import and linked-card errors.

Preserve exact runtime placeholders, multiline email structure and WIP, JSON, CSV, TSV and WeKan identifiers. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:20:19 +03:00
Lauri Ojansivu cbbb0cb06b Translate Somali roles and custom fields
Translate 50 remaining English placeholders covering the remaining colors, comment and read-only roles, deletion confirmations, clipboard actions, bulk-card JSON and custom-field types.

Keep the JSON example valid with exact title and description field names and preserve the Enter key identifier. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:18:50 +03:00
Lauri Ojansivu 53050829db Translate Somali accessibility and colors
Translate 50 remaining English placeholders covering starred boards and pages, automatic widths, card aging, movement, accessibility, dialogs and color-palette values.

Preserve exact separator and quoted-control delimiters while replacing visible English color names with Somali. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:17:16 +03:00
Lauri Ojansivu 7ea95aa596 Translate Somali member and theme controls
Translate 50 remaining English placeholders covering member dialogs, linked cards, safe imported-user mapping, themes, fonts, avatars, language and permissions.

Preserve the mapping role-escalation safeguard, CAS identifier, import/export separator and numeric font preview. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:16:13 +03:00
Lauri Ojansivu fb9ef836a4 Translate Somali voting and card dialogs
Translate 50 remaining English placeholders covering card membership, voting, Planning Poker, dependencies, organization, team and domain dialogs, backgrounds, account deletion and imports.

Preserve established Somali terminology and exact Planning Poker and domain feature identifiers. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:15:05 +03:00
Lauri Ojansivu bad7926854 Translate Somali board views and archives
Translate 50 remaining English placeholders covering public-board status, board movement, display modes, zoom, alternate views and archive and deletion guidance for cards, lists and swimlanes.

Preserve exact workspace and percent placeholders, HTML tags and zoom range. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:13:49 +03:00
Lauri Ojansivu 110b5cc336 Translate Somali board administration
Translate 50 remaining English placeholders covering administrator permissions and announcements, archives, attachment deletion, backgrounds, All Boards display, members, assignees and privacy.

Preserve exact runtime placeholders, URL and HTML strong tags. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:12:49 +03:00
Lauri Ojansivu 04e57064c0 Translate Somali workspace and layout controls
Translate 50 remaining English placeholders covering workspace deletion, board selection and Home behavior, dates, personal and shared list sizing, keyboard controls, swimlane height and checklist additions.

Preserve exact runtime placeholders, Admin Panel path structure, numeric limit and pixel units. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:11:41 +03:00
Lauri Ojansivu 0ec7d5f18a Translate Somali workspace activities
Translate 50 remaining English placeholders covering card movement and restoration, compact activity phrases for members, checklists, comments and dates and workspace navigation.

Use established Somali terminology while preserving every runtime placeholder and the markdown identifier exactly. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:10:34 +03:00
Lauri Ojansivu 751e68059a Translate Somali activity entries
Translate 50 remaining English placeholders covering title and due-date changes, replies, membership restrictions and detailed activity entries for boards, cards, lists, attachments, labels, checklists and comments.

Use the existing Somali board, card, list, swimlane, attachment and checklist terminology while preserving every runtime placeholder exactly. These direct Somali translations are low confidence and welcome review by a Somali speaker.

Thanks to xet7 !
2026-08-28 05:09:23 +03:00
Lauri Ojansivu b47bdb1503 Complete Samoan translations
Translate the final 17 English placeholders covering event network fields, filesystem integrity, scoped import and export controls and numeric global-search instructions.

Verify all 2,412 Samoan keys have zero English placeholders and exactly preserve English runtime-token and line-break inventories. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 05:07:42 +03:00
Lauri Ojansivu 50e8cf013a Translate Samoan repository and problem reports
Translate 50 remaining English placeholders covering repository accounts, authentication failures, problem reporting, card repair outcomes, CPU details and event fields.

Preserve exact OTP, API, CPU, diagnostic message and runtime placeholder identifiers. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 05:06:56 +03:00
Lauri Ojansivu f08d55483f Translate Samoan migration resource controls
Translate 50 remaining English placeholders covering migration targets, resource thresholds, background guidance, monitoring, navigation, statistics and operation controls.

Preserve exact GridFS, S3, CPU, percentage, millisecond and numeric-range identifiers. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 05:06:02 +03:00
Lauri Ojansivu f0050d3656 Translate Samoan migration monitoring
Translate 50 remaining English placeholders covering board repair steps, one-time conversion, CPU metrics, recurring intervals, migration monitoring, storage statistics and job queues.

Preserve exact ID, URL, CPU and GridFS identifiers. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 05:05:00 +03:00
Lauri Ojansivu d0d88fe56f Translate Samoan board repair migrations
Translate 50 remaining English placeholders covering board integrity checks, duplicate-list cleanup, lost and archived item restoration, URL repairs, confirmations and migration progress.

Preserve exact swimlaneId, listId, URL and ID identifiers and explicit destructive-operation safeguards. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 05:04:07 +03:00
Lauri Ojansivu 0fa38781e3 Translate Samoan attachment migration controls
Translate 50 remaining English placeholders covering GridFS and S3 storage, migration lifecycle controls, scheduled board operations, writable paths and attachment monitoring.

Preserve exact Google Cloud Storage, GridFS, MongoDB, CollectionFS, AWS S3, MinIO, region and SSL/TLS identifiers. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 05:02:48 +03:00
Lauri Ojansivu c43d51e0ba Translate Samoan cloud backup settings
Translate 50 remaining English placeholders covering backup schedules and restore modes, Google Cloud credentials, S3 and Azure console navigation, secrets and connection testing.

Preserve exact schedules, ranges, JSON fields, console labels, arrows, cloud provider names and credential-handling meaning. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 05:01:52 +03:00
Lauri Ojansivu 037af5cbc8 Translate Samoan security and backup controls
Translate 50 remaining English placeholders covering Sandstorm cleanup, card loading, safe rendering, imports, exports, identity privacy, activity and notification restrictions and backups.

Preserve exact HTML and Markdown examples, environment variables, identity syntax, product names, cloud services and archive paths. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 05:00:40 +03:00
Lauri Ojansivu c63a42da7c Translate Samoan scheduled jobs and migrations
Translate 50 remaining English placeholders covering scheduled-job recovery, filesystem, S3 and Azure storage settings and MongoDB, FerretDB and Sandstorm migration guidance.

Preserve exact database names, versions, ports, URLs, environment variables, Snap fragments, filesystem paths and runtime placeholders. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 04:58:44 +03:00
Lauri Ojansivu b8649cff2a Translate Samoan lockout and scheduled operations
Translate 50 remaining English placeholders covering accessibility content, brute-force lockout policy and user controls, scheduled jobs, attachment paths and board maintenance scheduling.

Keep the known-versus-unknown user cases, time units, active state and maintenance outcomes explicit. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 04:57:37 +03:00
Lauri Ojansivu 442e3f52b3 Translate Samoan board status and support settings
Translate 50 remaining English placeholders covering board timing and status, uploads, file policies, workspace dragging, custom translations, checklist display, support and accessibility.

Preserve exact runtime placeholders and Mongo, PDF, ISO 8601, ZIP, JSON and Markdown identifiers. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 04:56:39 +03:00
Lauri Ojansivu 49ca665ed4 Translate Samoan storage repair and compaction
Translate 50 remaining English placeholders covering attachment migration and repair, storage statistics, file identifiers and MongoDB compaction controls and safety guidance.

Preserve exact GridFS, S3, MongoDB, Compact, oplog, Meteor and ID identifiers. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 04:55:37 +03:00
Lauri Ojansivu 89f734aa8e Translate Samoan teams and attachment storage
Translate 50 remaining English placeholders covering team and organization invitations, Node memory metrics, legal notices, checklist and subtask actions and attachment storage moves.

Preserve exact Node, URL, GridFS and S3 identifiers. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 04:54:40 +03:00
Lauri Ojansivu 5078625fe9 Translate Samoan recovery and support reports
Translate 50 remaining English placeholders covering office, API and recovery reporting, swimlane copying, spinner styles, card sizing, deletion guards and support tickets.

Preserve exact REST API, WITH_API=true, MongoDB, spinner-style and Cc identifiers. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 04:53:38 +03:00
Lauri Ojansivu b11b6d08e5 Translate Samoan maps and system reports
Translate 50 remaining English placeholders covering map detection, server troubleshooting, movement controls, string templates, system and security reports, impersonation, recovery and login-office data.

Preserve exact shell commands, runtime placeholders, line breaks, HTML entities and CPU/IP identifiers. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 04:52:40 +03:00
Lauri Ojansivu 9a07b250e1 Translate Samoan dependencies and backgrounds
Translate 50 remaining English placeholders covering board and card sorting, completion, stickers, card dependencies and imports, board backgrounds and locations.

Preserve exact runtime placeholders, sort arrows and JSON/SVG identifiers. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 04:51:30 +03:00
Lauri Ojansivu 61d4e168cc Translate Samoan global search help
Translate 50 remaining English placeholders covering search predicates, errors, pagination and detailed operator instructions for boards, people, dates, fields, sorting and limits.

Preserve exact runtime placeholders, code examples, Markdown delimiters, metavariables and line breaks. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 04:50:40 +03:00
Lauri Ojansivu 348ca44334 Translate Samoan global search vocabulary
Translate 50 remaining English placeholders covering not-found and card-count results plus global-search operators and predicates for boards, people, dates, content and status.

Preserve exact percent and pagination placeholders. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 04:49:14 +03:00
Lauri Ojansivu ffcabdfc00 Translate Samoan shared views and domains
Translate 50 remaining English placeholders covering linked-card safeguards, checklist visibility, domain sharing, shared templates, card views, global search and not-found errors.

Preserve exact percent placeholders, Markdown emphasis, example domain and at-sign literals. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 04:48:23 +03:00
Lauri Ojansivu 7f0fca2b89 Translate Samoan roles and card preferences
Translate 50 remaining English placeholders covering card-window and keyboard behavior, notifications, account dialogs, board-role permissions, weekdays, status metadata, voting and linked-card deletion.

Preserve the exact Enter, Shift+Enter and Ctrl/Cmd+Enter shortcut identifiers. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 04:47:10 +03:00
Lauri Ojansivu 8511ba82b4 Translate Samoan date and interaction controls
Translate 50 remaining English placeholders covering member lists, custom HTML placement, authentication, board duplication, date activity, reminders, positioning, deletion confirmations and desktop interactions.

Preserve exact HTML tags, runtime placeholders, line breaks, reminder paths and key identifiers. These direct Samoan translations are low confidence and welcome review by a Samoan speaker.

Thanks to xet7 !
2026-08-28 04:46:11 +03:00
Lauri Ojansivu d778a20298 Translate Samoan rule actions and web metadata
Translate the twenty-third 50-value Samoan batch covering generated rule actions for email, archive, labels, members, checklists, cards and dates plus authentication, custom head metadata, web manifests, asset links, and layout controls. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact list syntax and HTML, JSON, and assetlinks.json identifiers.

Thanks to xet7 !
2026-08-28 04:43:32 +03:00
Lauri Ojansivu 7029686a3d Translate Samoan automation conditions
Translate the twenty-second 50-value Samoan batch covering automation conditions for cards, labels, members, attachments and checklists plus move, archive, color, member, checklist, and email actions. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve all runtime-placeholder inventories.

Thanks to xet7 !
2026-08-28 04:42:17 +03:00
Lauri Ojansivu 4f92bcb0ad Translate Samoan workflow scheduling
Translate the twenty-first 50-value Samoan batch covering n8n and Node-RED workflow imports, scheduled and due-date triggers, card and board buttons, list sorting, completion actions, and relative dates. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact runtime placeholders and workflow identifiers.

Thanks to xet7 !
2026-08-28 04:40:37 +03:00
Lauri Ojansivu 0a1990aa3c Translate Samoan automation workflows
Translate the twentieth 50-value Samoan batch covering label, attachment and custom-field activity, visual rule workflows, triggers, actions, received-date scheduling, and JSON, CSV and Trello Butler rule import and export. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact runtime placeholders and workflow formats.

Thanks to xet7 !
2026-08-28 04:39:24 +03:00
Lauri Ojansivu a13dc5c41e Translate Samoan card dates and settings
Translate the nineteenth 50-value Samoan batch covering received and end dates, card and selection colors, destructive board and notification actions, subtask and card settings, minicard metadata, parent paths, and label activity. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact runtime placeholders and checklist counters.

Thanks to xet7 !
2026-08-28 04:38:11 +03:00
Lauri Ojansivu 7542f572e5 Translate Samoan diagnostics and tenancy
Translate the eighteenth 50-value Samoan batch covering database and operating-system diagnostics, reactivity configuration, custom-field display, account changes, board visibility, and organization and team tenancy controls. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact product names, modes, environment variables, protocols, hostnames, and multitenancy configuration.

Thanks to xet7 !
2026-08-28 04:36:59 +03:00
Lauri Ojansivu 59a3f0a10f Translate Samoan transfer and server settings
Translate the seventeenth 50-value Samoan batch covering attachment and API limits, registration and invitations, SMTP configuration and test email, Webhooks, and server component labels. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact email placeholders and layout, protocols, product names, and server identifiers.

Thanks to xet7 !
2026-08-28 04:35:55 +03:00
Lauri Ojansivu 4d19440c20 Translate Samoan uploads and templates
Translate the sixteenth 50-value Samoan batch covering starred boards, time tracking, assignee and label shortcuts, uploads, custom logos and URL schemes, welcome and template boards, archive warnings, and WIP-limit errors. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact numeric settings and URL and WIP identifiers.

Thanks to xet7 !
2026-08-28 04:34:29 +03:00
Lauri Ojansivu af8fd3d14f Translate Samoan public pages and shortcuts
Translate the fifteenth 50-value Samoan batch covering watch notifications, private and public page guidance, previews, member removal, description recovery, search, WIP, keyboard shortcuts, and default-board controls. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact HTML, member placeholders, product names, and feature identifiers.

Thanks to xet7 !
2026-08-28 04:33:22 +03:00
Lauri Ojansivu 26a433eea1 Translate Samoan board roles and selection
Translate the fourteenth 50-value Samoan batch covering last-admin protection, leaving boards, list and archive actions, settings dialogs, imports, multi-selection, board roles, and notification preferences. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact board-title placeholders and import identifiers.

Thanks to xet7 !
2026-08-28 04:32:06 +03:00
Lauri Ojansivu e061a974d0 Translate Samoan Trello import controls
Translate the thirteenth 50-value Samoan batch covering Trello API imports and job controls, member mapping, validation, keyboard shortcuts, and label management. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact runtime placeholders, URL, API terms, file extensions, and year example.

Thanks to xet7 !
2026-08-28 04:31:04 +03:00
Lauri Ojansivu 9885341f58 Translate Samoan filters and board imports
Translate the twelfth 50-value Samoan batch covering assignee and custom-field filters, advanced-filter syntax, activity display, and imports from kanban, issue-tracker, spreadsheet, and Trello sources. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact operators, regex and escape examples, API paths, runtime tokens, product names, and file formats.

Thanks to xet7 !
2026-08-28 04:29:51 +03:00
Lauri Ojansivu aed178df45 Translate Samoan export and filter controls
Translate the eleventh 50-value Samoan batch covering account-conflict errors, card export to PDF and Excel, attachment metadata, list sorting, and date, label, and member filters. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact runtime placeholders and export-format identifiers.

Thanks to xet7 !
2026-08-28 04:27:41 +03:00
Lauri Ojansivu 60e5e01987 Translate Samoan emails and permission errors
Translate the tenth 50-value Samoan batch covering deletion policy, WIP settings, profile and date dialogs, account and invitation emails, and import and permission errors. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact email placeholders, line-break structure, and WIP, JSON, CSV, TSV, and WeKan identifiers.

Thanks to xet7 !
2026-08-28 04:26:40 +03:00
Lauri Ojansivu c7379ede64 Translate Samoan roles and custom fields
Translate the ninth 50-value Samoan batch covering board roles, deletion confirmations, clipboard and link actions, bulk template-copy JSON, and custom fields. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve runtime placeholders and the embedded JSON property structure.

Thanks to xet7 !
2026-08-28 04:25:24 +03:00
Lauri Ojansivu 9e33fcfee7 Translate Samoan card aging and colors
Translate the eighth 50-value Samoan batch covering starring, automatic list widths, card aging, navigation and accessibility controls, and localized color names. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve all runtime-placeholder inventories.

Thanks to xet7 !
2026-08-28 04:24:15 +03:00
Lauri Ojansivu 965c4090d0 Translate Samoan member and appearance controls
Translate the seventh 50-value Samoan batch covering member mapping, linked items, themes, fonts, avatars, permissions, and assorted popup titles. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact authentication and font-preview literals.

Thanks to xet7 !
2026-08-28 04:23:08 +03:00
Lauri Ojansivu e29307489b Translate Samoan voting and import dialogs
Translate the sixth 50-value Samoan batch covering voting and Planning Poker, dependencies, organization, team and domain controls, backgrounds, and scoped import and export dialogs. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact feature and runtime identifiers.

Thanks to xet7 !
2026-08-28 04:22:07 +03:00
Lauri Ojansivu d2b2c4abc7 Translate Samoan board views and archives
Translate the fifth 50-value Samoan batch covering public-board and workspace navigation, appearance and responsive views, zoom, calendars, archive warnings, and card metadata. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact runtime tokens, HTML markup, quoted examples, and zoom ranges.

Thanks to xet7 !
2026-08-28 04:21:02 +03:00
Lauri Ojansivu 54cb2c1639 Translate Samoan administration and archives
Translate the fourth 50-value Samoan batch covering administration, announcements, offline recovery, archives, templates, attachments, backgrounds, All Boards display, and member and assignee summaries. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact counters, size placeholders, URL, and HTML markup.

Thanks to xet7 !
2026-08-28 04:19:56 +03:00
Lauri Ojansivu 294f45dd99 Translate Samoan workspace and layout controls
Translate the third 50-value Samoan batch covering workspace selection, Home-board behavior, list widths, swimlane height, keyboard shortcuts, and checklist and card actions. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve exact runtime placeholders, units, and numeric UI constraints.

Thanks to xet7 !
2026-08-28 04:18:53 +03:00
Lauri Ojansivu 2dbded1418 Translate Samoan activities and workspaces
Translate the second 50-value Samoan batch completing core activity sentences and adding All Boards workspace creation, editing, and navigation controls. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve every runtime placeholder and the markdown format identifier.

Thanks to xet7 !
2026-08-28 04:17:47 +03:00
Lauri Ojansivu 476e02a82a Translate Samoan activity history
Translate the first 50-value Samoan batch covering activity history for board, card, list, swimlane, attachment, label, checklist, comment, custom-field, and member changes. These direct translations are low confidence and welcome review by a Samoan speaker. Preserve every percent and underscore-delimited runtime token exactly, including case-sensitive source spellings.

Thanks to xet7 !
2026-08-28 04:16:37 +03:00
Lauri Ojansivu 07862e54e8 Complete Sinhala translations
Translate the final 17 Sinhala values covering event addresses, filesystem integrity, scoped import and export guidance, and the Global Search card-number operator. These direct translations are low confidence and welcome review by a Sinhala speaker. Verify all 2,412 Sinhala keys retain English key order and exact runtime-placeholder inventories, with no genuine placeholders remaining.

Thanks to xet7 !
2026-08-28 04:15:00 +03:00
Lauri Ojansivu c4665db6a3 Translate Sinhala repository and problem reports
Translate the forty-third 50-value Sinhala batch covering repository management, authentication errors, Problems status, card repair, and event-report fields. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact OTP, API, CPU, and repair-counter identifiers.

Thanks to xet7 !
2026-08-28 04:13:38 +03:00
Lauri Ojansivu 709e03e4a2 Translate Sinhala migration monitoring
Translate the forty-second 50-value Sinhala batch covering attachment-migration tuning, monitoring, scheduling, and system-resource status. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact storage identifiers, CPU notation, units, and numeric configuration ranges.

Thanks to xet7 !
2026-08-28 04:11:51 +03:00
Lauri Ojansivu feb2100851 Translate Sinhala migration job status
Translate the forty-first 50-value Sinhala batch completing board-migration steps and adding cleanup, conversion, CPU, scheduling, filesystem, GridFS, and job-queue labels. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact generated labels, schema IDs, URLs, CPU, and GridFS identifiers.

Thanks to xet7 !
2026-08-28 04:10:07 +03:00
Lauri Ojansivu c267223cce Translate Sinhala board integrity migrations
Translate the fortieth 50-value Sinhala batch covering board-integrity migrations for lists, lost cards, archives, avatar and attachment URLs, and detailed progress steps. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact schema fields, generated labels, URL, and ID identifiers.

Thanks to xet7 !
2026-08-28 04:08:22 +03:00
Lauri Ojansivu d5dac17c16 Translate Sinhala migration administration
Translate the thirty-ninth 50-value Sinhala batch covering GridFS and S3 configuration, migration lifecycle controls, and scheduled board operations. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact storage systems, regions, and transport-security identifiers.

Thanks to xet7 !
2026-08-28 04:05:14 +03:00
Lauri Ojansivu 939786336a Translate Sinhala cloud backup settings
Translate the thirty-eighth 50-value Sinhala batch covering scheduled backup restoration and setup guidance for GCS, S3-compatible providers, and Azure storage. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact time formats, JSON fields, provider names, credential labels, and file extensions.

Thanks to xet7 !
2026-08-28 04:03:33 +03:00
Lauri Ojansivu 9690d7016c Translate Sinhala privacy and backup controls
Translate the thirty-seventh 50-value Sinhala batch covering Sandstorm cleanup, adaptive card loading, safe text rendering, privacy controls for imports and exports, and backup creation. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact markup examples, environment variables, anonymization patterns, providers, and backup paths.

Thanks to xet7 !
2026-08-28 04:01:09 +03:00
Lauri Ojansivu a99d5cc036 Translate Sinhala storage and database migration
Translate the thirty-sixth 50-value Sinhala batch covering scheduled-job recovery, filesystem, S3 and Azure storage, database migration, and Sandstorm migration status. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact runtime tokens, URLs, ports, environment variables, commands, paths, and product identifiers.

Thanks to xet7 !
2026-08-28 03:56:16 +03:00
Lauri Ojansivu 512f96e8a6 Translate Sinhala lockout and scheduled operations
Translate the thirty-fifth 50-value Sinhala batch covering accessibility content, brute-force lockout administration, scheduled jobs, attachment paths, and scheduled board operations. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve all runtime placeholder inventories.

Thanks to xet7 !
2026-08-28 03:54:02 +03:00
Lauri Ojansivu 267a8e196e Translate Sinhala board status and support controls
Translate the thirty-fourth 50-value Sinhala batch covering board status, uploads, account prompts, custom translations, checklist display, support, and accessibility controls. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact workspace tokens and PDF, ISO 8601, Markdown, ZIP, and JSON identifiers.

Thanks to xet7 !
2026-08-28 03:50:50 +03:00
Lauri Ojansivu 50bd8d87f0 Translate Sinhala attachment storage maintenance
Translate the thirty-third 50-value Sinhala batch covering attachment-storage migration, location repair, storage statistics, and MongoDB compaction controls. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact GridFS, S3, MongoDB, Compact, Meteor, oplog, and ID identifiers.

Thanks to xet7 !
2026-08-28 03:49:09 +03:00
Lauri Ojansivu 18628e8b77 Translate Sinhala organization and storage controls
Translate the thirty-second 50-value Sinhala batch covering team and organization controls, Node memory diagnostics, legal notices, checklist actions, and attachment-storage moves. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact Node, RSS, URL, GridFS, and S3 identifiers.

Thanks to xet7 !
2026-08-28 03:47:20 +03:00
Lauri Ojansivu a9606bd940 Translate Sinhala recovery and support reports
Translate the thirty-first 50-value Sinhala batch covering office, API and recovery reports, swimlane copying, loading animations, organization safeguards, and support-ticket fields. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact API configuration, database, mail-header, and animation identifiers.

Thanks to xet7 !
2026-08-28 03:45:24 +03:00
Lauri Ojansivu c2ed3a756e Translate Sinhala map and report controls
Translate the thirtieth 50-value Sinhala batch covering map detection, server troubleshooting, board activity controls, custom-field string templates, and Admin Panel reports. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact shell commands, format placeholders, HTML entities, and network address terms.

Thanks to xet7 !
2026-08-28 03:43:21 +03:00
Lauri Ojansivu d964793b24 Translate Sinhala dependency and location controls
Translate the twenty-ninth 50-value Sinhala batch covering sorting, stickers, card dependency relations and imports, board backgrounds, and location fields. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact import counters and the background-size runtime token.

Thanks to xet7 !
2026-08-28 03:41:40 +03:00
Lauri Ojansivu b7f72ae8ce Translate Sinhala global search help
Translate the twenty-eighth 50-value Sinhala batch covering the Global Search operator reference, validation messages, paging, and query-combination notes. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact search examples, operators, and underscore-delimited runtime tokens.

Thanks to xet7 !
2026-08-28 03:40:06 +03:00
Lauri Ojansivu 5cb015435e Translate Sinhala global search terms
Translate the twenty-seventh 50-value Sinhala batch covering missing-result messages and Global Search operators and predicates for boards, people, dates, content, and status. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact printf and underscore-delimited result placeholders.

Thanks to xet7 !
2026-08-28 03:35:44 +03:00
Lauri Ojansivu 0edee63a54 Translate Sinhala shared views and domains
Translate the twenty-sixth 50-value Sinhala batch covering linked-card safety, checklists, tasks, domains, shared templates, My Cards, Due Cards, Global Search, and missing items. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact Markdown emphasis, domain example, at-sign rule, UI scope names, printf placeholders, and runtime inventory.

Thanks to xet7 !
2026-08-28 03:34:29 +03:00
Lauri Ojansivu 94580fe27d Translate Sinhala roles and editor behavior
Translate the twenty-fifth 50-value Sinhala batch covering multi-card windows, editor keys, dialogs, notification filters, board roles, weekdays, and linked-card deletion. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact Enter, Shift+Enter, Ctrl/Cmd+Enter, Admin Panel, global admins, and Read-only interface literals.

Thanks to xet7 !
2026-08-28 03:32:35 +03:00
Lauri Ojansivu 9bea49de06 Translate Sinhala dates and interface controls
Translate the twenty-fourth 50-value Sinhala batch covering member lists, custom HTML, authentication, duplication, deletion, date activity and reminders, positioning, and desktop controls. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact body markup, date and reminder placeholders, line structure, OIDC, HTML, and Enter literals.

Thanks to xet7 !
2026-08-28 03:30:51 +03:00
Lauri Ojansivu cbb09b8d65 Translate Sinhala automation and metadata
Translate the twenty-third 50-value Sinhala batch covering generated automation actions, authentication, custom HTML metadata, web manifests, asset links, and layout controls. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact item-list syntax, HTML and JSON names, tag terminology, web manifest, assetlinks.json, and runtime placeholders.

Thanks to xet7 !
2026-08-28 03:28:49 +03:00
Lauri Ojansivu b31b7b3292 Translate Sinhala rule conditions
Translate the twenty-second 50-value Sinhala batch covering card, label, member, attachment, and checklist conditions plus move, archive, color, member, checklist, and email actions. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve the exact runtime placeholder inventory.

Thanks to xet7 !
2026-08-28 03:27:09 +03:00
Lauri Ojansivu 4330938782 Translate Sinhala scheduled automation
Translate the twenty-first 50-value Sinhala batch covering workflow imports, schedules, due-date triggers, automation buttons, sorting, completion actions, and relative dates. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact n8n, Node-RED, WeKan, weekday range, N variable, and runtime placeholders.

Thanks to xet7 !
2026-08-28 03:25:48 +03:00
Lauri Ojansivu ffbb09d16a Translate Sinhala rule workflows
Translate the twentieth 50-value Sinhala batch covering activity history, visual rule workflows, triggers, actions, scheduling, and JSON, CSV, and Trello Butler transfer. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact printf and underscore-delimited placeholders, argument roles, and format and product literals.

Thanks to xet7 !
2026-08-28 03:24:22 +03:00
Lauri Ojansivu f589a0acf4 Translate Sinhala card settings and dates
Translate the nineteenth 50-value Sinhala batch covering card dates and colors, destructive actions, subtasks, minicard metadata, parent paths, and label activity. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact runtime placeholder inventory, argument semantics, and 0/0 checklist counters.

Thanks to xet7 !
2026-08-28 03:22:50 +03:00
Lauri Ojansivu e9ba935e11 Translate Sinhala diagnostics and tenancy
Translate the eighteenth 50-value Sinhala batch covering database and OS diagnostics, reactivity, custom fields, account controls, board visibility, and organization and team tenancy. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact backend modes, environment variables, host examples, product names, and acronyms.

Thanks to xet7 !
2026-08-28 03:21:02 +03:00
Lauri Ojansivu ae23418582 Translate Sinhala transfer and SMTP settings
Translate the seventeenth 50-value Sinhala batch covering attachment and API limits, registration, invitations, SMTP, test email, Webhooks, and server components. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact email placeholders and line layout plus API, SMTP, TLS, kanban, Webhook, Node, and Meteor literals.

Thanks to xet7 !
2026-08-28 03:18:51 +03:00
Lauri Ojansivu 98ee5133cd Translate Sinhala tracking and branding
Translate the sixteenth 50-value Sinhala batch covering starred boards, time tracking, shortcuts, uploads, custom branding and URL schemes, welcome templates, and WIP errors. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact shortcut ranges, URL Scheme terminology, logo-height default, WIP, and runtime placeholder inventory.

Thanks to xet7 !
2026-08-28 03:16:55 +03:00
Lauri Ojansivu d6db4d7ced Translate Sinhala watch and shortcuts
Translate the fifteenth 50-value Sinhala batch covering watch notifications, page visibility, previews, member removal, description recovery, search, WIP limits, shortcuts, and default boards. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact anchor markup, runtime placeholders, and Google, WeKan, Sandstorm, Share access, WIP, and Enter literals.

Thanks to xet7 !
2026-08-28 03:15:06 +03:00
Lauri Ojansivu e7f7c891c8 Translate Sinhala roles and list actions
Translate the fourteenth 50-value Sinhala batch covering last-admin protection, board leaving, archives, list and swimlane actions, multi-selection, roles, and notifications. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact board-title placeholders, Menu and Archive navigation, Trello, and Excel CSV/TSV literals.

Thanks to xet7 !
2026-08-28 03:12:57 +03:00
Lauri Ojansivu 1e2f4a0dc2 Translate Sinhala Trello imports
Translate the thirteenth 50-value Sinhala batch covering safe Trello archive and API imports, workspace placement, cancellation, member mapping, validation, invitations, and labels. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact archive extensions, API terminology, key URL, year example, and runtime placeholders.

Thanks to xet7 !
2026-08-28 03:11:08 +03:00
Lauri Ojansivu 00cf39732b Translate Sinhala filters and imports
Translate the twelfth 50-value Sinhala batch covering advanced filters, member status, and multi-format board import guidance. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact operators, escaped examples, API paths, JSON properties, archive extensions, product names, and runtime placeholders.

Thanks to xet7 !
2026-08-28 03:09:19 +03:00
Lauri Ojansivu f48a7ff9a4 Translate Sinhala exports and filters
Translate the eleventh 50-value Sinhala batch covering account errors, PDF and Excel card export, attachment metadata, list sorting, and card filters. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact PDF and Excel product-format names and runtime placeholder inventory.

Thanks to xet7 !
2026-08-28 03:06:53 +03:00
Lauri Ojansivu 553a3b3c3c Translate Sinhala emails and errors
Translate the tenth 50-value Sinhala batch covering custom fields, deletion settings, WIP limits, profile dialogs, account emails, and import and permission errors. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact email placeholders and line layout plus Global Admin, WIP, JSON, CSV, TSV, and WeKan literals.

Thanks to xet7 !
2026-08-28 03:05:16 +03:00
Lauri Ojansivu b4916cd7ad Translate Sinhala roles and custom fields
Translate the ninth 50-value Sinhala batch covering restricted roles, deletion confirmations, clipboard actions, bulk card copying, and custom-field types. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact JSON syntax and title and description property names in the translated bulk-copy example.

Thanks to xet7 !
2026-08-28 03:03:08 +03:00
Lauri Ojansivu f17d0b932f Translate Sinhala navigation and colors
Translate the eighth 50-value Sinhala batch covering starring, automatic widths, card aging, movement, navigation, dialogs, and color names. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve the quoted Archive interface label, numeric tier count, and exact runtime placeholder inventory.

Thanks to xet7 !
2026-08-28 03:01:19 +03:00
Lauri Ojansivu 4ad2600105 Translate Sinhala identity and themes
Translate the seventh 50-value Sinhala batch covering member dialogs, identity mapping, themes, fonts, colors, avatars, language, and permissions. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact CAS and numeric font-preview literals and the runtime placeholder inventory.

Thanks to xet7 !
2026-08-28 03:00:01 +03:00
Lauri Ojansivu a881922f8e Translate Sinhala voting and imports
Translate the sixth 50-value Sinhala batch covering card membership, voting, Planning Poker, dependencies, organizations, teams, dialogs, and board-item transfer. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve the Planning Poker product term and exact placeholder inventory.

Thanks to xet7 !
2026-08-28 02:58:28 +03:00
Lauri Ojansivu 03b3bd993f Translate Sinhala board views and archives
Translate the fifth 50-value Sinhala batch covering public-board status, views and zoom, calendar navigation, archive guidance, and card metadata controls. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact workspace and printf placeholders, strong-element markup, zoom range, and Bucket List example.

Thanks to xet7 !
2026-08-28 02:57:09 +03:00
Lauri Ojansivu dd5f8cd984 Translate Sinhala board administration
Translate the fourth 50-value Sinhala batch covering announcements, archives, templates, attachments, backgrounds, member views, and private-board status. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact runtime placeholders, URL terminology, and strong-element markup.

Thanks to xet7 !
2026-08-28 02:55:32 +03:00
Lauri Ojansivu ad198106e7 Translate Sinhala workspace controls
Translate the third 50-value Sinhala batch covering workspace deletion, multi-board selection, Home-board actions, list and swimlane sizing, keyboard shortcuts, and checklist creation. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact printf placeholders and Admin Panel navigation labels.

Thanks to xet7 !
2026-08-28 02:53:58 +03:00
Lauri Ojansivu 1bd81b6c90 Translate Sinhala activities and workspaces
Translate the second 50-value Sinhala batch covering card movement and restoration, generic activity messages, checklist history, and workspace controls. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve the exact printf and underscore-delimited runtime placeholder inventory while using natural Sinhala word order.

Thanks to xet7 !
2026-08-28 02:52:02 +03:00
Lauri Ojansivu 59153b9b4e Translate Sinhala board activities
Translate the first 50-value Sinhala batch covering board activity messages for cards, attachments, subtasks, labels, checklists, comments, members, archives, and imports. These direct translations are low confidence and welcome review by a Sinhala speaker. Preserve exact printf and underscore-delimited runtime placeholders.

Thanks to xet7 !
2026-08-28 02:49:28 +03:00
Lauri Ojansivu 9b271c7a8a Complete Northern Sami translations
Translate the final 17 Northern Sami values covering event network fields, filesystem integrity, scoped import and export, and the number search operator. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact runtime placeholders, network labels, archive extensions, formats, and product names. Northern Sami now has no English placeholders remaining.

Thanks to xet7 !
2026-08-28 02:46:48 +03:00
Lauri Ojansivu 5ada4bf216 Translate Northern Sami accounts and events
Translate the forty-third 50-value Northern Sami batch covering repositories, authentication, problem reports, broken-card repair, CPU status, and event logs. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact runtime placeholders and OTP, API, quoted status, and CPU literals.

Thanks to xet7 !
2026-08-28 02:45:51 +03:00
Lauri Ojansivu 7d3592f20b Translate Northern Sami migration metrics
Translate the forty-second 50-value Northern Sami batch covering migration throttling, monitoring, pagination, system resources, and storage distribution. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact GridFS, S3, CPU, percent, millisecond, and numeric-range literals.

Thanks to xet7 !
2026-08-28 02:44:47 +03:00
Lauri Ojansivu 6554cab841 Translate Northern Sami migration monitoring
Translate the forty-first 50-value Northern Sami batch covering board-repair steps, conversion progress, migration schedules, resource monitoring, and job queues. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact recovery-name, ID, URL, CPU, and GridFS literals.

Thanks to xet7 !
2026-08-28 02:43:51 +03:00
Lauri Ojansivu 666591481d Translate Northern Sami board repairs
Translate the fortieth 50-value Northern Sami batch covering board-integrity checks, lost and archived item recovery, URL repair, and migration progress. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact swimlaneId, listId, URL, ID, and logical AND literals.

Thanks to xet7 !
2026-08-28 02:42:55 +03:00
Lauri Ojansivu 8d50a747ed Translate Northern Sami migration controls
Translate the thirty-ninth 50-value Northern Sami batch covering attachment and board migrations, GridFS and S3 configuration, and scheduled board operations. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact storage backend, region, and transport-security literals.

Thanks to xet7 !
2026-08-28 02:41:26 +03:00
Lauri Ojansivu 215872d0e3 Translate Northern Sami cloud backups
Translate the thirty-eighth 50-value Northern Sami batch covering backup schedules, restore modes, and GCS, AWS S3, and Azure setup guidance. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact time formats, JSON labels, credential fields, product names, and live console navigation paths.

Thanks to xet7 !
2026-08-28 02:40:28 +03:00
Lauri Ojansivu aeb9d6b71a Translate Northern Sami security and backups
Translate the thirty-seventh 50-value Northern Sami batch covering Sandstorm cleanup, card loading, security and privacy switches, anonymized transfer, and backups. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact markup examples, environment variables, counter examples, paths, formats, and product names.

Thanks to xet7 !
2026-08-28 02:38:48 +03:00
Lauri Ojansivu 05b5ea7cad Translate Northern Sami database migration
Translate the thirty-sixth 50-value Northern Sami batch covering scheduled-job recovery, storage configuration, database migration, and Sandstorm migration status. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact runtime placeholders, URLs, environment variables, commands, backend names, and storage paths.

Thanks to xet7 !
2026-08-28 02:37:15 +03:00
Lauri Ojansivu ce7f375d34 Translate Northern Sami account protection
Translate the thirty-fifth 50-value Northern Sami batch covering accessibility content, brute-force login protection, account unlocking, scheduled jobs, storage paths, and board maintenance. These direct translations are low confidence and welcome review by a Northern Sami speaker. Keep lock and unlock actions explicit and preserve the exact placeholder inventory.

Thanks to xet7 !
2026-08-28 02:36:08 +03:00
Lauri Ojansivu 0793c83a0b Translate Northern Sami board status and support
Translate the thirty-fourth 50-value Northern Sami batch covering board status, uploads, file restrictions, custom translations, checklist display, and support and accessibility pages. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact workspace placeholders and PDF, ISO 8601, Markdown, .zip, and JSON literals.

Thanks to xet7 !
2026-08-28 02:35:09 +03:00
Lauri Ojansivu 9e54633189 Translate Northern Sami attachment maintenance
Translate the thirty-third 50-value Northern Sami batch covering attachment moves, storage-location repair, file statistics, and MongoDB Compact administration. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact GridFS, S3, MongoDB, Compact, ID, oplog, and Meteor technical literals.

Thanks to xet7 !
2026-08-28 02:34:11 +03:00
Lauri Ojansivu ae182a3b83 Translate Northern Sami teams and storage
Translate the thirty-second 50-value Northern Sami batch covering teams, organizations, Node memory diagnostics, legal notices, checklist actions, and attachment storage moves. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact Node, malloc, URL, GridFS, and S3 technical literals.

Thanks to xet7 !
2026-08-28 02:33:12 +03:00
Lauri Ojansivu a031e43361 Translate Northern Sami recovery and tickets
Translate the thirty-first 50-value Northern Sami batch covering office and API reports, data recovery, loading indicators, deletion safeguards, and ticket workflow states. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact REST API, WITH_API=true, MongoDB, and Cc technical literals.

Thanks to xet7 !
2026-08-28 02:32:10 +03:00
Lauri Ojansivu 5bb3a9eab7 Translate Northern Sami reports and maps
Translate the thirtieth 50-value Northern Sami batch covering map locations, server troubleshooting, swimlane controls, string templates, and administration reports. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact shell commands, HTML entities, network literals, and runtime placeholders.

Thanks to xet7 !
2026-08-28 02:31:17 +03:00
Lauri Ojansivu cefdaa3bed Translate Northern Sami board metadata
Translate the twenty-ninth 50-value Northern Sami batch covering sorting, stickers, card dependencies and imports, board backgrounds, and location fields. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact JSON and SVG names plus underscore-delimited and template runtime tokens.

Thanks to xet7 !
2026-08-28 02:30:04 +03:00
Lauri Ojansivu 425d4e6676 Translate Northern Sami global search help
Translate the twenty-eighth 50-value Northern Sami batch covering the Global Search operator reference, validation messages, paging, and query-combination notes. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact search examples, printf values, and underscore-delimited runtime tokens.

Thanks to xet7 !
2026-08-28 02:29:14 +03:00
Lauri Ojansivu 54487e8d6b Translate Northern Sami global search terms
Translate the twenty-seventh 50-value Northern Sami batch covering missing-result messages and localized global-search operators and predicates for boards, people, dates, content, and status. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact printf and underscore-delimited runtime placeholders.

Thanks to xet7 !
2026-08-28 02:27:08 +03:00
Lauri Ojansivu 808ee5c6c3 Translate Northern Sami shared views and domains
Translate the twenty-sixth 50-value Northern Sami batch covering linked-card safety, checklist visibility, tasks, domains and shared templates, My Cards and Due Cards views, global search choices, and missing-item messages. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact printf placeholders, markdown emphasis, domain examples, and special characters.

Thanks to xet7 !
2026-08-28 02:26:06 +03:00
Lauri Ojansivu a531a308f8 Translate Northern Sami roles and keyboard behavior
Translate the twenty-fifth 50-value Northern Sami batch covering multi-card windows, editor keyboard behavior, organization, team and user dialogs, notifications, board-role permissions and status, weekdays, and linked-card deletion guidance. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact Enter, Shift+Enter, Ctrl/Cmd+Enter, Admin Panel, and runtime placeholders.

Thanks to xet7 !
2026-08-28 02:25:05 +03:00
Lauri Ojansivu 18e147dcd3 Translate Northern Sami reminders and interaction
Translate the twenty-fourth 50-value Northern Sami batch covering member lists, custom HTML placement, authentication errors, duplication and deletion confirmations, card-date activity and reminders, positioning, and desktop interaction controls. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact markup, line layout, trailing spaces, runtime placeholders, OIDC, and Enter.

Thanks to xet7 !
2026-08-28 02:23:40 +03:00
Lauri Ojansivu a2397f579d Translate Northern Sami rule details and metadata
Translate the twenty-third 50-value Northern Sami batch covering further rule actions for email, archives, labels, cards, members, checklists, swimlanes and dates, plus authentication, product metadata, web manifests, asset links, and layout controls. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact HTML, JSON, tag, manifest, assetlinks.json, runtime placeholders, separators, and source spacing.

Thanks to xet7 !
2026-08-28 02:22:23 +03:00
Lauri Ojansivu 8b63cf3c25 Translate Northern Sami rule actions
Translate the twenty-second 50-value Northern Sami batch covering automation sentence fragments for board, list, card, label, member, attachment and checklist conditions, plus move, restore, color, membership, checklist and email actions. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact runtime placeholders.

Thanks to xet7 !
2026-08-28 02:21:15 +03:00
Lauri Ojansivu a1885ef548 Translate Northern Sami scheduled automation
Translate the twenty-first 50-value Northern Sami batch covering n8n and Node-RED workflow imports, scheduled and button triggers, recurring dates, due-date conditions, card residence, list sorting, completion, and relative-date actions. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact runtime placeholders, product names, and symbolic schedule values.

Thanks to xet7 !
2026-08-28 02:20:29 +03:00
Lauri Ojansivu bb20dcc875 Translate Northern Sami automation workflow
Translate the twentieth 50-value Northern Sami batch covering label and custom-field activity, rule editing and workflow views, card, label, member, checklist and attachment triggers, and JSON, CSV and Trello Butler rule exchange. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact runtime placeholders and rule-exchange product and format names.

Thanks to xet7 !
2026-08-28 02:19:24 +03:00
Lauri Ojansivu b9bf3560ed Translate Northern Sami card dates and subtasks
Translate the nineteenth 50-value Northern Sami batch covering received and end dates, color and assignee metadata, board and notification deletion, subtask and card settings, minicard badges, parent paths, and label activity. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact runtime placeholders and count formats.

Thanks to xet7 !
2026-08-28 02:18:23 +03:00
Lauri Ojansivu f8d783fc52 Translate Northern Sami runtime and tenancy
Translate the eighteenth 50-value Northern Sami batch covering database, FerretDB, reactivity, DDP and OS diagnostics, custom-field display, account visibility, organization tenancy, domains, administrators, and membership synchronization. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact runtime modes, environment variables, acronyms, hostnames, flags, and product names.

Thanks to xet7 !
2026-08-28 02:17:11 +03:00
Lauri Ojansivu fa23ed087d Translate Northern Sami transfer and SMTP settings
Translate the seventeenth 50-value Northern Sami batch covering attachment and API size limits, avatar blocking, registration and SMTP invitations, webhook controls, and runtime package and version labels. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact email layout, runtime placeholders, API, SMTP, TLS, Node, and Meteor literals.

Thanks to xet7 !
2026-08-28 02:16:00 +03:00
Lauri Ojansivu ff7e2680d6 Translate Northern Sami tracking and branding
Translate the sixteenth 50-value Northern Sami batch covering starred boards, time tracking, assignee and label shortcuts, upload status, custom logo and URL branding, welcome-board templates, and WIP validation. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact numeric ranges, default dimensions, URL, WIP, and runtime placeholders.

Thanks to xet7 !
2026-08-28 02:14:45 +03:00
Lauri Ojansivu 331e4e6b5a Translate Northern Sami access and shortcuts
Translate the fifteenth 50-value Northern Sami batch covering watched-board notifications, private-page access, image previews, board visibility, member removal, Sandstorm guidance, search, WIP and keyboard shortcuts, and default-board controls. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact placeholders, HTML anchor markup, product names, external UI labels, Enter, and WIP.

Thanks to xet7 !
2026-08-28 02:13:43 +03:00
Lauri Ojansivu 808699b216 Translate Northern Sami board roles and selection
Translate the fourteenth 50-value Northern Sami batch covering board departure and archive navigation, user, team and organization settings, list and swimlane actions, bulk selection, board roles, and participation notifications. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact placeholders, product and file-format names, and align archive navigation with translated UI labels.

Thanks to xet7 !
2026-08-28 02:12:27 +03:00
Lauri Ojansivu d8c3f5c6b2 Translate Northern Sami Trello API imports
Translate the thirteenth 50-value Northern Sami batch covering Trello ZIP safety, workspace placement, API imports and cancellation, member mapping, clipboard and keyboard controls, date validation, and labels. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact placeholders, API URL, extensions, product names, and year example.

Thanks to xet7 !
2026-08-28 02:11:30 +03:00
Lauri Ojansivu aaefc03d11 Translate Northern Sami advanced imports
Translate the twelfth 50-value Northern Sami batch covering advanced filters, imported-member status, and Kanboard, NextCloud Deck, OpenProject, Asana, ZenKit, Trello, Jira, Excel, CSV and WeKan imports. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact filter grammar, escapes, regex, JSON fields, endpoints, menu labels, extensions, product names, and runtime placeholders.

Thanks to xet7 !
2026-08-28 02:10:15 +03:00
Lauri Ojansivu 7438834b54 Translate Northern Sami exports and filters
Translate the eleventh 50-value Northern Sami batch covering account conflicts, card PDF and Excel exports, attachment metadata and disk-space errors, list sorting, and date, label and member filters. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact PDF, Excel, and runtime placeholders.

Thanks to xet7 !
2026-08-28 02:08:54 +03:00
Lauri Ojansivu 5388ab5478 Translate Northern Sami emails and validation
Translate the tenth 50-value Northern Sami batch covering permanent deletion, profile and WIP controls, account emails, and board, user, JSON, CSV and linked-card validation. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact email line structure, runtime placeholders, product names, roles, and data formats.

Thanks to xet7 !
2026-08-28 02:07:44 +03:00
Lauri Ojansivu 6f07dfc73a Translate Northern Sami roles and custom fields
Translate the ninth 50-value Northern Sami batch covering comment and read-only roles, deletion confirmations, clipboard links, multi-card JSON templates, labels, and custom-field types and options. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact JSON structure, property names, Enter, and runtime placeholders.

Thanks to xet7 !
2026-08-28 02:06:39 +03:00
Lauri Ojansivu 3df7d06710 Translate Northern Sami card aging and colors
Translate the eighth 50-value Northern Sami batch covering starring, automatic list width, card aging, movement and accessibility controls, dialog navigation, and 23 color names. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact tier counts and runtime placeholders.

Thanks to xet7 !
2026-08-28 02:05:27 +03:00
Lauri Ojansivu 9b7431299f Translate Northern Sami member and theme dialogs
Translate the seventh 50-value Northern Sami batch covering member, sticker, sorting and restoration dialogs, imported-member mapping, themes, fonts, text colors, avatars, language, and permissions. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact CAS and numeric font-preview literals.

Thanks to xet7 !
2026-08-28 02:04:33 +03:00
Lauri Ojansivu 6543386fa2 Translate Northern Sami voting and imports
Translate the sixth 50-value Northern Sami batch covering card membership and custom fields, voting and Planning Poker, card actions and dependencies, organizations, teams, domains, backgrounds, and import and export dialogs. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact Planning Poker and runtime placeholders.

Thanks to xet7 !
2026-08-28 02:03:16 +03:00
Lauri Ojansivu 81694686bc Translate Northern Sami board views
Translate the fifth 50-value Northern Sami batch covering public-board and drag-and-drop guidance, board appearance and views, desktop and mobile modes, zoom and calendar controls, archive warnings, and card metadata editing. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact placeholders, HTML markup, ranges, and example labels.

Thanks to xet7 !
2026-08-28 02:02:25 +03:00
Lauri Ojansivu 9186787390 Translate Northern Sami board administration
Translate the fourth 50-value Northern Sami batch covering administration and announcements, archives and templates, attachment warnings, board appearance, All Boards settings, member and assignee summaries, and private-board status. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact placeholders, URL, and HTML markup.

Thanks to xet7 !
2026-08-28 02:01:16 +03:00
Lauri Ojansivu 5888ae953e Translate Northern Sami workspace controls
Translate the third 50-value Northern Sami batch covering workspace deletion, multi-board and home-board controls, list width and swimlane height, keyboard shortcuts, templates, subtasks, checklists, labels, and members. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact printf placeholders, Admin Panel UI labels, and numeric limits.

Thanks to xet7 !
2026-08-28 02:00:23 +03:00
Lauri Ojansivu 6970d8d2f9 Translate Northern Sami activity summaries
Translate the second 50-value Northern Sami batch covering card moves and restoration, activity summaries for imports, checklists and dates, starred-board views, and workspace creation and settings. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact printf and underscore-delimited runtime placeholders.

Thanks to xet7 !
2026-08-28 01:59:02 +03:00
Lauri Ojansivu 5be19a3f57 Translate Northern Sami activity records
Translate the first 50-value Northern Sami batch covering board, card, list and swimlane creation, deletion, archiving and imports, plus comments, labels, checklists, attachments, and membership restrictions. These direct translations are low confidence and welcome review by a Northern Sami speaker. Preserve exact printf and underscore-delimited runtime placeholders.

Thanks to xet7 !
2026-08-28 01:58:03 +03:00
Lauri Ojansivu 01b33dbd4f Complete Sindhi translations
Translate the final 17 Sindhi values covering network-event metadata, filesystem integrity, scoped export, and WeKan, Trello, Jira, CSV and Excel import guidance. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact placeholders, network terms, product names and file formats. The authoritative fill audit now reports zero Sindhi placeholders.

Thanks to xet7 !
2026-08-28 01:56:34 +03:00
Lauri Ojansivu f9f4e5228f Translate Sindhi authentication and repairs
Translate the forty-third 50-value Sindhi batch covering repository authentication, account creation and lockout messages, problem reporting, broken-card repair, CPU status, and event metadata. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact placeholders, OTP, API, CPU, minimum length, and quoted runtime messages.

Thanks to xet7 !
2026-08-28 01:55:27 +03:00
Lauri Ojansivu fbc03cd554 Translate Sindhi migration resource controls
Translate the forty-second 50-value Sindhi batch covering attachment-migration tuning and monitoring, CPU and memory thresholds, scheduling, progress, storage distribution, and board scanning. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact units, ranges, CPU, GridFS, and S3 literals.

Thanks to xet7 !
2026-08-28 01:54:35 +03:00
Lauri Ojansivu e89c237591 Translate Sindhi migration monitoring
Translate the forty-first 50-value Sindhi batch covering board-migration steps, conversion status, CPU metrics, job schedules and queues, filesystem and GridFS monitoring, and board scans. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact URL, ID, CPU, and GridFS literals.

Thanks to xet7 !
2026-08-28 01:53:30 +03:00
Lauri Ojansivu dd6b16c591 Translate Sindhi board integrity migration
Translate the fortieth 50-value Sindhi batch covering board-integrity checks, duplicate-list cleanup, lost-card and archive restoration, URL repair, confirmations, and migration progress. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact schema fields, generated UI labels, logical operators, IDs, and URL literals.

Thanks to xet7 !
2026-08-28 01:52:43 +03:00
Lauri Ojansivu dc30294534 Translate Sindhi migration and S3 settings
Translate the thirty-ninth 50-value Sindhi batch covering GridFS, S3 and writable-path settings, attachment and board migrations, migration progress, and scheduled board operations. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact provider, storage, region, and transport-security literals.

Thanks to xet7 !
2026-08-28 01:51:26 +03:00
Lauri Ojansivu 854d0fa338 Translate Sindhi cloud backup settings
Translate the thirty-eighth 50-value Sindhi batch covering backup scheduling and restoration, Google Cloud credentials and permissions, AWS S3, Azure and Google Cloud console paths, and cloud connection status. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact time formats, identifiers, provider names, file formats, and external console labels.

Thanks to xet7 !
2026-08-28 01:50:15 +03:00
Lauri Ojansivu 32e7f13a4b Translate Sindhi security and backup controls
Translate the thirty-seventh 50-value Sindhi batch covering Sandstorm cleanup, adaptive card loading, secure rich-text rendering, import and export controls, identity anonymization, activities, notifications, and backup scope. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact product names, configuration variables, markup, identity examples, formats, and storage paths.

Thanks to xet7 !
2026-08-28 01:48:43 +03:00
Lauri Ojansivu 3a8025fabb Translate Sindhi database migration controls
Translate the thirty-sixth 50-value Sindhi batch covering scheduled-job recovery, filesystem and cloud storage, MongoDB-to-FerretDB migration, and Sandstorm migration status. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact placeholders, endpoints, environment variables, commands, paths, versions, and product names.

Thanks to xet7 !
2026-08-28 01:46:32 +03:00
Lauri Ojansivu ecc88d91e8 Translate Sindhi login protection and schedules
Translate the thirty-fifth 50-value Sindhi batch covering accessibility content, brute-force login protection and user unlocking, people filters, scheduled jobs, attachment paths, and scheduled board operations. These direct translations are low confidence and welcome review by a Sindhi speaker.

Thanks to xet7 !
2026-08-28 01:45:26 +03:00
Lauri Ojansivu a7f07fde51 Translate Sindhi board status and support
Translate the thirty-fourth 50-value Sindhi batch covering board time status, upload progress and file policy, custom translations, checklist display, board ZIP imports, support, and accessibility settings. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact workspace placeholders, product names, file formats, and standards.

Thanks to xet7 !
2026-08-28 01:44:19 +03:00
Lauri Ojansivu d9b8d93270 Translate Sindhi attachment storage migration
Translate the thirty-third 50-value Sindhi batch covering attachment and avatar storage migration, location repair, file counts and identifiers, MongoDB Compact guidance, and storage metadata. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact GridFS, S3, MongoDB, Compact, oplog, Meteor, and ID literals.

Thanks to xet7 !
2026-08-28 01:43:24 +03:00
Lauri Ojansivu bfcef763c7 Translate Sindhi teams and storage controls
Translate the thirty-second 50-value Sindhi batch covering team and organization controls, invitations, Node memory metrics, legal notices, checklist actions, and attachment-storage moves. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact Node, URL, GridFS, and S3 literals.

Thanks to xet7 !
2026-08-28 01:42:14 +03:00
Lauri Ojansivu 0c4bba1e46 Translate Sindhi recovery and tickets
Translate the thirty-first 50-value Sindhi batch covering office and REST API reporting, database recovery, swimlane copying, wait-spinner choices, organization and team warnings, tickets, and card-detail controls. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact API configuration, product names, animation names, and mail literals.

Thanks to xet7 !
2026-08-28 01:41:21 +03:00
Lauri Ojansivu 731d3336b1 Translate Sindhi maps and reports
Translate the thirtieth 50-value Sindhi batch covering map-based locations, server-error troubleshooting, custom string templates, board activity controls, and administrative reports. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact shell commands, HTML entities, keyboard labels, network terms, and runtime placeholders.

Thanks to xet7 !
2026-08-28 01:40:16 +03:00
Lauri Ojansivu 26a937ea7b Translate Sindhi dependencies and locations
Translate the twenty-ninth 50-value Sindhi batch covering board and card sorting, completion state, stickers, card dependencies and imports, board backgrounds, and basic location fields. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact JSON, SVG, ordering labels, and runtime placeholders.

Thanks to xet7 !
2026-08-28 01:39:20 +03:00
Lauri Ojansivu 35d7f88f49 Translate Sindhi global search help
Translate the twenty-eighth 50-value Sindhi batch covering the Global Search operator reference, validation messages, paging, and query-combination notes. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact search examples, printf values, and underscore-delimited runtime tokens.

Thanks to xet7 !
2026-08-28 01:38:20 +03:00
Lauri Ojansivu da4bf2e742 Translate Sindhi global search terms
Translate the twenty-seventh 50-value Sindhi batch covering missing-result messages and global-search operators and predicates for boards, people, dates, content, and status. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact printf and underscore-delimited placeholders.

Thanks to xet7 !
2026-08-28 01:36:47 +03:00
Lauri Ojansivu 890993e7ad Translate Sindhi shared views and domains
Translate the twenty-sixth 50-value Sindhi batch covering linked-card safety, checklist visibility, tasks, domains and shared templates, My Cards and Due Cards views, global search choices, and missing-item messages. These direct translations are low confidence and welcome review by a Sindhi speaker.

Thanks to xet7 !
2026-08-28 01:35:46 +03:00
Lauri Ojansivu c9bd6abd89 Translate Sindhi roles and keyboard behavior
Translate the twenty-fifth 50-value Sindhi batch covering multi-card windows, editor keyboard behavior, organization and user dialogs, notifications, board-role permissions, weekdays, and linked-card deletion guidance. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact Enter, Shift+Enter, and Ctrl/Cmd+Enter literals.

Thanks to xet7 !
2026-08-28 01:34:41 +03:00
Lauri Ojansivu 2ecf21ab67 Translate Sindhi card reminders and interaction controls
Translate the twenty-fourth 50-value Sindhi batch covering member lists, injected HTML positions, authentication errors, duplication and deletion, card-date activity, reminders, positioning, and desktop controls. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact HTML tags, OIDC, Enter, runtime placeholders, and line layout.\n\nThanks to xet7 !
2026-08-28 01:32:45 +03:00
Lauri Ojansivu 74442a47c6 Translate Sindhi rule details and product metadata
Translate the twenty-third 50-value Sindhi batch covering rule descriptions, checklist examples, date actions, authentication, custom product metadata, web manifests, asset links, and layout controls. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact HTML, JSON, tag, manifest, and assetlinks.json literals.\n\nThanks to xet7 !
2026-08-28 01:31:41 +03:00
Lauri Ojansivu d83194bc75 Translate Sindhi rule filters and actions
Translate the twenty-second 50-value Sindhi batch covering board, list, label, member, attachment, and checklist rule filters plus archive, movement, color, checklist, and email actions. These direct translations are low confidence and welcome review by a Sindhi speaker.\n\nThanks to xet7 !
2026-08-28 01:30:44 +03:00
Lauri Ojansivu 2f476aa764 Translate Sindhi scheduled rule actions
Translate the twenty-first 50-value Sindhi batch covering visual-workflow imports, schedules, buttons, due-date conditions, sorting, completion, relative dates, and time units. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact n8n, Node-RED, WeKan, weekday-range, count, and time literals.\n\nThanks to xet7 !
2026-08-28 01:29:58 +03:00
Lauri Ojansivu 0f6218ed74 Translate Sindhi automation rule workflow
Translate the twentieth 50-value Sindhi batch covering activity records, rule management, workflow triggers, and JSON, CSV, and Trello Butler interchange. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact placeholders, formats, product names, and runtime argument order.\n\nThanks to xet7 !
2026-08-28 01:29:04 +03:00
Lauri Ojansivu 7ed82c8c2d Translate Sindhi card dates and subtask settings
Translate the nineteenth 50-value Sindhi batch covering card lifecycle dates, colors, destructive confirmations, subtask and minicard settings, parent-card paths, and label activity. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact board and printf placeholders and runtime argument order.\n\nThanks to xet7 !
2026-08-28 01:28:03 +03:00
Lauri Ojansivu 6d33201bf3 Translate Sindhi runtime and tenancy diagnostics
Translate the eighteenth 50-value Sindhi batch covering database and FerretDB diagnostics, reactivity, DDP, OS resources, custom-field display, visibility, tenancy, domains, organization admins, and team templates. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact runtime modes, environment variables, hostnames, and system literals.\n\nThanks to xet7 !
2026-08-28 01:27:05 +03:00
Lauri Ojansivu 3b536a800f Translate Sindhi transfer and webhook settings
Translate the seventeenth 50-value Sindhi batch covering transfer limits, avatars, registration, invitations, SMTP/TLS, authorization, webhooks, and runtime version labels. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact placeholders, email layout, API, SMTP, TLS, Node, and Meteor literals.\n\nThanks to xet7 !
2026-08-28 01:26:10 +03:00
Lauri Ojansivu 7990e10758 Translate Sindhi tracking and branding settings
Translate the sixteenth 50-value Sindhi batch covering starred boards, subscriptions, time tracking, shortcuts, uploads, custom branding URLs, URL schemes, welcome templates, and WIP feedback. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact URL, WIP, shortcut ranges, and numeric defaults.\n\nThanks to xet7 !
2026-08-28 01:24:57 +03:00
Lauri Ojansivu 4831a0098c Translate Sindhi notifications and navigation
Translate the fifteenth 50-value Sindhi batch covering watch notifications, private-page login, previews, visibility, member removal, description rescue, search, WIP limits, shortcuts, sidebars, and default boards. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact HTML, printf and member placeholders, product names, and UI literals.\n\nThanks to xet7 !
2026-08-28 01:23:59 +03:00
Lauri Ojansivu 3ed77bda1e Translate Sindhi board membership and selection controls
Translate the fourteenth 50-value Sindhi batch covering board departure, list archive and movement, settings, multi-selection, muting, roles, archived items, and participation notifications. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact board-title placeholder and import format names.\n\nThanks to xet7 !
2026-08-28 01:22:48 +03:00
Lauri Ojansivu 4af4ca0179 Translate Sindhi Trello import management
Translate the thirteenth 50-value Sindhi batch covering Trello ZIP validation, API credentials, workspaces, resumable jobs, member mapping, date validation, shortcuts, and labels. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact Trello API URL, file extensions, year example, and printf placeholder.\n\nThanks to xet7 !
2026-08-28 01:21:50 +03:00
Lauri Ojansivu 9452ea256a Translate Sindhi advanced filters and board imports
Translate the twelfth 50-value Sindhi batch covering advanced filters, member states, impersonation, and board imports from supported task, issue, spreadsheet, and Kanban formats. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact operators, escape examples, API paths, JSON keys, formats, extensions, product names, and placeholders.\n\nThanks to xet7 !
2026-08-28 01:20:44 +03:00
Lauri Ojansivu 62fed3e9f6 Translate Sindhi card exports and filters
Translate the eleventh 50-value Sindhi batch covering account errors, PDF and Excel card exports, attachment metadata, sorting, and card, date, label, and member filters. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact PDF and Excel literals.\n\nThanks to xet7 !
2026-08-28 01:19:23 +03:00
Lauri Ojansivu 2231a6fc39 Translate Sindhi email and validation settings
Translate the tenth 50-value Sindhi batch covering permanent deletion, WIP limits, profile and date dialogs, account emails, permissions, imports, and linked-card validation. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact placeholders, email line layout, WIP, JSON, CSV, TSV, and WeKan literals.\n\nThanks to xet7 !
2026-08-28 01:18:30 +03:00
Lauri Ojansivu de2545f05a Translate Sindhi roles and custom fields
Translate the ninth 50-value Sindhi batch covering comment and read-only roles, deletion safeguards, clipboard links, bulk-card JSON, labels, and custom-field types and options. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve valid JSON structure and field-name literals.\n\nThanks to xet7 !
2026-08-28 01:17:22 +03:00
Lauri Ojansivu 1c4b95a31a Translate Sindhi card aging and interface colors
Translate the eighth 50-value Sindhi batch covering starring, automatic list widths, card aging, keyboard movement, dialog navigation, board restoration, and interface colors. These direct translations are low confidence and welcome review by a Sindhi speaker.\n\nThanks to xet7 !
2026-08-28 01:16:22 +03:00
Lauri Ojansivu 13aba0fc0d Translate Sindhi member mapping and appearance settings
Translate the seventh 50-value Sindhi batch covering member dialogs, restoration, CAS sign-in, linked items, imported-member mapping, themes, fonts, colors, avatars, language, and permissions. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact CAS and font-preview digits.\n\nThanks to xet7 !
2026-08-28 01:15:26 +03:00
Lauri Ojansivu 151ed508c7 Translate Sindhi voting and import dialogs
Translate the sixth 50-value Sindhi batch covering card membership, voting, Planning Poker, dependencies, organizations, teams, domains, backgrounds, and import dialogs. These direct translations are low confidence and welcome review by a Sindhi speaker.\n\nThanks to xet7 !
2026-08-28 01:14:35 +03:00
Lauri Ojansivu 2e0fcdc519 Translate Sindhi board views and archive guidance
Translate the fifth 50-value Sindhi batch covering public boards, workspace navigation, board appearance and views, zoom, calendars, archive guidance, deletion safeguards, and card controls. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact workspace and printf placeholders, HTML tags, URL, and zoom range.\n\nThanks to xet7 !
2026-08-28 01:13:44 +03:00
Lauri Ojansivu 9b6c9c8e50 Translate Sindhi board administration and archives
Translate the fourth 50-value Sindhi batch covering administrator roles, announcements, offline recovery, archives, templates, attachments, backgrounds, All Boards display, membership, and privacy. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact placeholders, HTML tags, and URL literal.\n\nThanks to xet7 !
2026-08-28 01:12:48 +03:00
Lauri Ojansivu 25c0ca091e Translate Sindhi workspace and layout controls
Translate the third 50-value Sindhi batch covering workspace deletion, board selection, Home boards, list widths, keyboard shortcuts, swimlane heights, and card actions. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact printf argument order and numeric constraints.\n\nThanks to xet7 !
2026-08-28 01:11:50 +03:00
Lauri Ojansivu 37303eeb76 Translate Sindhi activity summaries and workspaces
Translate the second 50-value Sindhi batch covering card movement, membership, restoration, concise activity summaries, checklists, dates, and workspace controls. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact placeholders and unnumbered printf argument order.\n\nThanks to xet7 !
2026-08-28 01:10:41 +03:00
Lauri Ojansivu 14730e5fe8 Translate Sindhi activity records
Translate the first 50-value Sindhi batch covering organization and team membership, comments, due dates, and detailed board, card, attachment, label, checklist, and custom-field activity. These direct translations are low confidence and welcome review by a Sindhi speaker. Preserve exact underscore and printf placeholders.\n\nThanks to xet7 !
2026-08-28 01:09:30 +03:00
Lauri Ojansivu 03db10d9e5 Complete Sakha translations
Translate the final 17 Sakha values covering network event fields, filesystem integrity, scoped export, WeKan import validation, and card-number search. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact IP, IPv4, IPv6, file-format, product, Markdown, and search placeholders. Verify that Sakha has zero remaining English placeholders.\n\nThanks to xet7 !
2026-08-28 01:08:05 +03:00
Lauri Ojansivu ec01bc0b06 Translate Sakha accounts and problem reports
Translate the forty-third 50-value Sakha batch covering OTP login, repositories, account validation, problem and repair reporting, CPU load, and operational events. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact OTP, API, CPU, and repair placeholders.\n\nThanks to xet7 !
2026-08-28 01:07:18 +03:00
Lauri Ojansivu 265e18fa23 Translate Sakha migration tuning and progress
Translate the forty-second 50-value Sakha batch covering attachment migration tuning, background progress, logs, monitoring, scheduling, resource totals, and minicard visibility. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact GridFS, S3, CPU, millisecond, percentage, and numeric-range literals.\n\nThanks to xet7 !
2026-08-28 01:06:26 +03:00
Lauri Ojansivu b8615bd4ce Translate Sakha migration monitoring
Translate the forty-first 50-value Sakha batch covering migration repair steps, board conversion, CPU and filesystem monitoring, recurring intervals, job queues, and concurrency. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact URL, ID, CPU, and GridFS literals.\n\nThanks to xet7 !
2026-08-28 01:05:36 +03:00
Lauri Ojansivu cc3f89d051 Translate Sakha board integrity migrations
Translate the fortieth 50-value Sakha batch covering comprehensive board-integrity migration, duplicate-list cleanup, lost-card and archive restoration, URL repair, confirmations, and progress steps. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact swimlaneId, listId, URL, and ID literals.\n\nThanks to xet7 !
2026-08-28 01:04:45 +03:00
Lauri Ojansivu d973b9619b Translate Sakha attachment migration controls
Translate the thirty-ninth 50-value Sakha batch covering Google Cloud, GridFS and S3 attachment storage, migration lifecycle controls, scheduled board operations, and attachment monitoring. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact storage names, region, protocol, and product literals.\n\nThanks to xet7 !
2026-08-28 01:03:39 +03:00
Lauri Ojansivu 2b58455e80 Translate Sakha cloud backup settings
Translate the thirty-eighth 50-value Sakha batch covering backup schedules, restore modes, Google Cloud credentials, and guided AWS S3, Azure, and GCS storage setup. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact console paths, field labels, formats, product names, and credential literals.\n\nThanks to xet7 !
2026-08-28 01:02:41 +03:00
Lauri Ojansivu 875eec8c44 Translate Sakha security controls and backups
Translate the thirty-seventh 50-value Sakha batch covering Sandstorm cleanup, card loading, safe rich-text rendering, import/export restrictions, user anonymization, activities, notifications, and scoped cloud backups. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact environment variables, markup examples, product names, anonymization tokens, and backup paths.\n\nThanks to xet7 !
2026-08-28 01:01:31 +03:00
Lauri Ojansivu f226707554 Translate Sakha scheduled migrations and storage
Translate the thirty-sixth 50-value Sakha batch covering scheduled-job status, filesystem, S3 and Azure storage, MongoDB/FerretDB migration, and Sandstorm grain migration. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact database URLs, environment variables, commands, paths, versions, and __db__ placeholder.\n\nThanks to xet7 !
2026-08-28 00:59:55 +03:00
Lauri Ojansivu f9c18244e2 Translate Sakha login protection and scheduled operations
Translate the thirty-fifth 50-value Sakha batch covering accessibility content, brute-force protection, locked-user administration, scheduled jobs, attachment paths, and board archive, backup, and cleanup operations. These direct translations are low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:58:55 +03:00
Lauri Ojansivu b719dc325e Translate Sakha board status and support settings
Translate the thirty-fourth 50-value Sakha batch covering board time status, upload limits, PDF guidance, workspace dragging, custom translations, ZIP import, checklist display, support, and accessibility. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact PDF, ISO 8601, Markdown, JSON, .zip, and workspace placeholders.\n\nThanks to xet7 !
2026-08-28 00:57:57 +03:00
Lauri Ojansivu df0232cd7b Translate Sakha attachment migration and repair
Translate the thirty-third 50-value Sakha batch covering bulk attachment and avatar migration, storage-location repair, progress, identifiers, and MongoDB Compact guidance. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact GridFS, S3, MongoDB, Compact, oplog, and ID literals.\n\nThanks to xet7 !
2026-08-28 00:57:06 +03:00
Lauri Ojansivu 966dc3d071 Translate Sakha teams and attachment storage
Translate the thirty-second 50-value Sakha batch covering team and organization assignment, invitations, Node heap diagnostics, legal notices, checklist actions, and attachment moves across filesystem, GridFS, and S3 storage. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact Node, malloc, URL, GridFS, and S3 literals.\n\nThanks to xet7 !
2026-08-28 00:56:11 +03:00
Lauri Ojansivu 321e349bf4 Translate Sakha recovery and service reports
Translate the thirty-first 50-value Sakha batch covering office and REST API reports, database recovery, swimlane copying, wait spinners, deletion safeguards, tickets, requests, and card dialogs. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact REST API, WITH_API=true, MongoDB, Cc, and spinner names.\n\nThanks to xet7 !
2026-08-28 00:55:16 +03:00
Lauri Ojansivu da4d7af4ea Translate Sakha maps and system reports
Translate the thirtieth 50-value Sakha batch covering map coordinates, server diagnostics, activity visibility, string templates, system reports, impersonation, recovery, and office logins. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact shell commands, entities, placeholders, CPU, IPv4, and IPv6 literals.\n\nThanks to xet7 !
2026-08-28 00:54:19 +03:00
Lauri Ojansivu 034a51c92f Translate Sakha dependencies and board backgrounds
Translate the twenty-ninth 50-value Sakha batch covering sorting, completion, stickers, dependency relationships and imports, board backgrounds, and card locations. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact JSON, SVG, arrow, and size placeholders.\n\nThanks to xet7 !
2026-08-28 00:53:26 +03:00
Lauri Ojansivu 36b6abbb94 Translate Sakha global search instructions
Translate the twenty-eighth 50-value Sakha batch covering global-search validation, pagination, query syntax, status, field presence, sorting, limits, and Boolean matching. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact query examples, Markdown delimiters, and runtime placeholders.\n\nThanks to xet7 !
2026-08-28 00:52:30 +03:00
Lauri Ojansivu b770e65408 Translate Sakha global search vocabulary
Translate the twenty-seventh 50-value Sakha batch covering lookup errors, result counts, and global-search operators and predicates for boards, people, dates, status, attachments, descriptions, and checklists. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact printf and pagination placeholders.\n\nThanks to xet7 !
2026-08-28 00:51:19 +03:00
Lauri Ojansivu be22fe0b1e Translate Sakha shared templates and card views
Translate the twenty-sixth 50-value Sakha batch covering checklist visibility, domain-scoped template sharing, My Cards, due cards, global search, and not-found messages. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact example.com, @, Markdown, and printf literals.\n\nThanks to xet7 !
2026-08-28 00:50:36 +03:00
Lauri Ojansivu f2a6f997ad Translate Sakha roles and keyboard behavior
Translate the twenty-fifth 50-value Sakha batch covering multi-card windows, editor keyboard behavior, organization and user dialogs, notifications, board-role permissions, weekdays, and linked-card deletion guidance. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact Enter, Shift+Enter, and Ctrl/Cmd+Enter literals.\n\nThanks to xet7 !
2026-08-28 00:49:35 +03:00
Lauri Ojansivu 563e59c49d Translate Sakha card reminders and interaction controls
Translate the twenty-fourth 50-value Sakha batch covering member lists, injected HTML positions, authentication errors, duplication and deletion, card-date activity, reminders, positioning, and desktop controls. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve exact HTML tags, OIDC, Enter, runtime placeholders, and line breaks.\n\nThanks to xet7 !
2026-08-28 00:48:38 +03:00
Lauri Ojansivu 4a3ee252a6 Translate Sakha rule details and product metadata
Translate the twenty-third 50-value Sakha batch covering rule descriptions, checklist examples, date actions, authentication, custom product metadata, web manifests, asset links, and layout controls. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve HTML, JSON, assetlinks.json, and product-name literals.\n\nThanks to xet7 !
2026-08-28 00:47:43 +03:00
Lauri Ojansivu 4db0a74ea8 Translate Sakha rule filters and actions
Translate the twenty-second 50-value Sakha batch covering board, list, label, member, attachment, and checklist rule filters plus archive, movement, color, checklist, and email actions. These direct translations are low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:46:48 +03:00
Lauri Ojansivu 9822e60afe Translate Sakha scheduled rule actions
Translate the twenty-first 50-value Sakha batch covering visual-workflow imports, schedules, buttons, due-date conditions, sorting, completion, relative dates, and time units. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve the exact __count__ placeholder and workflow product names.\n\nThanks to xet7 !
2026-08-28 00:45:54 +03:00
Lauri Ojansivu 8ec961c704 Translate Sakha automation rule workflow
Translate the twentieth 50-value Sakha batch covering activity records, rule management, workflow triggers, and JSON, CSV, and Trello Butler interchange. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve the exact __time__, __count__, and printf placeholders and their runtime argument order.\n\nThanks to xet7 !
2026-08-28 00:45:08 +03:00
Lauri Ojansivu 2a26e3aafa Translate Sakha card dates and subtask settings
Translate the nineteenth 50-value Sakha batch covering card lifecycle dates, colors, destructive confirmations, subtask and minicard settings, parent-card paths, and label activity. These direct translations are low confidence and welcome review by a Sakha speaker. Preserve the exact __board__ and printf placeholders.\n\nThanks to xet7 !
2026-08-28 00:44:06 +03:00
Lauri Ojansivu 2bca8fa2d4 Translate Sakha runtime and tenancy diagnostics
Translate the next 50 English placeholders in the Sakha locale, covering database, FerretDB, reactivity, DDP, OS resources, fields, visibility, organization tenancy, domains, administrators, and teams. Preserve runtime modes, environment variables, host examples, MULTITENANCY=true, Admin, and all placeholder tokens.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:42:04 +03:00
Lauri Ojansivu b0328ac220 Translate Sakha transfer and webhook settings
Translate the next 50 English placeholders in the Sakha locale, covering attachment and API limits, registration, invitations, SMTP/TLS, authorization, webhooks, and version labels. Preserve multiline email structure, all placeholders, and API, SMTP, TLS, Webhook, Node, and Meteor literals.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:40:49 +03:00
Lauri Ojansivu f94648bfa8 Translate Sakha tracking and branding settings
Translate the next 50 English placeholders in the Sakha locale, covering starred boards, subscriptions, time tracking, shortcuts, uploads, custom branding, URL schemes, welcome templates, and WIP feedback. Preserve URL, numeric range, dimension, and WIP literals and the exact English placeholder inventory.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:39:40 +03:00
Lauri Ojansivu 1e31e32e91 Translate Sakha notifications and navigation
Translate the next 50 English placeholders in the Sakha locale, covering watch notifications, private pages, member removal, Sandstorm access, search, WIP limits, shortcuts, sidebars, signup, and default boards. Preserve HTML, all placeholders, product names, external UI labels, Enter, WIP, and Emoji literals.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:38:30 +03:00
Lauri Ojansivu a69e5bed21 Translate Sakha board membership and selection controls
Translate the next 50 English placeholders in the Sakha locale, covering last-admin protection, leaving boards, archive guidance, role settings, selection actions, muted and normal roles, and notifications. Preserve board-title placeholders, Trello, Excel, CSV/TSV, and navigation structure while using Sakha prose.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:37:12 +03:00
Lauri Ojansivu 8dc6143dee Translate Sakha Trello import management
Translate the next 50 English placeholders in the Sakha locale, covering Trello ZIP safety, API credentials, workspace placement, board selection, cancellation, member mapping, validation, shortcuts, and labels. Preserve URLs, API names, file extensions, numerals, and the exact English placeholder inventory.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:35:55 +03:00
Lauri Ojansivu 39d71c9d77 Translate Sakha advanced filters and board imports
Translate the next 50 English placeholders in the Sakha locale, covering advanced filter syntax, member states, and board imports from supported services and formats. Preserve every operator, escape, regex, JSON field, endpoint, file extension, product name, external menu label, and placeholder.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:34:46 +03:00
Lauri Ojansivu d77b62cda9 Translate Sakha card exports and filters
Translate the next 50 English placeholders in the Sakha locale, covering account errors, PDF and Excel card export, attachment metadata, disk-space feedback, sorting, and card filters. Preserve PDF and Excel literals and the exact English placeholder inventory while using Sakha prose.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:32:55 +03:00
Lauri Ojansivu 6bc5fe2f55 Translate Sakha email and validation settings
Translate the next 50 English placeholders in the Sakha locale, covering profiles, WIP limits, email enrollment and recovery, scrolling, board permissions, import formats, and linked-card validation. Preserve multiline structure, every placeholder, and Global Admin, WIP, JSON, CSV, TSV, and WeKan literals.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:31:50 +03:00
Lauri Ojansivu d3630d2807 Translate Sakha roles and custom fields
Translate the next 50 English placeholders in the Sakha locale, covering interface colors, board roles, deletion confirmations, clipboard actions, multi-card copies, and custom fields. Preserve the JSON title/description schema, Enter literal, and exact English placeholder inventory while translating sample prose to Sakha.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:30:18 +03:00
Lauri Ojansivu f8c7a417ff Translate Sakha card aging and interface colors
Translate the next 50 English placeholders in the Sakha locale, covering settings, subtasks, starring, automatic widths, clipboard actions, card aging, movement, dialogs, and interface colors. Preserve all tier numerals and the exact English placeholder inventory while using Sakha prose and established color terms.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:29:02 +03:00
Lauri Ojansivu 1d263e9738 Translate Sakha member mapping and appearance settings
Translate the next 50 English placeholders in the Sakha locale, covering members, stickers, validation, linked items, imported-member mapping, themes, fonts, colors, avatars, languages, and permissions. Preserve CAS and numeric font-preview literals and every English placeholder token while using Sakha prose.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:27:38 +03:00
Lauri Ojansivu b23ef9fbc6 Translate Sakha voting and import dialogs
Translate the next 50 English placeholders in the Sakha locale, covering members, voting, Planning Poker, dependencies, organizations, teams, backgrounds, domains, mapping, and imports. Preserve the Planning Poker product term and every English placeholder token while using Sakha prose.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:26:29 +03:00
Lauri Ojansivu 9ce473558b Translate Sakha board views and archive guidance
Translate the next 50 English placeholders in the Sakha locale, covering public boards, navigation, appearance, device views, zoom, calendar, archive and deletion guidance, due time, and card editing. Preserve HTML, workspace and format tokens, numeric ranges, and native Sakha prose.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:25:25 +03:00
Lauri Ojansivu c79279c8f3 Translate Sakha administration and board summaries
Translate the next 50 English placeholders in the Sakha locale, covering administration, archive flows, attachments, backgrounds, board summaries, members, assignees, and privacy. Preserve URL, markup, format and underscore tokens, and use native Sakha terminology without Russian seeding.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:24:10 +03:00
Lauri Ojansivu 2cd6a940b7 Translate Sakha workspace and layout controls
Translate the next 50 English placeholders in the Sakha locale, covering workspace deletion, multi-board selection, Home boards, list widths, keyboard shortcuts, swimlane heights, checklists, labels, and members. Preserve embedded Admin Panel navigation, numeric and format literals, and native Sakha terminology without Russian seeding.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:23:00 +03:00
Lauri Ojansivu ad0cad68f1 Translate Sakha activity summaries and workspaces
Translate the next 50 English placeholders in the Sakha locale, covering card movement, activity summaries, checklist events, board states, and workspace controls. Preserve every format and underscore placeholder, the markdown literal, and native Sakha terminology without Russian seeding.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:21:40 +03:00
Lauri Ojansivu f2d8edf93c Translate Sakha activity records
Translate the first 50 English placeholders in the Sakha locale, covering activity changes, membership restrictions, replies, due dates, and activity records for board content. Preserve every format and underscore placeholder and use the locale's established Sakha kanban vocabulary rather than Russian seeding.\n\nThese direct Sakha translations have low confidence and welcome review by a Sakha speaker.\n\nThanks to xet7 !
2026-08-28 00:20:31 +03:00
Lauri Ojansivu 83635be766 Complete Kinyarwanda translations
Translate the final 17 translatable English placeholders in the Kinyarwanda locale, covering event addresses, filesystem integrity, scoped import and export, search-number guidance, and WeKan file imports. Verify zero translatable placeholders remain, with only explicitly invariant technical values still equal to English, no known Kirundi vocabulary, and an exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:18:40 +03:00
Lauri Ojansivu f1ca54cf4e Translate Kinyarwanda account and problem reports
Translate the next 50 English placeholders in the Kinyarwanda locale, covering repositories, OTP login, validation, problem summaries, card repair, CPU usage, and event reporting. Replace an existing Kirundi loading message with Kinyarwanda and preserve the exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:17:06 +03:00
Lauri Ojansivu b6bd786177 Translate Kinyarwanda migration resource monitoring
Translate the next 50 English placeholders in the Kinyarwanda locale, covering bulk storage migration, resource thresholds, monitoring, schedules, progress, and storage statistics. Preserve GridFS, S3, CPU, percentages, timing units, numeric ranges, and the exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:15:56 +03:00
Lauri Ojansivu 7017ee6369 Translate Kinyarwanda migration progress details
Translate the next 50 English placeholders in the Kinyarwanda locale, covering migration steps, conversion progress, scheduled intervals, CPU and filesystem monitoring, GridFS statistics, and job queues. Preserve URL, ID, CPU, GridFS, and the exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:14:53 +03:00
Lauri Ojansivu 6b1203c10e Translate Kinyarwanda board integrity migrations
Translate the next 50 English placeholders in the Kinyarwanda locale, covering comprehensive board migration, duplicate-list cleanup, lost-item restoration, integrity repair, and migration progress. Preserve swimlaneId, listId, URL, ID, and the exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:13:50 +03:00
Lauri Ojansivu 3389ff9196 Translate Kinyarwanda storage migration controls
Translate the next 50 English placeholders in the Kinyarwanda locale, covering GridFS and cloud attachment storage, migration controls, S3 authentication, scheduled board operations, and writable paths. Preserve storage backend names, region values, transport protocols, and the exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:12:37 +03:00
Lauri Ojansivu 75e6a7b20a Translate Kinyarwanda cloud backup settings
Translate the next 50 English placeholders in the Kinyarwanda locale, covering backup schedules, restore modes, cloud credentials, permissions, and provider-console navigation. Preserve time formats, JSON fields, provider names, external UI labels, and the exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:11:30 +03:00
Lauri Ojansivu 5426d3da0d Translate Kinyarwanda security and backup controls
Translate the next 50 English placeholders in the Kinyarwanda locale, covering Sandstorm cleanup, adaptive card loading, plain-text security, import and export controls, anonymization, notifications, and backups. Preserve markup examples, application and provider names, environment settings, identity placeholders, archive paths, and the exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:10:17 +03:00
Lauri Ojansivu d06ecc620b Translate Kinyarwanda database migration settings
Translate the next 50 English placeholders in the Kinyarwanda locale, covering scheduled migrations, storage backends, MongoDB and FerretDB migration, and Sandstorm grain status. Preserve backend names, versions, URLs, environment variables, command fragments, paths, and the exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:08:33 +03:00
Lauri Ojansivu 8cf129b4b3 Translate Kinyarwanda account protection settings
Translate the next 50 English placeholders in the Kinyarwanda locale, covering accessibility, account lockouts, user-status filters, scheduled jobs, attachment paths, and board maintenance operations. Preserve the exact English placeholder inventory and established Kinyarwanda administration terminology.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:07:21 +03:00
Lauri Ojansivu 9fb16ef157 Translate Kinyarwanda board status and support settings
Translate the next 50 English placeholders in the Kinyarwanda locale, covering board status, upload progress and limits, custom translations, checklist display, ZIP import, support, and accessibility. Preserve Mongo, bytes, PDF, ISO 8601, ZIP, JSON, and workspace-token literals and the exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:06:21 +03:00
Lauri Ojansivu d2a5fe3eef Translate Kinyarwanda attachment storage maintenance
Translate the next 50 English placeholders in the Kinyarwanda locale, covering bulk attachment moves, location repair, storage statistics, file identifiers, and MongoDB Compact guidance. Preserve GridFS, S3, MongoDB, Compact, replica, oplog, Meteor, and identifier literals and the exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:05:16 +03:00
Lauri Ojansivu 5f2dbaa635 Translate Kinyarwanda organization and storage views
Translate the next 50 English placeholders in the Kinyarwanda locale, covering team and organization management, Node memory metrics, legal notices, checklists, subtasks, and attachment storage. Preserve Node, RAM, URL, filesystem, GridFS, S3, and malloc literals and the exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:04:05 +03:00
Lauri Ojansivu d4e545fceb Translate Kinyarwanda recovery and ticket views
Translate the next 50 English placeholders in the Kinyarwanda locale, covering office and API reports, recovery, wait indicators, deletion safeguards, tickets, requests, and card details. Preserve REST API, MongoDB, environment-setting, and mail-header literals and the exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:02:58 +03:00
Lauri Ojansivu 438f71f0c7 Translate Kinyarwanda reports and locations
Translate the next 50 English placeholders in the Kinyarwanda locale, covering map locations, server troubleshooting, activity, templates, administration reports, recovery, and office login details. Preserve shell commands, entities, networking terms, and the exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:01:54 +03:00
Lauri Ojansivu cae4862477 Translate Kinyarwanda card relationships
Translate the next 50 English placeholders in the Kinyarwanda locale, covering sorting, completion, stickers, card dependencies, backgrounds, and locations. Preserve JSON and SVG literals and the exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-28 00:00:39 +03:00
Lauri Ojansivu 1d0e095bfb Translate Kinyarwanda advanced search guidance
Translate the next 50 English placeholders in the Kinyarwanda locale, covering search predicates, validation messages, paging, and detailed advanced-search operator guidance. Preserve query literals and the exact English placeholder inventory.\n\nThese direct Kinyarwanda translations have low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:59:46 +03:00
Lauri Ojansivu 10acb608cc Translate Kinyarwanda search operators
Translate 50 English placeholders covering missing-item feedback, card-result counts, and advanced-search operators and predicates. Preserve every printf and underscore token and existing correct-language translation.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:57:00 +03:00
Lauri Ojansivu 8856289b04 Translate Kinyarwanda templates and card views
Translate 50 English placeholders covering linked-list safety, domains, shared templates, card views, global search, and missing-item feedback. Preserve example domains, emphasis markup, and printf tokens exactly.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:56:09 +03:00
Lauri Ojansivu f7a0eb3a6d Translate Kinyarwanda roles and editor settings
Translate 50 English placeholders covering multi-card interaction, administrative dialogs, notification controls, board-role rights, weekdays, ownership, and linked-card guidance. Preserve keyboard chord names and existing correct-language translations.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:55:07 +03:00
Lauri Ojansivu b8e8fe227e Translate Kinyarwanda member and activity controls
Translate 50 English placeholders covering membership, authentication, board duplication, activity times, reminders, deletion confirmations, and interaction settings. Preserve HTML body tags and every format and underscore token exactly.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:54:10 +03:00
Lauri Ojansivu db8b256d40 Translate Kinyarwanda generated rules and branding
Translate 50 English placeholders covering generated rule actions, checklist syntax, date fields, authentication, custom branding, HTML head tags, manifests, assetlinks.json, layout, and card counters. Replace a detected Kirundi form and preserve every technical literal.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:53:07 +03:00
Lauri Ojansivu e9ec80ee48 Translate Kinyarwanda rule action phrases
Translate 50 English placeholders covering automation conditions and actions for boards, lists, labels, members, attachments, checklists, movement, colors, and email. Preserve existing correct-language values and all source tokens.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:51:43 +03:00
Lauri Ojansivu 50a0dfa172 Translate Kinyarwanda scheduled rule triggers
Translate 50 English placeholders covering visual workflow imports, schedules, due and duration triggers, automation buttons, sorting, completion, moves, and relative dates. Preserve n8n, Node-RED, N, weekday ranges, and interpolation tokens.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:50:43 +03:00
Lauri Ojansivu b517892456 Translate Kinyarwanda rule builder
Translate 50 English placeholders covering activity phrases, rule creation and selection, visual workflow building, triggers, and JSON, CSV, and Trello Butler import/export. Preserve all printf and underscore placeholders plus format and product names.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:49:48 +03:00
Lauri Ojansivu 6176650c92 Translate Kinyarwanda card dates and subtasks
Translate 50 English placeholders covering card dates and colors, deletion safeguards, subtask settings, minicard content, counts, parent paths, and label activity. Preserve board and printf placeholders exactly.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:49:02 +03:00
Lauri Ojansivu cc9b0f01ae Translate Kinyarwanda runtime and tenant settings
Translate 50 English placeholders covering database and OS diagnostics, custom-field display, account permissions, visibility, and organization tenancy. Preserve environment variables, diagnostic mode names, example hostnames, and Admin flags.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:48:05 +03:00
Lauri Ojansivu d44ab6baf3 Translate Kinyarwanda transfer and webhook settings
Translate 50 English placeholders covering attachment and API limits, registration, SMTP, invitations, webhooks, and software versions. Preserve SMTP, TLS, API, webhook, Node, Meteor, email line breaks, and every interpolation token.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:47:15 +03:00
Lauri Ojansivu a67dde0a78 Translate Kinyarwanda tracking and branding
Translate 50 English placeholders covering starred boards, time tracking, uploads, custom branding URLs, welcome templates, and WIP-limit feedback. Preserve URL, API, WIP, numeric shortcuts, and existing correct-language translations.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:46:16 +03:00
Lauri Ojansivu 072471b21d Translate Kinyarwanda navigation and shortcuts
Translate 50 English placeholders covering notifications, page access, image previews, visibility, member removal, search, WIP, keyboard shortcuts, sidebars, signup, and automatic board opening. Preserve the HTML login link, format token, and Sandstorm UI label exactly.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:45:26 +03:00
Lauri Ojansivu 635b3630de Translate Kinyarwanda board roles and selection
Translate 50 English placeholders covering board departure, archive and list operations, settings dialogs, multi-selection, normal and muted roles, and participation notifications. Preserve board-title tokens and external UI path labels.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:44:25 +03:00
Lauri Ojansivu 066a0f1b8e Translate Kinyarwanda Trello API imports
Translate 50 English placeholders covering ZIP safety, Trello API credentials and workspace imports, cancellation, cleanup, member mapping, validation, and labels. Preserve URLs, API, token, ZIP, JSON, and interpolation literals, and replace a Kirundi minimum phrase with Kinyarwanda.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:43:38 +03:00
Lauri Ojansivu 9ef720dc22 Translate Kinyarwanda filters and board imports
Translate 50 English placeholders covering advanced filters, member state, multi-source board imports, Trello attachments, and ZIP errors. Preserve operators, escapes, JSON fields, API paths, archive formats, product names, and interpolation tokens exactly.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:42:36 +03:00
Lauri Ojansivu f999202bea Translate Kinyarwanda exports and filters
Translate 50 English placeholders covering account conflicts, PDF and Excel card exports, attachment metadata, disk-space feedback, sorting, and card filters. Preserve file-format names and existing correct-language translations.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:40:49 +03:00
Lauri Ojansivu 51979839ae Translate Kinyarwanda email templates and errors
Translate 50 English placeholders covering deletion settings, profiles, WIP, email enrollment and verification templates, scrolling, and board/import/link errors. Preserve Global Admin, WIP, JSON, CSV, TSV, URLs, line breaks, and every interpolation token.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:40:05 +03:00
Lauri Ojansivu 3eb607c161 Translate Kinyarwanda roles and custom fields
Translate 50 English placeholders covering permission roles, deletion confirmations, clipboard actions, templates, and custom-field options. Preserve the multi-card JSON schema and verify its translated example still parses.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:39:02 +03:00
Lauri Ojansivu 41ef1b075d Translate Kinyarwanda card aging and colors
Translate 50 English placeholders covering settings, stars, automatic width, clipboard interaction, card aging, movement, accessibility dialogs, board closing, and color names. Replace detected Kirundi day and diminutive forms with Kinyarwanda before committing.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:38:13 +03:00
Lauri Ojansivu 96ef540d95 Translate Kinyarwanda themes and member mapping
Translate 50 English placeholders covering dialogs, linked items, imported-member mapping, themes, fonts, colors, avatars, language, and permissions. Replace detected Kirundi search vocabulary with Kinyarwanda before committing.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:37:10 +03:00
Lauri Ojansivu af73a3bd77 Translate Kinyarwanda voting and import dialogs
Translate 50 English placeholders covering membership, voting, Planning Poker, dependencies, administrative dialogs, and imports. Remove detected wrong-script and Kirundi fragments before applying, while preserving technical feature names.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:36:13 +03:00
Lauri Ojansivu ddb15ef737 Translate Kinyarwanda board views and archives
Translate 50 English placeholders covering board visibility and movement, backgrounds and views, zoom, archive and deletion guidance, dates, and card editing. Preserve HTML tags, workspace tokens, numeric ranges, and existing correct-language translations.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:35:05 +03:00
Lauri Ojansivu e8b91fdc19 Translate Kinyarwanda archive and board settings
Translate 50 English placeholders covering announcements, offline recovery, archives, templates, attachments, backgrounds, board summaries, and visibility. Replace a detected Kirundi carryover with Kinyarwanda and preserve all source tokens.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:34:16 +03:00
Lauri Ojansivu 62d8fc2ae3 Translate Kinyarwanda board layout controls
Translate 50 English placeholders covering workspace deletion, multi-board selection, Home behavior, list widths, keyboard shortcuts, swimlane height, and card controls. Preserve Admin Panel paths, numeric limits, and existing correct-language translations.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:33:17 +03:00
Lauri Ojansivu 3bddf8335d Translate Kinyarwanda movement and workspaces
Translate 50 English placeholders covering card movement and restoration, activity phrases, checklist history, dates, and workspace controls. Preserve every source placeholder and existing correct-language translation.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:32:27 +03:00
Lauri Ojansivu f8e73d2bf1 Translate Kinyarwanda activity history
Translate 50 English placeholders covering membership limits, comment and due-date controls, and concrete board, card, checklist, label, attachment, archive, and import activity. Preserve all source placeholders and existing correct-language translations.\n\nThese direct Kinyarwanda translations were completed with low confidence and welcome review by a Kinyarwanda speaker.\n\nThanks to xet7 !
2026-08-27 23:31:25 +03:00
Lauri Ojansivu 719a49094d Complete Kirundi translations
Translate the final 17 English placeholders covering event metadata, filesystem integrity, scoped export, WeKan imports, numeric search, and import sources. Verify Kirundi now has zero untranslated placeholders while preserving every format token and existing correct-language translation.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:30:17 +03:00
Lauri Ojansivu e5b347f284 Translate Kirundi repository and repair reports
Translate 50 remaining English placeholders covering repository accounts, authentication feedback, problem reports, card repair results, CPU status, and event metadata. Preserve OTP, API, CPU, byte units, and interpolation tokens.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:29:26 +03:00
Lauri Ojansivu 44d4574ec1 Translate Kirundi migration performance controls
Translate 50 remaining English placeholders covering storage migration, CPU and batch limits, monitoring, schedules, progress, minicard display, and system totals. Preserve CPU, GridFS, S3, millisecond ranges, and technical literals.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:28:42 +03:00
Lauri Ojansivu d45e8f37e0 Translate Kirundi migration monitoring
Translate 50 remaining English placeholders covering repair steps, board conversion, CPU and database monitoring, schedules, storage statistics, scans, and job queues. Preserve URL, ID, CPU, GridFS, and other technical terms.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:28:01 +03:00
Lauri Ojansivu 4e80923818 Translate Kirundi board integrity migrations
Translate 50 remaining English placeholders covering comprehensive board migration, duplicate and missing-list repair, lost-card restoration, URL correction, confirmations, and progress steps. Preserve swimlaneId, listId, URL, ID, and other technical identifiers.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:27:11 +03:00
Lauri Ojansivu 2d0c09bc24 Translate Kirundi storage migration controls
Translate 50 remaining English placeholders covering GridFS, S3 configuration, migration lifecycle, scheduled board operations, writable paths, and attachment monitoring. Preserve storage backend names, regions, SSL/TLS, and technical literals.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:26:15 +03:00
Lauri Ojansivu cbb8ce5dbb Translate Kirundi cloud backup settings
Translate 50 remaining English placeholders covering scheduled backups, restore modes, Google Cloud credentials and permissions, provider console paths, secret states, and connection tests. Preserve provider UI labels, product names, JSON, filenames, and credential field names.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:25:23 +03:00
Lauri Ojansivu 82c2bd9c73 Translate Kirundi security and backup controls
Translate 50 remaining English placeholders covering Sandstorm cleanup, card loading, link and code safety, import/export controls, anonymization, notifications, and backups. Preserve markup examples, paths, environment variables, product names, and technical literals.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:24:27 +03:00
Lauri Ojansivu 80c1d8bacf Translate Kirundi database migration settings
Translate 50 remaining English placeholders covering scheduled jobs, storage configuration, MongoDB and FerretDB migration, and Sandstorm migration status. Preserve URLs, environment variables, paths, commands, database names, and interpolation tokens exactly.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:22:18 +03:00
Lauri Ojansivu be94ad75c9 Translate Kirundi account protection and scheduled jobs
Translate 50 remaining English placeholders covering accessibility, brute-force protection, locked-user administration, storage paths, scheduled jobs, and board operations. Preserve existing correct-language translations.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:21:20 +03:00
Lauri Ojansivu 107e1a2428 Translate Kirundi board status and support settings
Translate 50 remaining English placeholders covering board status, uploads, workspace dragging, custom translations, checklist display, imports, support, and accessibility. Preserve PDF, ISO 8601, JSON, .zip, and interpolation tokens.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:20:24 +03:00
Lauri Ojansivu dec4953f6d Translate Kirundi attachment storage maintenance
Translate 50 remaining English placeholders covering bulk attachment moves, storage repair, progress statistics, file identifiers, and MongoDB compaction. Preserve GridFS, S3, MongoDB, Compact, and other technical literals.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:19:39 +03:00
Lauri Ojansivu 0f365c0c34 Translate Kirundi administration and storage controls
Translate 50 remaining English placeholders covering teams, organizations, Node diagnostics, legal notices, checklist actions, and attachment storage moves. Preserve Node, GridFS, S3, URL, and other technical literals.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:18:46 +03:00
Lauri Ojansivu c9fc5b5dfe Translate Kirundi recovery and ticket reports
Translate 50 remaining English placeholders covering office and API reporting, data recovery, loading indicators, deletion safeguards, and ticket states. Preserve REST API, MongoDB, WITH_API=true, and other technical literals.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:17:53 +03:00
Lauri Ojansivu f6b013f3e2 Translate Kirundi map and report controls
Translate 50 remaining English placeholders covering map detection, server diagnostics, string templates, creator metadata, and administrative reports. Preserve shell commands, HTML entities, and interpolation tokens exactly.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:17:03 +03:00
Lauri Ojansivu b6d63ffd59 Translate Kirundi card dependencies and locations
Translate 50 remaining English placeholders covering sorting, completion, stickers, card dependencies, board backgrounds, and location metadata. Preserve JSON, SVG, and all interpolation tokens exactly.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:16:10 +03:00
Lauri Ojansivu a4f7ec7796 Translate Kirundi search guidance
Translate 50 remaining English placeholders covering advanced-search validation, operators, predicates, pagination, and query guidance. Preserve all placeholders and query examples exactly.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:15:26 +03:00
Lauri Ojansivu bb6994b9d6 Translate Kirundi search operators
Translate 50 remaining English placeholders covering missing-item feedback, card-result counts, and advanced-search operators and predicates. Preserve every format token and existing correct-language translation.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:14:17 +03:00
Lauri Ojansivu f234506876 Translate Kirundi templates and card views
Translate 50 remaining English placeholders covering linked-list safety, domains, shared templates, card views, global search, and missing-item feedback. Preserve placeholders and existing correct-language translations.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:13:20 +03:00
Lauri Ojansivu c4d37ce8e3 Translate Kirundi roles and editor settings
Translate 50 remaining English placeholders covering multi-card interaction, administrative dialogs, notification controls, board-role rights, weekdays, ownership, and linked-card guidance. Preserve existing correct-language values.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:12:27 +03:00
Lauri Ojansivu 58b914cad0 Translate Kirundi member and activity controls
Translate 50 remaining English placeholders covering membership, authentication, board duplication, activity times, reminders, deletion confirmations, and interaction settings. Preserve every source placeholder exactly and retain existing correct-language translations.\n\nThese direct Kirundi translations were completed with low confidence and welcome review by a Kirundi speaker.\n\nThanks to xet7 !
2026-08-27 23:11:45 +03:00
Lauri Ojansivu 2e6c06a169 Translate Kirundi generated rules and branding
Translate 50 remaining Kirundi interface values for generated automation actions, checklist item syntax, date fields, card linking, authentication, product naming, custom HTML, manifests, asset links, layout, and counters. Preserve every placeholder, markup and JSON format name, asset filename, and comma-separated structure exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 23:10:07 +03:00
Lauri Ojansivu f9b5ce82e5 Translate Kirundi rule action phrases
Translate 50 remaining Kirundi interface values for board, list, label, member, attachment, checklist, archive, movement, color, checking, and email rule actions. Preserve the full placeholder inventory and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 23:08:32 +03:00
Lauri Ojansivu b484867677 Translate Kirundi scheduled rule triggers
Translate 50 remaining Kirundi interface values for visual workflow imports, schedules, due-date conditions, card age, rule buttons, sorting, completion, bulk movement, relative dates, units, and trigger phrases. Preserve workflow product names, parameters, and placeholders exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 23:07:27 +03:00
Lauri Ojansivu 43b3526540 Translate Kirundi rule workflows and imports
Translate 50 remaining Kirundi interface values for activity history, rule triggers and actions, workflow building, event triggers, schedule and date rules, JSON and CSV exchange, Trello Butler mapping, and rule scope. Preserve every placeholder and rule-format and product literal exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 23:06:07 +03:00
Lauri Ojansivu 3a55a606dd Translate Kirundi card dates and subtask settings
Translate 50 remaining Kirundi interface values for card dates and colors, deletion, subtask and card settings, parent cards, minicard metadata, counts, path prefixes, source boards, and label activity. Preserve every placeholder and checklist count literal exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 23:04:34 +03:00
Lauri Ojansivu efbfe38c0a Translate Kirundi diagnostics and organization tenancy
Translate 50 remaining Kirundi interface values for database and operating-system diagnostics, custom-field display, account changes, visibility, organization and team templates, member synchronization, tenancy domains, and delegated administration. Preserve every diagnostic, environment-variable, hostname, role, and placeholder literal exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 23:03:14 +03:00
Lauri Ojansivu f32aaaa1fd Translate Kirundi transfer and webhook settings
Translate 50 remaining Kirundi interface values for attachment and API limits, registration, invitations, SMTP and TLS, invitation emails, authorization, webhooks, filtering, and runtime version labels. Preserve every placeholder, email line break, protocol, service, and product literal exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 23:00:49 +03:00
Lauri Ojansivu d1c5c626eb Translate Kirundi tracking and templates
Translate 50 remaining Kirundi interface values for starred boards, subscriptions, overtime tracking, numbered shortcuts, uploads, custom logos and URL schemes, welcome content, templates, and WIP errors. Preserve every placeholder, numeric shortcut, URL, dimension, and WIP literal exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:59:36 +03:00
Lauri Ojansivu 96ca1b66ee Translate Kirundi privacy and shortcut controls
Translate 50 remaining Kirundi interface values for watched items, private pages, previews, board privacy, quick access, member removal, Sandstorm access, search, WIP limits, shortcuts, sidebars, signup, starring, and default boards. Preserve placeholders, HTML markup, and every operational and product literal exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:58:30 +03:00
Lauri Ojansivu 8fe82ffd3a Translate Kirundi membership and selection controls
Translate 50 remaining Kirundi interface values for board departure, archive and list actions, organization settings, imports, deletion, login, multi-selection, muted boards, restricted roles, invitations, and notifications. Preserve all placeholders and Trello, Excel, CSV, and TSV literals exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:57:02 +03:00
Lauri Ojansivu c414ef49e4 Translate Kirundi Trello API imports
Translate 50 remaining Kirundi interface values for Trello ZIP safety, API credentials, workspace and board imports, progress and cancellation, member mapping, validation, invitations, shortcuts, and labels. Preserve every URL, API and archive literal, numeric example, and placeholder exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:55:51 +03:00
Lauri Ojansivu 53307331a8 Translate Kirundi advanced filters and board imports
Translate 50 remaining Kirundi interface values for advanced filters, member states, impersonation, and board imports from kanban, issue-tracker, spreadsheet, JSON, and ZIP sources. Preserve every placeholder, operator example, escape sequence, API path, data field, format, and product name exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:54:48 +03:00
Lauri Ojansivu e0448b2995 Translate Kirundi card exports and filters
Translate 50 remaining Kirundi interface values for account conflicts, card exports, export fields and disk-space errors, list sorting, and card, date, label, and member filters. Preserve Excel and PDF format names and every placeholder exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:52:38 +03:00
Lauri Ojansivu 4fd9f43c29 Translate Kirundi email and validation messages
Translate 50 remaining Kirundi interface values for permanent deletion, WIP limits, card dates, notifications, account emails, role errors, structured-data validation, export recovery, linked-card safety, and disabled users. Preserve placeholders, email layout, role names, and format and product literals exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:51:37 +03:00
Lauri Ojansivu afff176f1a Translate Kirundi roles and custom fields
Translate 50 remaining Kirundi interface values for restricted roles, deletion confirmations, clipboard actions, linked and copied cards, templates, labels, and custom fields. Keep the translated card example valid JSON with its code field names unchanged, preserve every placeholder, and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:50:17 +03:00
Lauri Ojansivu 27079c1384 Translate Kirundi card aging and colors
Translate 50 remaining Kirundi interface values for starring, automatic list width, clipboard input, card aging, movement, dialogs, closing controls, and color names. Express shared color loanwords as Kirundi phrases so no English placeholders remain, preserve every token, and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:49:12 +03:00
Lauri Ojansivu 4a219c612f Translate Kirundi member mapping and themes
Translate 50 remaining Kirundi interface values for member dialogs, restore actions, rules, CAS sign-in, linked items, imported-member mapping, themes, fonts, text colors, avatars, language, and permissions. Preserve placeholders, CAS, and the complete font digit sample exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:48:11 +03:00
Lauri Ojansivu f3fe200a08 Translate Kirundi voting and card dialogs
Translate 50 remaining Kirundi interface values for card membership, voting, Planning Poker, dependencies, organizations, teams, backgrounds, domains, member mapping, and imports. Preserve the Planning Poker product term and the full placeholder inventory exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:47:00 +03:00
Lauri Ojansivu 73f77e9854 Translate Kirundi board views and archives
Translate 50 remaining Kirundi interface values for board navigation, workspaces, appearance, views, zoom, calendar navigation, archives, deletion guidance, due time, and card editing. Preserve placeholders, HTML markup, and the zoom range exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:45:54 +03:00
Lauri Ojansivu ff4ba92cca Translate Kirundi board administration
Translate 50 remaining Kirundi interface values for administration, announcements, public boards, offline recovery, archives, attachments, watching, board appearance, member groups, and private-board information. Preserve placeholders and HTML markup exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:44:45 +03:00
Lauri Ojansivu 0cd5ebc68e Translate Kirundi workspace and layout controls
Translate 50 remaining Kirundi interface values for workspaces, multi-board selection, Home boards, dates, templates, list widths, keyboard shortcuts, swimlane height, checklists, covers, labels, and members. Preserve placeholders and administrative and measurement literals exactly and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:43:55 +03:00
Lauri Ojansivu 413673455e Translate Kirundi activities and workspaces
Translate 50 remaining Kirundi interface values for card movement, membership, compact activity history, checklists, comments, dates, and workspace management. Follow existing Kirundi kanban terminology and preserve every placeholder and markdown literal exactly.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:42:54 +03:00
Lauri Ojansivu 8f1f1e308a Translate Kirundi activity history
Translate 50 remaining Kirundi interface values for membership rules, due dates, replies, and board, card, list, swimlane, attachment, checklist, comment, archive, and import activities. Follow the locale's existing kanban vocabulary, preserve every placeholder exactly, and record the next checkpoint.

These direct Kirundi translations are low confidence and welcome review by a Kirundi speaker.

Thanks to xet7 !
2026-08-27 22:41:45 +03:00
Lauri Ojansivu ea28d5a8a2 Complete Pashto translations
Translate the final 67 Pashto interface values for migration resources, authentication, repositories, API endpoints, problem reporting, repairs, and imports. Preserve placeholders, markup, formats, network labels, and product names exactly, prove that Pashto has no remaining placeholders, and advance the global checkpoint to Kirundi.

These direct Pashto translations are low confidence and welcome review by a Pashto speaker.

Thanks to xet7 !
2026-08-27 22:40:19 +03:00
Lauri Ojansivu 909482387e Translate Pashto migration resource controls
Translate 50 remaining Pashto interface values for migration storage, batching, CPU and delay limits, monitoring, scheduling, progress, and system-resource statistics. Preserve all storage, unit, range, and CPU literals exactly and record the next checkpoint.

These direct Pashto translations are low confidence and welcome review by a Pashto speaker.

Thanks to xet7 !
2026-08-27 22:38:20 +03:00
Lauri Ojansivu 1b48b8b9cc Translate Pashto migration monitoring and jobs
Translate 50 remaining Pashto interface values for board-repair steps, conversion progress, CPU and migration monitoring, scheduled intervals, storage statistics, scans, and job queues. Preserve URL, ID, CPU, and GridFS literals exactly and record the next checkpoint.

These direct Pashto translations are low confidence and welcome review by a Pashto speaker.

Thanks to xet7 !
2026-08-27 22:37:05 +03:00
Lauri Ojansivu 5e4f484eca Translate Pashto board integrity migrations
Translate 50 remaining Pashto interface values for comprehensive board checks, lost-card restoration, list repair, URL repair, migration confirmations, progress, and structural steps. Preserve field identifiers and URL and ID literals exactly and record the next checkpoint.

These direct Pashto translations are low confidence and welcome review by a Pashto speaker.

Thanks to xet7 !
2026-08-27 22:35:56 +03:00
Lauri Ojansivu cc818bfe04 Translate Pashto migration and S3 controls
Translate 50 remaining Pashto interface values for GridFS and CollectionFS, migration lifecycle, S3 configuration, scheduled board operations, storage paths, and attachment monitoring. Preserve product and connection literals exactly and record the next checkpoint.

These direct Pashto translations are low confidence and welcome review by a Pashto speaker.

Thanks to xet7 !
2026-08-27 22:34:26 +03:00
Lauri Ojansivu 92257eaf5f Translate Pashto backup schedules and cloud setup
Translate 50 remaining Pashto interface values for scheduled backups, restores, Google Cloud credentials, AWS and Azure configuration paths, cloud connection tests, and Azure attachment storage. Preserve placeholders and all operational console, format, credential, and product literals exactly and record the next checkpoint.

These direct Pashto translations are low confidence and welcome review by a Pashto speaker.

Thanks to xet7 !
2026-08-27 22:33:20 +03:00
Lauri Ojansivu 849d61a52a Translate Pashto security controls and backups
Translate 50 remaining Pashto interface values for Sandstorm cleanup, card loading, secure text rendering, import and export restrictions, anonymization, notifications, and backups. Preserve placeholders, markup examples, environment variables, paths, and product names exactly and record the next checkpoint.

These direct Pashto translations are low confidence and welcome review by a Pashto speaker.

Thanks to xet7 !
2026-08-27 22:31:11 +03:00
Lauri Ojansivu 04b28c25db Translate Pashto storage and database migrations
Translate 50 remaining Pashto interface values for scheduled jobs, filesystem and cloud storage, Azure settings, and database and Sandstorm migrations. Preserve placeholders, commands, paths, URLs, environment variables, and product names exactly and record the next checkpoint.

These direct Pashto translations are low confidence and welcome review by a Pashto speaker.

Thanks to xet7 !
2026-08-27 22:28:57 +03:00
Lauri Ojansivu fbba205987 Translate Pashto lockout and scheduled operations
Translate 50 remaining Pashto interface values for accessibility, brute-force lockout controls, people status, scheduled jobs, storage paths, and board maintenance operations. Preserve the full placeholder inventory and record the next checkpoint.

These direct Pashto translations are low confidence and welcome review by a Pashto speaker.

Thanks to xet7 !
2026-08-27 22:27:33 +03:00
Lauri Ojansivu a394d8be2d Translate Pashto board status and support settings
Translate 50 remaining Pashto interface values for board timing, uploads, account prompts, file constraints, custom translations, checklists, support, and accessibility. Preserve workspace placeholders and technical format literals exactly and record the next checkpoint.

These direct Pashto translations are low confidence and welcome review by a Pashto speaker.

Thanks to xet7 !
2026-08-27 22:26:26 +03:00
Lauri Ojansivu cb58559619 Translate Pashto attachment storage management
Translate 50 remaining Pashto interface values for attachment migration and repair, default storage, file statistics, identifiers, and MongoDB Compact operations. Preserve storage, database, replication, and identifier literals exactly and record the next checkpoint.\n\nThese direct Pashto translations are low confidence and welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:25:09 +03:00
Lauri Ojansivu 48914a7e78 Translate Pashto teams and attachment actions
Translate 50 remaining Pashto interface values for team and organization membership, Node memory diagnostics, legal notices, checklists, subtasks, and attachment storage moves. Preserve technical product and storage literals exactly and record the next checkpoint.\n\nThese direct Pashto translations are low confidence and welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:22:47 +03:00
Lauri Ojansivu d2595b5df0 Translate Pashto recovery and ticket controls
Translate 50 remaining Pashto interface values for office and API activity, recovery maintenance, swimlane copying, wait indicators, organization and team warnings, and ticket controls. Preserve all technical literals and record the next translation checkpoint.\n\nThese direct Pashto translations are low confidence and welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:21:25 +03:00
Lauri Ojansivu 4337363d1d Translate Pashto maps and reports
Translate 50 remaining Pashto interface values for map coordinates, troubleshooting, sorting, activities, custom-field templates, and administrative reports. Preserve placeholders, command examples, HTML entities, and networking literals exactly, and record the next checkpoint.\n\nThese direct Pashto translations are low confidence and welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:19:50 +03:00
Lauri Ojansivu 9a5d6bcd9a Translate Pashto sorting and dependencies
Translate 50 remaining Pashto interface values for sorting, completion, stickers, card dependencies, board backgrounds, and locations. Preserve the English placeholder inventory exactly and record the next translation checkpoint.\n\nThese direct Pashto translations are low confidence and welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:18:42 +03:00
Lauri Ojansivu 128bc7c383 Translate Pashto search instructions
Fill 50 remaining Pashto placeholders for search predicates, validation errors, pagination, and complete global-search operator, status, field, sort, limit, and composition guidance. Preserve placeholders, whitespace-free tokens, code examples, malformed source markers, and Markdown counts exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:16:33 +03:00
Lauri Ojansivu d9d748c93a Translate Pashto search operators
Fill 50 remaining Pashto placeholders for lookup errors, result counts, and global-search operators and predicates covering boards, cards, people, dates, sorting, text, fields, attachments, and checklists. Keep every search token whitespace-free and preserve all placeholders exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:14:34 +03:00
Lauri Ojansivu bea7a4d53d Translate Pashto domains and card views
Fill 50 remaining Pashto placeholders for linked-list protection, checklists, tasks, domains, shared templates, organization identity, My Cards, Due Cards, global search views, and not-found errors. Preserve Markdown, domain examples, symbols, and placeholders exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:13:17 +03:00
Lauri Ojansivu 6a19a97359 Translate Pashto editor and role settings
Fill 50 remaining Pashto placeholders for multi-card windows, editor submission, organizations, teams, users, notification states, renaming, role permissions, weekdays, metadata, voting, and linked-card deletion. Preserve editor shortcuts and Admin Panel UI terminology exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:11:40 +03:00
Lauri Ojansivu b6cef1af72 Translate Pashto authentication and card reminders
Fill 50 remaining Pashto placeholders for board members, custom HTML body insertion, authentication, duplication, counts, swimlane deletion, multiline card dates and reminders, positioning, deletion, resizing, and Enter submission. Preserve markup, source literals, multiline layout, and placeholders exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:10:24 +03:00
Lauri Ojansivu 10f820dd61 Translate Pashto rule and custom-head settings
Fill 50 remaining Pashto placeholders for rule email, archive, labels, members, checklists, cards, swimlanes, date fields, authentication, product naming, HTML head tags, manifests, asset links, layout, logos, and counters. Preserve code formats, HTML, JSON, filenames, and examples exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:08:14 +03:00
Lauri Ojansivu 04903a4417 Translate Pashto rule actions
Fill 50 remaining Pashto placeholders for board and list rule targets, movement and archive triggers, labels, members, attachments, checklists, positioning, colors, membership removal, and email actions. Preserve every existing human translation and exact placeholder inventory.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:06:47 +03:00
Lauri Ojansivu fba206cc53 Translate Pashto workflow scheduling
Fill 50 remaining Pashto placeholders for visual-workflow imports, schedules, due conditions, list durations, buttons, sorting, completion, bulk movement, relative dates, units, and movement triggers. Preserve product names, schedule ranges, symbols, and placeholders exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:05:37 +03:00
Lauri Ojansivu 5780a65013 Translate Pashto rule workflows
Fill 50 remaining Pashto placeholders for activity history, board-rule selection and workflows, triggers, actions, schedules, received dates, and JSON, CSV, and Trello Butler transfer. Preserve every placeholder and rule-transfer literal exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:04:21 +03:00
Lauri Ojansivu ca7ae82bd0 Translate Pashto card and subtask settings
Fill 50 remaining Pashto placeholders for card dates and colors, assignment metadata, board and notification deletion, duplicate cleanup, subtasks, minicard fields, counters, covers, and parent paths. Preserve placeholders and checklist counter literals exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:03:08 +03:00
Lauri Ojansivu a3e778bce7 Translate Pashto runtime and organization settings
Fill 50 remaining Pashto placeholders for database and runtime metadata, reactivity, DDP, operating-system resources, fields, visibility, organizations, teams, authentication sync, multitenancy, domains, and administrators. Preserve environment variables, hostnames, product names, and runtime literals exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:01:50 +03:00
Lauri Ojansivu 63b92476c6 Translate Pashto transfer and webhook settings
Fill 50 remaining Pashto placeholders for attachment and API limits, avatar blocking, registration, SMTP and TLS, invitation and test emails, authorization, webhooks, filtering, and runtime labels. Preserve placeholders, email layout, and service and product literals exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 22:00:38 +03:00
Lauri Ojansivu ed8ec342d1 Translate Pashto tracking and templates
Fill 50 remaining Pashto placeholders for subscriptions, time tracking, overtime, shortcuts, uploads, custom branding and URL schemes, archive warnings, watching, welcome labels, templates, and WIP errors. Preserve URL, numeric, and WIP literals exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:59:14 +03:00
Lauri Ojansivu 1c4949ec80 Translate Pashto notifications and shortcuts
Fill 50 remaining Pashto placeholders for watch notifications, private pages, image previews, board visibility, member removal, Sandstorm access, search, WIP limits, shortcuts, sidebars, signup, and default boards. Preserve anchor markup, products, UI labels, and placeholders exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:58:03 +03:00
Lauri Ojansivu fa810a3e80 Translate Pashto board roles and bulk actions
Fill 50 remaining Pashto placeholders for administrator protection, leaving boards, list movement and archiving, settings, swimlanes, imports, login, multi-selection, mute states, archives, roles, and notifications. Preserve placeholders and import format names exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:56:32 +03:00
Lauri Ojansivu cef04be344 Translate Pashto Trello API imports
Fill 50 remaining Pashto placeholders for Trello ZIP safety, API workspace and board imports, cancellation and cleanup, member mapping, validation, invitations, shortcuts, and labels. Preserve URLs, API names, extensions, years, and placeholders exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:55:14 +03:00
Lauri Ojansivu 81e5835809 Translate Pashto advanced filters and imports
Fill 50 remaining Pashto placeholders for advanced filtering, member states, and Kanboard, NextCloud Deck, OpenProject, Asana, ZenKit, Trello, Jira, Excel, CSV, TSV, and WeKan imports. Preserve filter syntax, endpoints, JSON keys, product names, extensions, and placeholders exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:53:48 +03:00
Lauri Ojansivu aa57874ba5 Translate Pashto exports and filters
Fill 50 remaining Pashto placeholders for account errors, board and card exports, Excel and PDF options, attachment metadata, disk-space reporting, list sorting, and date, title, label, and member filters. Preserve all format names and placeholders exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:51:38 +03:00
Lauri Ojansivu 31ac2e922e Translate Pashto email and validation messages
Fill 50 remaining Pashto placeholders for deletion, WIP limits, editing, account emails, invitations, password resets, verification, and validation errors. Preserve all email placeholders and line structure plus WIP, JSON, CSV, TSV, and WeKan literals exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:50:11 +03:00
Lauri Ojansivu 398933e750 Translate Pashto roles and custom fields
Fill 50 remaining Pashto placeholders for comment and read roles, workers, deletion confirmations, clipboard actions, linked cards, bulk template copying, labels, and custom fields. Preserve valid JSON structure and all source keys in the translated example.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:48:42 +03:00
Lauri Ojansivu 869b805957 Translate Pashto settings and colors
Fill 50 remaining Pashto placeholders for settings, stars, automatic widths, clipboard input, card aging, directional movement, dialog accessibility, board closure, and colors. Preserve exact placeholder inventories and tier counts.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:46:57 +03:00
Lauri Ojansivu 8c0223c3f9 Translate Pashto member and theme dialogs
Fill 50 remaining Pashto placeholders for members, invitations, sorting, restoration, rules, CAS, linked items, imported-member mapping, themes, fonts, colors, avatars, languages, and permissions. Preserve authentication names and preview digits exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:45:37 +03:00
Lauri Ojansivu e729ef46ab Translate Pashto voting and import dialogs
Fill 50 remaining Pashto placeholders for card membership, voting, Planning Poker, dependencies, organizations, teams, avatars, backgrounds, domains, member mapping, and imports. Preserve the Planning Poker feature name and exact placeholder inventory.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:44:17 +03:00
Lauri Ojansivu 4647dad0bc Translate Pashto board views and archives
Fill 50 remaining Pashto placeholders for public boards, workspace assignment, board appearance and views, device modes, zoom, calendars, archives, card dates, attachments, fields, labels, and members. Preserve placeholders, strong markup, and zoom ranges exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:43:13 +03:00
Lauri Ojansivu cffe66bd0f Translate Pashto board administration
Fill 50 remaining Pashto placeholders for administration, announcements, archives, templates, attachments, board appearance, All Boards settings, membership summaries, stars, and privacy. Preserve placeholders, URL, and strong markup exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:41:52 +03:00
Lauri Ojansivu 61389c4eb2 Translate Pashto workspace and layout controls
Fill 50 remaining Pashto placeholders for workspace selection, Home boards, dates, templates, list widths, keyboard shortcuts, swimlane heights, checklists, covers, labels, and members. Preserve percent placeholders, numeric limits, units, and Admin Panel UI paths exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:40:43 +03:00
Lauri Ojansivu 70a37286e3 Translate Pashto activities and workspaces
Fill 50 remaining Pashto placeholders for card movement, activity history, checklist and comment changes, dates, and workspace controls. Preserve every percent and underscore placeholder and the markdown literal exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:39:22 +03:00
Lauri Ojansivu e9597f4861 Start Pashto activity translations
Fill the first 50 Pashto placeholders for activity history, board membership, dates, comments, attachments, subtasks, labels, checklists, custom fields, archiving, and imports. Preserve every percent and underscore-delimited placeholder exactly.\n\nThese low-confidence direct translations welcome review by a Pashto speaker.\n\nThanks to xet7 !
2026-08-27 21:38:04 +03:00
Lauri Ojansivu b0e0da8a5c Complete Oromo translations
Fill the final 55 Oromo placeholders for repository uploads, account validation, problem and repair reporting, events, filesystem integrity, global search, and board imports. Preserve all placeholders, search syntax, extensions, product names, and network literals exactly.\n\nThese low-confidence direct translations complete the Oromo locale and welcome review by an Oromo speaker.\n\nThanks to xet7 !
2026-08-27 21:36:30 +03:00
Lauri Ojansivu 3d894916d7 Translate Oromo migration and repository monitoring
Fill 50 remaining Oromo placeholders for migration logs and controls, monitoring progress, operation totals, system resources, account creation, OTP, and repositories. Preserve exact placeholder inventories and technical literals.\n\nThanks to xet7 !
2026-08-27 21:34:54 +03:00
Lauri Ojansivu f6ece877cc Translate Oromo migration monitoring
Fill 50 remaining Oromo placeholders for board conversion, file scanning, URL repair, runtime monitoring, scheduled intervals, and migration targets and thresholds. Preserve CPU, GridFS, S3, URL, unit, percentage, and numeric range literals exactly.\n\nThanks to xet7 !
2026-08-27 21:33:09 +03:00
Lauri Ojansivu 3f7899ef51 Translate Oromo board integrity repairs
Fill 50 remaining Oromo placeholders for missing-list, avatar URL, file URL, shared-list, orphaned-card, and lost-item migration repairs and progress. Preserve URL, ID, swimlaneId, and listId literals exactly.\n\nThanks to xet7 !
2026-08-27 21:32:14 +03:00
Lauri Ojansivu 0ef759d6b9 Translate Oromo attachment and board migrations
Fill 50 remaining Oromo placeholders for migration lifecycle, CollectionFS and S3 guidance, scheduled board operations, attachment migrations, duplicate cleanup, and restoration. Preserve all storage names, protocol labels, regions, and database field identifiers exactly.\n\nThanks to xet7 !
2026-08-27 21:31:03 +03:00
Lauri Ojansivu 2c502b558e Translate Oromo cloud storage guidance
Fill 50 remaining Oromo placeholders for backup restoration, Google Cloud, AWS S3, Azure, GridFS, cloud connection tests, and migration controls. Preserve console paths, UI labels, product names, JSON references, and navigation arrows exactly.\n\nThanks to xet7 !
2026-08-27 21:29:47 +03:00
Lauri Ojansivu e85aee7b4c Translate Oromo security and backup settings
Fill 50 remaining Oromo placeholders for card loading, security rendering, imports and exports, anonymization, activities, notifications, watching, and backups. Preserve markup examples, product names, environment variables, paths, storage names, and time formats exactly.\n\nThanks to xet7 !
2026-08-27 21:28:07 +03:00
Lauri Ojansivu e210c9ebd8 Translate Oromo database migration controls
Fill 50 remaining Oromo placeholders for scheduled migration recovery, filesystem, S3 and Azure storage, MongoDB-FerretDB migration, Sandstorm cleanup, and card loading. Preserve every URL, environment variable, command, path, version, and placeholder exactly.\n\nThanks to xet7 !
2026-08-27 21:26:20 +03:00
Lauri Ojansivu 04ac7b7696 Translate Oromo lockout and scheduled operations
Fill 50 remaining Oromo placeholders for locked-user administration, storage paths, scheduled board operations, cron jobs, and migration statuses. Preserve existing human translations and exact placeholder inventories.\n\nThanks to xet7 !
2026-08-27 21:24:52 +03:00
Lauri Ojansivu 4bc9bf38bc Translate Oromo uploads and account protection
Fill 50 remaining Oromo placeholders for uploads, custom translations, checklists, support and accessibility pages, and brute-force account-lockout controls. Preserve placeholders and technical file-format and standards literals exactly.\n\nThanks to xet7 !
2026-08-27 21:23:51 +03:00
Lauri Ojansivu b66cbf4151 Translate Oromo storage repair and board status
Fill 50 remaining Oromo placeholders for attachment repair, storage selection, MongoDB GridFS compaction, board status, uploads, and account prompts. Preserve database, storage, and identifier literals exactly and record the low-confidence checkpoint.\n\nThanks to xet7 !
2026-08-27 21:22:42 +03:00
Lauri Ojansivu e0ac548594 Translate Oromo runtime and attachment controls
Fill 50 remaining Oromo placeholders for Node runtime metrics, organizations, legal notices, checklists, subtasks, and attachment storage movement. Preserve URL, GridFS, S3, and runtime terminology and record the low-confidence checkpoint.\n\nThanks to xet7 !
2026-08-27 21:21:25 +03:00
Lauri Ojansivu 791dd809d6 Translate Oromo recovery and support controls
Fill 50 remaining Oromo placeholders for recovery status, swimlane copying, wait spinners, support tickets, requests, invitations, team controls, and Node heap labels. Preserve technical literals and record the low-confidence direct translation checkpoint.\n\nThanks to xet7 !
2026-08-27 21:20:27 +03:00
Lauri Ojansivu d85e51473f Translate Oromo administrative reports
Fill 50 remaining Oromo placeholders for board activity, custom-field templates, and administrative file, security, office, API, and recovery reports. Preserve placeholders, HTML entities, product names, network terms, and configuration literals exactly.\n\nThanks to xet7 !
2026-08-27 21:19:32 +03:00
Lauri Ojansivu 446ff3b931 Translate Oromo dependencies and locations
Fill 50 remaining Oromo placeholders covering completion states, stickers, card dependencies, board backgrounds, locations, server troubleshooting, and sorting. Preserve all placeholders and command literals and record the low-confidence direct translation checkpoint.\n\nThanks to xet7 !
2026-08-27 21:18:15 +03:00
Lauri Ojansivu b012ec31a4 Translate Oromo search notes and sorting
Fill 20 remaining Oromo placeholders for global-search limits and notes, search links, label metadata, archive timestamps, and card and board sorting controls. Preserve search tokens and Markdown syntax exactly and record the low-confidence direct translation checkpoint.\n\nThanks to xet7 !
2026-08-27 21:17:15 +03:00
Lauri Ojansivu 3449920632 Translate Oromo search field guidance
Translate Oromo search status, field-existence, and sorting guidance while preserving syntax and placeholders.\n\nThanks to xet7 !
2026-08-27 21:08:56 +03:00
Lauri Ojansivu 507614fff5 Translate Oromo search status guidance
Translate Oromo created, modified, and status search guidance while preserving syntax, Markdown, placeholders, and correct-language translations.\n\nThanks to xet7 !
2026-08-27 21:08:04 +03:00
Lauri Ojansivu 39e52c8350 Translate Oromo search ownership guidance
Translate Oromo assignee, creator, organization, team, and due-date search guidance while preserving syntax and placeholders.\n\nThanks to xet7 !
2026-08-27 21:07:32 +03:00
Lauri Ojansivu fe0e79cf2e Translate Oromo search operator guidance
Translate Oromo global-search operator guidance while preserving code examples, Markdown, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 21:06:58 +03:00
Lauri Ojansivu ba383dcb1b Translate Oromo search syntax introduction
Translate the Oromo global-search syntax introduction while preserving its code examples, Markdown, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 21:06:12 +03:00
Lauri Ojansivu f29c00cb61 Translate Oromo search predicates and errors
Continue the direct low-confidence Oromo translation with remaining search predicates, operator errors, pagination, and instruction headings while preserving query-token syntax, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 21:05:26 +03:00
Lauri Ojansivu 97c6fe2810 Translate Oromo global search operators
Continue the direct low-confidence Oromo translation with lookup errors, result counts, and localized global-search operators and predicates while preserving token syntax, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 21:03:35 +03:00
Lauri Ojansivu cd72d427c4 Translate Oromo domains and card views
Continue the direct low-confidence Oromo translation with domains, shared templates, My Cards, Due Cards, global search, and not-found errors while preserving domain examples, Markdown, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 21:02:35 +03:00
Lauri Ojansivu f57b2475af Translate Oromo roles and editor behavior
Continue the direct low-confidence Oromo translation with card windows, keyboard submission, notifications, role permissions, weekdays, metadata, voting, and linked-card safety while preserving shortcut literals, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 21:01:22 +03:00
Lauri Ojansivu fde41e6feb Translate Oromo authentication and activity settings
Continue the direct low-confidence Oromo translation with custom HTML, authentication, duplication, activity reminders, deletion controls, resizing, and editor submission while preserving markup, line breaks, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 21:00:25 +03:00
Lauri Ojansivu 8935ecd830 Translate Oromo rule actions and branding
Continue the direct low-confidence Oromo translation with checklist and date actions, authentication, custom HTML and JSON branding, layout, and counters while preserving technical literals, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:58:23 +03:00
Lauri Ojansivu 7a97e23c77 Translate Oromo rule conditions and actions
Continue the direct low-confidence Oromo translation with movement, archive, label, member, attachment, checklist, color, and email rule phrases while preserving exact placeholders and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:57:29 +03:00
Lauri Ojansivu 192501f7d4 Translate Oromo workflow scheduling
Continue the direct low-confidence Oromo translation with visual workflow imports, schedules, due-date conditions, buttons, sorting, completion, and relative dates while preserving workflow products, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:56:32 +03:00
Lauri Ojansivu 786dfffdb1 Translate Oromo automation rules
Continue the direct low-confidence Oromo translation with activity history, visual rule building, triggers, actions, and rule transfer while preserving JSON, CSV, Trello Butler, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:55:41 +03:00
Lauri Ojansivu 9a783443eb Translate Oromo card dates and subtask settings
Continue the direct low-confidence Oromo translation with card dates, destructive confirmations, subtask placement, minicard metadata, and parent-card display while preserving exact placeholders and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:54:37 +03:00
Lauri Ojansivu 5a6cb0e8f4 Translate Oromo diagnostics and tenancy
Continue the direct low-confidence Oromo translation with runtime diagnostics, field display, visibility, organization and team synchronization, and multitenancy while preserving configuration literals, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:53:43 +03:00
Lauri Ojansivu 54d604e2ec Translate Oromo transfer and webhook settings
Continue the direct low-confidence Oromo translation with attachment and API limits, SMTP invitations, webhooks, and version labels while preserving protocols, email layout, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:52:37 +03:00
Lauri Ojansivu 2d42e11a79 Translate Oromo tracking and customization
Continue the direct low-confidence Oromo translation with time tracking, uploads, custom URLs, welcome content, templates, and WIP errors while preserving technical literals, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:51:42 +03:00
Lauri Ojansivu 5fd2598716 Translate Oromo board access and shortcuts
Continue the direct low-confidence Oromo translation with board visibility, member removal, search, WIP limits, shortcuts, sidebars, and default-board controls while preserving HTML, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:50:40 +03:00
Lauri Ojansivu f36cbf68f8 Translate Oromo roles and selection controls
Continue the direct low-confidence Oromo translation with board departure, list archiving, settings, multi-selection, muted boards, and roles while preserving exact placeholders and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:49:44 +03:00
Lauri Ojansivu 918448d02a Translate Oromo Trello import controls
Continue the direct low-confidence Oromo translation with Trello API and ZIP imports, cancellation, member mapping, validation, and labels while preserving URLs, file extensions, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:48:42 +03:00
Lauri Ojansivu 7916870edf Translate Oromo filters and board imports
Continue the direct low-confidence Oromo translation with advanced filters and multi-format board import guidance while preserving operators, API paths, JSON fields, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:47:38 +03:00
Lauri Ojansivu f079d4cc38 Translate Oromo exports and filters
Continue the direct low-confidence Oromo translation with account errors, PDF and Excel card exports, sorting, and date, label, and member filters while preserving exact placeholders and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:46:06 +03:00
Lauri Ojansivu b9799c0f7c Translate Oromo email and validation messages
Continue the direct low-confidence Oromo translation with account emails, WIP and deletion controls, data validation, import recovery, and permission errors while preserving exact placeholders, email layout, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:45:14 +03:00
Lauri Ojansivu 4683e6a386 Translate Oromo roles and custom fields
Continue the direct low-confidence Oromo translation with access roles, deletion prompts, clipboard actions, bulk-copy JSON, and custom fields while preserving code literals, exact placeholders, and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:43:48 +03:00
Lauri Ojansivu 2506a4364b Translate Oromo navigation and colors
Continue the direct low-confidence Oromo translation with list sizing, card aging, navigation, movement controls, and color names while preserving exact placeholders and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:42:45 +03:00
Lauri Ojansivu 5751cee210 Translate Oromo member and theme controls
Continue the direct low-confidence Oromo translation with member mapping, linked items, themes, fonts, avatars, and permissions while preserving exact placeholders and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:41:39 +03:00
Lauri Ojansivu 0d7782f65b Translate Oromo voting and imports
Continue the direct low-confidence Oromo translation with voting, Planning Poker, dependencies, board administration, and import controls while preserving exact placeholders and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:40:44 +03:00
Lauri Ojansivu a4619e18f0 Translate Oromo board and card views
Continue the direct low-confidence Oromo translation with board visibility and view controls, archive warnings, and card metadata while preserving exact placeholders and existing correct-language translations.\n\nThanks to xet7 !
2026-08-27 20:39:35 +03:00
Lauri Ojansivu 15ca581a22 Translate Oromo administration and archives
Add direct Oromo translations for administration, announcements, offline recovery, archives, templates, attachments, watching, backgrounds, board summaries, members, assignees, and visibility while preserving existing correct-language translations, terminology, markup, and source placeholders. These translations are low-confidence and welcome review by an Oromo speaker.\n\nThanks to xet7 !
2026-08-27 20:36:45 +03:00
Lauri Ojansivu 5d8f1c520b Translate Oromo board and list controls
Add direct Oromo translations for workspaces, board selection, Home boards, dates, templates, list widths, shortcuts, swimlane height, checklists, covers, labels, and members while preserving existing correct-language translations, established terminology, limits, and source placeholders. These translations are low-confidence and welcome review by an Oromo speaker.\n\nThanks to xet7 !
2026-08-27 20:35:01 +03:00
Lauri Ojansivu abf08cecca Translate Oromo movement and workspaces
Add direct Oromo translations for card movement, member and checklist activity, comments, dates, board groups, and workspace controls while preserving existing correct-language translations, established terminology, technical literals, and every source placeholder. These translations are low-confidence and welcome review by an Oromo speaker.\n\nThanks to xet7 !
2026-08-27 20:33:10 +03:00
Lauri Ojansivu 38342b8e75 Start Oromo activity translations
Add direct Oromo translations for deletion, membership, comments, dates, attachments, subtasks, labels, checklists, creation, archiving, and imports while preserving existing correct-language translations, established terminology, and every source placeholder. These translations are low-confidence and welcome review by an Oromo speaker.\n\nThanks to xet7 !
2026-08-27 20:31:33 +03:00
Lauri Ojansivu 9fa5098003 Complete Chichewa translations
Translate the final Chichewa event, integrity, scoped import and export, and numeric search strings while preserving existing correct-language translations, formats, search markup, and every source placeholder. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:29:41 +03:00
Lauri Ojansivu 0da780388b Translate Chichewa accounts and problem reports
Add direct Chichewa translations for account login, repositories, validation, problem reports, card repair, CPU metrics, and event metadata while preserving existing correct-language translations, technical abbreviations, cross-label consistency, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:27:55 +03:00
Lauri Ojansivu aa34ecde89 Translate Chichewa migration monitoring
Add direct Chichewa translations for memory and storage migration, resource thresholds, progress, monitoring, schedules, minicard display, and system totals while preserving existing correct-language translations, backend names, units, ranges, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:26:24 +03:00
Lauri Ojansivu ed48b0142b Translate Chichewa migration steps and jobs
Add direct Chichewa translations for board-migration steps, conversion, scheduling, monitoring, storage statistics, minicard visibility, and job queues while preserving existing correct-language translations, identifiers, localized generated titles, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:25:03 +03:00
Lauri Ojansivu 819479c656 Translate Chichewa board integrity migrations
Add direct Chichewa translations for comprehensive board checks, duplicate lists, lost-card recovery, archives, URL repairs, migration confirmations, progress, and validation while preserving existing correct-language translations, identifiers, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:23:27 +03:00
Lauri Ojansivu 7151819cb9 Translate Chichewa storage migrations
Add direct Chichewa translations for Google Cloud and GridFS storage, migration controls, S3 settings, scheduled board operations, writable paths, and attachment and board migrations while preserving existing correct-language translations, provider names, protocol literals, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:21:44 +03:00
Lauri Ojansivu 6f6579c436 Translate Chichewa cloud backups
Add direct Chichewa translations for scheduled backups, restore modes, Google Cloud credentials, cloud-provider console guidance, secret states, connection tests, and Azure storage while preserving existing correct-language translations, formats, console labels, navigation structure, providers, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:20:12 +03:00
Lauri Ojansivu 4e226933e1 Translate Chichewa security and backups
Add direct Chichewa translations for Sandstorm cleanup, adaptive card loading, rendering security, feature controls, user anonymization, and backups while preserving existing correct-language translations, markup, identifiers, providers, paths, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:18:30 +03:00
Lauri Ojansivu a3a38f7ef5 Translate Chichewa scheduled migrations
Add direct Chichewa translations for scheduled jobs, storage configuration, database migration, and Sandstorm migration status while preserving existing correct-language translations, URLs, environment variables, commands, paths, product names, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:16:13 +03:00
Lauri Ojansivu 387c4d6769 Translate Chichewa account lockout settings
Add direct Chichewa translations for accessibility, login lockout administration, scheduled jobs, storage paths, and board maintenance operations while preserving existing correct-language translations and every source placeholder. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:14:27 +03:00
Lauri Ojansivu 5a0caa64df Translate Chichewa board and support settings
Add direct Chichewa translations for board status, upload restrictions, custom translations, checklist display, support, and accessibility while preserving existing correct-language translations, standards, formats, units, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:13:04 +03:00
Lauri Ojansivu 2f438ca3ae Translate Chichewa storage maintenance
Add direct Chichewa translations for bulk attachment movement, location repair, default storage, file statistics, MongoDB Compact, and metadata while preserving existing correct-language translations, backend names, maintenance terms, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:11:30 +03:00
Lauri Ojansivu b9bbac1549 Translate Chichewa teams and attachment storage
Add direct Chichewa translations for team and organization membership, Node memory diagnostics, checklists, and attachment storage actions while preserving existing correct-language translations, storage names, examples, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:09:55 +03:00
Lauri Ojansivu 24c469ea4c Translate Chichewa recovery and support reports
Add direct Chichewa translations for API activity, data recovery, waiting animations, deletion warnings, and support tickets while preserving existing correct-language translations, environment settings, product names, animation identifiers, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:08:33 +03:00
Lauri Ojansivu 67dbc5ee54 Translate Chichewa maps and reports
Add direct Chichewa translations for maps, server troubleshooting, string templates, system reports, impersonation, recovery, and office locations while preserving existing correct-language translations, commands, entities, technical literals, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:07:13 +03:00
Lauri Ojansivu 6e47462c11 Translate Chichewa dependencies and backgrounds
Add direct Chichewa translations for sorting, completion, stickers, card dependencies, dependency imports, board backgrounds, and locations while preserving existing correct-language translations, format names, sort notation, and every source placeholder. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:05:54 +03:00
Lauri Ojansivu 86d5a6c899 Translate Chichewa global search guidance
Add direct Chichewa translations for query predicates, validation, paging, and global-search operator guidance while preserving existing correct-language translations, single-token syntax, metavariables, Markdown structure, and every source placeholder. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:04:32 +03:00
Lauri Ojansivu 00c1bd7e80 Translate Chichewa global search tokens
Add direct Chichewa translations for missing-item errors, result counts, and global-search operators and predicates while preserving existing correct-language translations, single-token query syntax, and every source placeholder. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:02:50 +03:00
Lauri Ojansivu b33cb1caad Translate Chichewa shared templates and card views
Add direct Chichewa translations for linked-list guidance, tasks, domains, shared templates, card views, global search, and missing-item errors while preserving existing correct-language translations, markup, literals, and source placeholders. Correct visible English domain terminology directly to Chichewa. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 20:01:37 +03:00
Lauri Ojansivu 4c8e582a34 Translate Chichewa roles and notification settings
Add direct Chichewa translations for multi-window cards, keyboard submission, account dialogs, notifications, board-role capabilities, weekdays, voting, and linked-card guidance while preserving existing correct-language translations, keyboard literals, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:59:41 +03:00
Lauri Ojansivu b1db214594 Translate Chichewa activity and layout settings
Add direct Chichewa translations for custom body HTML, authentication, date and reminder activity, deletion controls, drag handles, resizing, and editor submission while preserving existing correct-language translations, markup, multiline structure, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:58:28 +03:00
Lauri Ojansivu 0edfeca548 Translate Chichewa rule details and customization
Add direct Chichewa translations for detailed rule actions, authentication, custom HTML, web manifests, asset links, layout, and card counters while preserving existing correct-language translations, sample structure, technical literals, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:57:13 +03:00
Lauri Ojansivu ed21e928eb Translate Chichewa automation fragments
Add direct Chichewa translations for composable automation fragments covering movement, archive, labels, members, attachments, checklists, colors, and email while preserving existing correct-language translations and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:55:50 +03:00
Lauri Ojansivu 9fb5bd5f6e Translate Chichewa scheduled automation
Add direct Chichewa translations for visual workflow imports, scheduled and button triggers, recurrence, due-date conditions, sorting, completion, movement, and relative dates while preserving existing correct-language translations, product names, parameters, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:54:42 +03:00
Lauri Ojansivu 46088f597e Translate Chichewa automation rules
Add direct Chichewa translations for activity messages, visual rule construction, triggers, scheduling, and rule import and export while preserving existing correct-language translations and every source placeholder. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:53:31 +03:00
Lauri Ojansivu 274ee00e63 Translate Chichewa card hierarchy settings
Add direct Chichewa translations for card dates, deletion controls, subtasks, minicard content, parent paths, and label activity while preserving existing correct-language translations and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:52:16 +03:00
Lauri Ojansivu 660070ea5b Translate Chichewa runtime and tenancy settings
Add direct Chichewa translations for runtime diagnostics, field display, visibility, and organization and team tenancy settings while preserving existing correct-language translations, environment variables, domains, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:51:01 +03:00
Lauri Ojansivu 162081e4a2 Translate Chichewa transfer and email settings
Add direct Chichewa translations for transfer limits, registration, SMTP email, invitations, webhooks, and runtime labels while preserving existing correct-language translations, message structure, technical literals, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:49:45 +03:00
Lauri Ojansivu 92b094fdb4 Translate Chichewa tracking and branding
Add direct Chichewa translations for starred boards, time tracking, uploads, custom branding, watching, templates, and WIP errors while preserving existing correct-language translations and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:48:33 +03:00
Lauri Ojansivu 8c306c1434 Translate Chichewa search and shortcuts
Add direct Chichewa translations for visibility guidance, member removal, search, WIP limits, shortcuts, sidebars, and default-board behavior while preserving existing correct-language translations, markup, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:47:13 +03:00
Lauri Ojansivu 17dba31e2b Translate Chichewa board and selection actions
Add direct Chichewa translations for board departure, list archiving, settings, selection actions, roles, and notifications while preserving existing correct-language translations and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:46:05 +03:00
Lauri Ojansivu e429e1cdf1 Translate Chichewa Trello import workflow
Add direct Chichewa translations for Trello API imports, job controls, member mapping, validation, and labels while preserving existing correct-language translations and source literals. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:44:56 +03:00
Lauri Ojansivu 9805b45b68 Translate Chichewa filters and board imports
Add direct Chichewa translations for advanced filters and board-import guidance while preserving existing correct-language translations, technical literals, and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:43:40 +03:00
Lauri Ojansivu 68e1156c0a Translate Chichewa exports and filters
Add direct Chichewa translations for validation errors, card exports, sorting, and filters while preserving existing correct-language translations and source placeholders. These translations are low-confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:42:16 +03:00
Lauri Ojansivu e92eb653cf Translate Chichewa account emails and validation
Translate the next 50 Chichewa placeholders covering text and date fields, permanent deletion, WIP limits, card dates and notifications, enrollment, invitation, password-reset and verification email templates, scrollbars, authorization, and import errors. Preserve every account, board, URL, and format token while advancing the validated checkpoint.\n\nOnly correct-language Chichewa values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:40:48 +03:00
Lauri Ojansivu bd1a3e90a8 Translate Chichewa roles and custom fields
Translate the next 50 Chichewa placeholders covering remaining colors, comment and read-only roles, worker permissions, deletion confirmations, swimlane movement, clipboard links, bulk card-template JSON, labels, and custom-field types and options. Preserve valid JSON structure and field names while advancing the validated checkpoint.\n\nOnly correct-language Chichewa values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:39:22 +03:00
Lauri Ojansivu 4ea90ff523 Translate Chichewa card aging and interface colors
Translate the next 50 Chichewa placeholders covering settings and subtasks, starred boards and pages, automatic list width, clipboard input, three-tier card aging, keyboard movement and dialog navigation, board restoration guidance, and interface colors. Preserve numeric tier semantics while advancing the validated checkpoint.\n\nOnly correct-language Chichewa values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:38:04 +03:00
Lauri Ojansivu 7c87254a29 Translate Chichewa member mapping and themes
Translate the next 50 Chichewa placeholders covering member, sticker, and invitation dialogs, list sorting and archive restoration, linked cards and boards, secure imported-member mapping, themes, fonts, text colors, avatars, languages, and permissions. Preserve authentication and numeric preview literals while advancing the validated checkpoint.\n\nOnly correct-language Chichewa values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:36:51 +03:00
Lauri Ojansivu 017ead12bb Translate Chichewa voting and import dialogs
Translate the next 50 Chichewa placeholders covering card membership and start dates, attachments, custom fields, voting and Planning Poker, dependencies, organization and team membership, deletion dialogs, domains, and checklist, swimlane, list, card, and board imports. Preserve product and technical terminology while advancing the validated checkpoint.\n\nOnly correct-language Chichewa values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:35:43 +03:00
Lauri Ojansivu e9c41cc5fd Translate Chichewa board views and card editing
Translate the next 50 Chichewa placeholders covering board visibility, drag-and-drop, backgrounds, titles, watching, views, desktop and mobile modes, zoom, calendar navigation, archive guidance, and card dates and editing. Preserve workspace, format, percentage, and HTML tokens while advancing the validated checkpoint.\n\nOnly correct-language Chichewa values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:34:40 +03:00
Lauri Ojansivu 70cf02f550 Translate Chichewa administration and archives
Translate the next 50 Chichewa placeholders covering administrator announcements and permissions, public-board labels, connection recovery, board and selection archiving, templates, attachments, background images, All Boards display, and board member and assignee summaries. Preserve count, size, format, URL, and HTML tokens while advancing the validated checkpoint.\n\nOnly correct-language Chichewa values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:33:19 +03:00
Lauri Ojansivu ee5159e73a Translate Chichewa board and list controls
Translate the next 50 Chichewa placeholders covering workspace deletion and board selection, Home-board controls, due and end dates, list-width preferences, keyboard shortcuts, swimlane heights, subtasks, checklists, covers, labels, and members. Preserve activity tokens and numeric constraints while advancing the validated checkpoint.\n\nOnly correct-language Chichewa values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:32:10 +03:00
Lauri Ojansivu e97a9b5105 Translate Chichewa movement and workspaces
Translate the next 50 Chichewa placeholders covering card movement and restoration, compact activity summaries, checklist and comment history, received and start dates, and All Boards workspace names, menus, and settings. Preserve every old and new location, activity, checklist, and format token while advancing the validated checkpoint.\n\nOnly correct-language Chichewa values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:30:53 +03:00
Lauri Ojansivu b6659fdb6f Start Chichewa activity translations
Translate the first 50 Chichewa placeholders covering activity history for titles, descriptions, boards, lists, swimlanes, cards, attachments, labels, checklists, comments, custom fields, and members, together with organization and team restrictions. Preserve every activity and format token while advancing the validated checkpoint.\n\nOnly correct-language Chichewa values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Chichewa speaker.\n\nThanks to xet7 !
2026-08-27 19:29:36 +03:00
Lauri Ojansivu c69db2e833 Complete Northern Sotho translations
Translate the final 17 Northern Sotho placeholders covering diagnostic event details and IP addresses, filesystem integrity, scoped export and import guidance, and the numeric card-search operator. Preserve every format, search, protocol, and file-extension token, complete the locale, and advance the validated checkpoint to Chichewa.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:28:13 +03:00
Lauri Ojansivu c62146b9cc Translate Northern Sotho account and problem reporting
Translate the next 50 Northern Sotho placeholders covering repositories, account and OTP access, login validation, Problems status and guidance, broken-card repair, CPU metrics, and diagnostic event fields. Preserve authentication, API, CPU, and repair-count identifiers while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:27:15 +03:00
Lauri Ojansivu 283ae6c1dc Translate Northern Sotho migration monitoring
Translate the next 50 Northern Sotho placeholders covering attachment migration targets, batch and CPU thresholds, migration logs and lifecycle controls, monitoring, pagination, minicard display, and storage and operation totals. Preserve backend names, units, percentages, and numeric ranges while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:26:08 +03:00
Lauri Ojansivu 139c361a75 Translate Northern Sotho repair steps and job metrics
Translate the next 50 Northern Sotho placeholders covering board-repair steps, conversion status, CPU and storage metrics, recurring schedules, export monitoring, filesystem and GridFS totals, and job details and queues. Preserve URL, identifier, CPU, and backend names while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:25:08 +03:00
Lauri Ojansivu 25fca061df Translate Northern Sotho board integrity repair
Translate the next 50 Northern Sotho placeholders covering comprehensive board integrity checks, duplicate-list cleanup, lost-card and archive restoration, missing-list repair, avatar and file URL repair, confirmations, and migration progress. Preserve structure-field, URL, and identifier names while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:24:00 +03:00
Lauri Ojansivu 776e6e9786 Translate Northern Sotho storage migration controls
Translate the next 50 Northern Sotho placeholders covering Google Cloud Storage and GridFS targets, migration lifecycle controls, CollectionFS movement, AWS S3 and MinIO security settings, scheduled board operations, writable paths, and attachment monitoring. Preserve every backend, region, protocol, and security identifier while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:22:35 +03:00
Lauri Ojansivu 16d5310cba Translate Northern Sotho backups and cloud setup
Translate the next 50 Northern Sotho placeholders covering scheduled backups and restore modes, Google Cloud credentials and permissions, and detailed AWS S3, Azure, and GCS configuration paths, secret handling, and connection tests. Preserve provider labels, identifiers, JSON names, formats, roles, and service names while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:21:26 +03:00
Lauri Ojansivu 34d70bc9bc Translate Northern Sotho security and backup controls
Translate the next 50 Northern Sotho placeholders covering Sandstorm cleanup, adaptive card loading, safe link and code rendering, import and export privacy, anonymization, and streamed backup scopes. Preserve HTML syntax, environment variables, product names, identifiers, and exact backup paths while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:20:03 +03:00
Lauri Ojansivu a0e5666411 Translate Northern Sotho jobs and database migration
Translate the next 50 Northern Sotho placeholders covering scheduled jobs and migration recovery, filesystem, S3 and Azure storage, MongoDB and FerretDB text-data migration, and Sandstorm migration status. Preserve every URL, port, environment variable, command, path, backend name, and database token while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:18:08 +03:00
Lauri Ojansivu 7a65129b29 Translate Northern Sotho login protection and schedules
Translate the next 50 Northern Sotho placeholders covering accessibility content, brute-force login protection, locked-user administration, people status, attachment and avatar paths, and scheduled board operations. Preserve security semantics and scheduling terminology while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:16:50 +03:00
Lauri Ojansivu 2e57540dd2 Translate Northern Sotho upload and support settings
Translate the next 50 Northern Sotho placeholders covering board status, upload rules, custom translations, checklist display, support, and accessibility controls. Preserve the workspace token, standards, file formats, and product names while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:15:43 +03:00
Lauri Ojansivu 3287690b0b Translate Northern Sotho storage repair and compaction
Translate the next 50 Northern Sotho placeholders covering bulk attachment movement, storage-location repair, file counts and identifiers, and MongoDB compaction status and guidance. Preserve storage, database, operation, and identifier names while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:14:43 +03:00
Lauri Ojansivu 3af3d648cf Translate Northern Sotho teams and attachment storage
Translate the next 50 Northern Sotho placeholders covering team and organization membership, Node memory diagnostics, legal notices, checklist and subtask actions, and attachment storage movement. Preserve product names, storage names, URLs, and technical memory terminology while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:13:31 +03:00
Lauri Ojansivu 9c63d84707 Translate Northern Sotho recovery and support reports
Translate the next 50 Northern Sotho placeholders covering office and API reporting, database recovery, loading indicators, card display controls, organization and team safeguards, support tickets, and request statuses. Preserve API configuration, protocol, product, and spinner identifiers while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:12:26 +03:00
Lauri Ojansivu 7813774d45 Translate Northern Sotho maps and reports
Translate the next 50 Northern Sotho placeholders covering map detection, server troubleshooting, board activity, custom-field string templates, administration reports, and office-login summaries. Preserve the template placeholder, HTML entities, shell commands, and protocol names while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:11:23 +03:00
Lauri Ojansivu 27d1358ad1 Translate Northern Sotho dependencies and locations
Translate the next 50 Northern Sotho placeholders covering card and board sorting, completion, stickers, card dependencies and their imports, board backgrounds, and card locations. Preserve dependency counts, the background-size template token, file formats, and sort arrows while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:10:15 +03:00
Lauri Ojansivu 9a088c22b4 Translate Northern Sotho search instructions
Translate the next 50 Northern Sotho placeholders covering search predicates, operator validation, pagination, and detailed global-search syntax, status, field, sorting, and combination guidance. Preserve every replaceable token and literal query example while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:09:18 +03:00
Lauri Ojansivu 253995b414 Translate Northern Sotho global search terms
Translate the next 50 Northern Sotho placeholders covering missing-result and card counts plus compact global-search operators and predicates for boards, users, dates, attachments, checklists, and card metadata. Keep every operator free of spaces and preserve all count and format tokens while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:07:56 +03:00
Lauri Ojansivu 0a92347620 Translate Northern Sotho templates and card views
Translate the next 50 Northern Sotho placeholders covering linked-list safeguards, shared-template domains, personal and due-card views, global search, and missing board, swimlane, list, and label messages. Preserve every format token and literal domain example while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:06:35 +03:00
Lauri Ojansivu 89e6976913 Translate Northern Sotho roles and notifications
Translate the next 50 Northern Sotho placeholders covering multi-window card editing, organization, team and user dialogs, notification filters, board-role permissions, weekdays, status labels, and linked-card deletion guidance. Preserve keyboard names and permission semantics while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:05:35 +03:00
Lauri Ojansivu 7bd2655214 Translate Northern Sotho activity and layout controls
Translate the next 50 Northern Sotho placeholders covering board-member lists, custom body HTML, authentication and duplication controls, activity dates and reminders, destructive safeguards, selection placement, and resize controls. Preserve literal HTML and every activity and format token while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:04:38 +03:00
Lauri Ojansivu e06f3777b3 Translate Northern Sotho rule actions and metadata
Translate the next 50 Northern Sotho placeholders covering concrete card, checklist, swimlane, and date actions, authentication labels, custom product and web metadata, and layout controls. Keep literal HTML, JSON, filenames, and product names intact while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:03:34 +03:00
Lauri Ojansivu a5abdc0af6 Translate Northern Sotho rule grammar
Translate the next 50 Northern Sotho placeholders covering visual-rule grammar for card movement and archiving, labels, members, attachments, checklists, and email actions. Keep the phrases composable in the rule builder while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:02:42 +03:00
Lauri Ojansivu 0ac66e6e69 Translate Northern Sotho workflow schedules
Translate the next 50 Northern Sotho placeholders covering imported visual workflows, scheduled and button triggers, due-date conditions, list sorting, completion, relative dates, and rule fragments. Preserve the count token and literal configuration and product names while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:01:46 +03:00
Lauri Ojansivu f09057ef17 Translate Northern Sotho visual rules
Translate the next 50 Northern Sotho placeholders covering label, attachment, and custom-field activity plus visual rule editing, triggers, and JSON, CSV, and Trello Butler transfers. Preserve every activity, time, and count token while advancing the validated checkpoint.\n\nOnly correct-language Northern Sotho values are protected; wrong-language values remain eligible for direct repair. These translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 19:00:49 +03:00
Lauri Ojansivu 3587cbe7ec Translate Northern Sotho card and subtask settings
Translate the next 50 Northern Sotho placeholders covering card receipt and end dates, colors, board and notification deletion safeguards, subtask and card settings, minicard display, and parent-card paths. Preserve the board and activity format tokens while advancing the validated checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:59:46 +03:00
Lauri Ojansivu 076fb7fd57 Translate Northern Sotho diagnostics and tenancy
Translate the next 50 Northern Sotho placeholders covering database and operating-system diagnostics, time units, custom-field display, account controls, board visibility, and organization and team tenancy. Preserve configuration names, example domains, and product names while advancing the validated checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:58:21 +03:00
Lauri Ojansivu 2b2c297d8d Translate Northern Sotho transfer limits and webhooks
Translate the next 50 Northern Sotho placeholders covering attachment and API transfer limits, avatar upload controls, registration, invitations, SMTP, webhooks, card-title filtering, and runtime labels. Preserve invitation placeholders and advance the validated checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:55:05 +03:00
Lauri Ojansivu 71848542f0 Document HostnameBleed in upcoming release
Add the CodeQL hostname-regexp findings, exact-comparison fix, whole-tree negative coverage, and test-only runtime boundary to the upcoming release notes.\n\nThanks to GitHub CodeQL code scanning and xet7 !
2026-08-27 18:25:48 +03:00
Lauri Ojansivu b5a36bd3af Make hostname examples exact in translation coverage
Replace dynamic regular expressions for the Marathi tenant hostname examples with literal containment checks, so dots cannot act as wildcards. Add positive, negative, and repository-wide HostnameBleed coverage for CodeQL alerts #435 and #436, and record why this test-only finding has no runtime Problems event.\n\nThanks to GitHub CodeQL code scanning and xet7 !
2026-08-27 18:24:40 +03:00
Lauri Ojansivu 7388255f5f Translate Northern Sotho tracking and branding
Translate the next 50 Northern Sotho placeholders covering starred boards, time tracking, numeric shortcuts, uploads, custom branding, URL schemes, watching, templates, and WIP-limit errors. Preserve numeric configuration values and advance the guarded checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:22:30 +03:00
Lauri Ojansivu 35857168e8 Translate Northern Sotho privacy and shortcuts
Translate the next 50 Northern Sotho placeholders covering watched notifications, page and board privacy, previews, member removal, card-description rescue, search, WIP limits, keyboard shortcuts, sidebars, signup, and default boards. Preserve the login link and member identity tokens while advancing the validated checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:21:08 +03:00
Lauri Ojansivu b8f931fa2b Translate Northern Sotho board roles and selection
Translate the next 50 Northern Sotho placeholders covering board departure safeguards, archive actions, settings, imports, multi-selection, muted notifications, and constrained normal roles. Preserve the board-title token and advance the validated translation checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:20:03 +03:00
Lauri Ojansivu b732586cb8 Translate Northern Sotho Trello API imports
Translate the next 50 Northern Sotho placeholders covering Trello API credentials, workspace placement, import selection and lifecycle, member mapping, validation, shortcuts, and labels. Preserve the API URL, year example, and label format token while advancing the guarded checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:18:48 +03:00
Lauri Ojansivu eebf705569 Translate Northern Sotho filters and board imports
Translate the next 50 Northern Sotho placeholders covering advanced filters, imported-member states, and board import guidance and failures for the supported source formats. Preserve filter operators, regular expressions, API paths, source placeholders, and file extensions while advancing the validated checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:17:42 +03:00
Lauri Ojansivu 2608433fc1 Translate Northern Sotho exports and filters
Translate the next 50 Northern Sotho placeholders covering account conflicts, PDF and Excel card exports, attachment metadata, disk-space errors, list sorting, and date, label, and member filters. Preserve product and format terminology and advance the validated translation checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:16:04 +03:00
Lauri Ojansivu 79d17d5e42 Translate Northern Sotho email and validation
Translate the next 50 Northern Sotho placeholders covering permanent deletion, WIP limits, dates, notifications, account emails, scrollbars, authorization failures, import validation, and linked-card errors. Preserve every email placeholder and advance the validated translation checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:15:13 +03:00
Lauri Ojansivu aa642980c0 Translate Northern Sotho roles and custom fields
Translate the next 50 Northern Sotho placeholders covering constrained roles, deletion confirmations, clipboard actions, bulk template JSON, labels, and custom-field types and options. Validate the translated JSON example and advance the guarded translation checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:14:03 +03:00
Lauri Ojansivu 370431e20a Translate Northern Sotho navigation and colors
Translate the next 50 Northern Sotho placeholders covering settings, starring, automatic list width, card aging, movement and dialog navigation, restoration guidance, and interface colors. Preserve the three-tier setting and advance the validated translation checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:13:10 +03:00
Lauri Ojansivu feb6b67079 Translate Northern Sotho members and appearance
Translate the next 50 Northern Sotho placeholders covering dialogs, archive restoration, linked items, imported-member mapping, themes, fonts, colors, avatars, languages, and permissions. Preserve the numeric font sample and established authorization terminology while advancing the validated checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:11:58 +03:00
Lauri Ojansivu ac8e36f2c1 Translate Northern Sotho voting and imports
Translate the next 50 Northern Sotho placeholders covering card membership, voting and Planning Poker, dependencies, organization and team membership, domains, backgrounds, and imports. Advance the validated translation checkpoint while preserving established kanban terminology.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:11:04 +03:00
Lauri Ojansivu 10cf7612a1 Translate Northern Sotho board views and archives
Translate the next 50 Northern Sotho placeholders covering board visibility and appearance, view modes and zoom, calendar navigation, archive guidance, and card editing controls. Preserve workspace, format, percentage, and HTML tokens and advance the validated translation checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:09:51 +03:00
Lauri Ojansivu d88d3ccb6e Translate Northern Sotho board administration
Translate the next 50 Northern Sotho placeholders covering administrator announcements, board visibility and archiving, attachments, background images, All Boards display settings, and member summaries. Preserve count, size, format, and HTML tokens and advance the validated translation checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:08:38 +03:00
Lauri Ojansivu 76bbcaf55d Translate Northern Sotho workspace and layout controls
Translate the next 50 Northern Sotho placeholders covering workspace and Home-board actions, board selection, list and swimlane sizing, keyboard shortcuts, and checklist and card controls. Preserve format tokens and numeric constraints, and advance the validated translation checkpoint.\n\nThese direct translations are low confidence and welcome review by a Northern Sotho speaker.\n\nThanks to xet7 !
2026-08-27 18:07:26 +03:00
Lauri Ojansivu 9ad8acc38e Translate Northern Sotho activity and workspaces
Fill 50 Northern Sotho placeholders covering card moves and restoration, detailed activity and checklist history and All Boards workspace names, menus and settings. Preserve every old and new board, list, swimlane, card, checklist and format token, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Northern Sotho speaker.

Thanks to xet7 !
2026-08-27 18:02:32 +03:00
Lauri Ojansivu d0168c8cb4 Start completing Northern Sotho translations
Fill 50 Northern Sotho placeholders covering activity history for boards, lists, swimlanes, cards, attachments, labels, checklists, comments, custom fields and members, plus organization and team restrictions. Preserve all format and entity tokens, add locale-wide token and tag regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Northern Sotho speaker.

Thanks to xet7 !
2026-08-27 18:00:56 +03:00
Lauri Ojansivu 942b37d9b3 Complete Nepali translations
Fill the final 17 Nepali placeholders covering diagnostic addresses, filesystem integrity, export selection, import feedback and numeric card search. Preserve WeKan, source names, file suffixes, IP variants and the search token and tags, extend locale-wide regression coverage and advance the recorded checkpoint to Northern Sotho. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:59:12 +03:00
Lauri Ojansivu b69b625a99 Translate Nepali repositories and diagnostics
Fill 50 Nepali placeholders covering repositories, account and OTP access, login validation, Problems status and guidance, broken-card repair, CPU metrics and diagnostic event fields. Preserve named technical literals and repair counters, extend regression coverage and record the final checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:58:14 +03:00
Lauri Ojansivu 7d21f92b13 Translate Nepali migration resource controls
Fill 50 Nepali placeholders covering attachment migration targets, batch and resource thresholds, migration logs and lifecycle controls, monitoring, navigation, minicard display and storage and operation totals. Preserve storage names and all numeric resource ranges, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:56:34 +03:00
Lauri Ojansivu 4dc81aaf9f Translate Nepali migration monitoring
Fill 50 Nepali placeholders covering board-repair steps, conversion status, CPU and storage metrics, schedules, export monitoring, filesystem and GridFS totals and job details and queues. Preserve URL, ID, CPU, GridFS and schedule literals, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:54:48 +03:00
Lauri Ojansivu 0952367444 Translate Nepali board integrity repairs
Fill 50 Nepali placeholders covering comprehensive board migration, duplicate-list cleanup, lost-card and archive restoration, missing-list repair, avatar and file URL correction, confirmations, progress and board-structure repair steps. Preserve storage identifiers and URL and ID literals, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:53:19 +03:00
Lauri Ojansivu 1e3cfdcf8d Translate Nepali cloud storage migrations
Fill 50 Nepali placeholders covering Google Cloud Storage and GridFS storage, migration lifecycle controls, CollectionFS moves, AWS S3 and MinIO credentials and SSL/TLS, scheduled board operations, writable paths and attachment and board migration settings. Preserve storage and security literals, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:50:45 +03:00
Lauri Ojansivu 51372d12c0 Translate Nepali cloud backup settings
Fill 50 Nepali placeholders covering scheduled backup frequency and restore modes, Google Cloud Storage credentials and permissions and detailed AWS S3, compatible S3, Azure and GCS configuration paths, secret handling and connection tests. Preserve time and range formats, permission fields, provider names and storage literals, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:49:03 +03:00
Lauri Ojansivu 790ee853db Translate Nepali privacy and backups
Fill 50 Nepali placeholders covering Sandstorm raw-database cleanup, automatic and lazy card loading, link and code rendering safeguards, global import, export, avatar, activity, notification and watch controls, user anonymization and scoped streamed backups. Preserve markup, environment variables, providers and backup paths, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:46:52 +03:00
Lauri Ojansivu 259d748e02 Translate Nepali database migrations
Fill 50 Nepali placeholders covering scheduled-job and migration controls and errors, filesystem, S3 and Azure storage settings, MongoDB and FerretDB text-data migration and Sandstorm database and file migration status. Preserve endpoints, ports, environment variables, commands, database versions and paths, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:43:58 +03:00
Lauri Ojansivu 831de8ad5c Translate Nepali lockout and scheduled jobs
Fill 50 Nepali placeholders covering accessibility content, brute-force login protection and locked-user administration, people status filters, scheduled jobs, attachment and avatar paths and scheduled board archive, backup and cleanup operations. Extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:42:05 +03:00
Lauri Ojansivu 31efe8eac9 Translate Nepali upload and support settings
Fill 50 Nepali placeholders covering board time status, upload progress and limits, passwords, Mongo sessions, PDF preview fallback, workspace dragging, custom translations, ISO week display, ZIP board imports, checklist collapsing, support and accessibility pages. Preserve technical literals and workspace tokens, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:40:31 +03:00
Lauri Ojansivu 3e87821914 Translate Nepali storage repair and compact
Fill 50 Nepali placeholders covering bulk attachment and avatar storage moves, file-location repair, default storage, file statistics and identifiers and MongoDB GridFS Compact guidance, progress and results. Preserve storage, database, replica-set and runtime terminology, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:38:43 +03:00
Lauri Ojansivu fed562e1da Translate Nepali teams metrics and storage
Fill 50 Nepali placeholders covering team and organization membership, registration invitations and email domains, Node heap and memory metrics, legal notices, checklist movement and copying and attachment actions and filesystem, GridFS and S3 storage moves. Preserve runtime and storage literals, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:37:13 +03:00
Lauri Ojansivu f8ab8d3bc3 Translate Nepali API recovery and tickets
Fill 50 Nepali placeholders covering office activity, REST API use, automatic recovery and maintenance, swimlane copying, card display and wait spinners, organization and team deletion safeguards and ticket and help request fields and states. Preserve API configuration, database and mail-copy literals, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:35:34 +03:00
Lauri Ojansivu 84b4b7b6aa Translate Nepali maps and Problems reports
Fill 50 Nepali placeholders covering map coordinates and detection, server error troubleshooting, sorting, board activities, swimlane movement, string templates, invisible-filename filtering, Problems reports, impersonation, recovery and office login locations. Preserve diagnostic commands, template tokens, HTML entities and technical literals, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:33:59 +03:00
Lauri Ojansivu 7d1a555b3c Translate Nepali dependencies and backgrounds
Fill 50 Nepali placeholders covering board and card sorting, completion, stickers, dependency relationships, filtering and JSON or SVG imports, board background upload and deletion and card locations. Preserve sort arrows, structured formats, import counters and size tokens, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:32:03 +03:00
Lauri Ojansivu f778a3d387 Translate Nepali global search guidance
Fill 50 Nepali placeholders covering remaining search predicates, operator validation, pagination and detailed global-search instructions for board structure, people, organizations, teams, dates, status, field existence, sorting, limits and AND and OR combinations. Preserve every operator, value, field and period token, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:30:23 +03:00
Lauri Ojansivu 6839b54ac6 Translate Nepali global search terms
Fill 50 Nepali placeholders covering username, comment, organization and team search errors, result counts and pagination and global search operators and predicates for board structure, people, status, dates, text, attachments and checklists. Preserve all result and pagination tokens, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:28:20 +03:00
Lauri Ojansivu fc15add218 Translate Nepali domains and card views
Fill 50 Nepali placeholders covering linked-list deletion protection, checklist visibility, tasks, domains and shared templates, My Cards and Due Cards views and sorting, global search scope and board, swimlane, list and label not-found errors. Preserve domain examples and all identifier format tokens, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:27:13 +03:00
Lauri Ojansivu 15cb1142d8 Translate Nepali roles and editor shortcuts
Fill 50 Nepali placeholders covering multi-window cards, Enter and modifier-key editing, organization, team and user forms, notification actions, rename and board-role permissions and status, week-start days, ownership and activity metadata and linked-card deletion guidance. Preserve modifier-key combinations, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:25:12 +03:00
Lauri Ojansivu 25d6e5bd2f Translate Nepali authentication and reminders
Fill 50 Nepali placeholders covering custom body HTML, authentication errors and OIDC labels, board duplication and entity totals, swimlane deletion, card placement, due reminders and mentions, account, team and organization deletion and desktop and menu resizing. Preserve body markup and all activity tokens, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:23:21 +03:00
Lauri Ojansivu bd1da69ada Translate Nepali automation and head settings
Fill 50 Nepali placeholders covering automation email, archive, label, member, checklist, swimlane, date-field and card-link actions, authentication, custom product names, HTML head metadata, web manifests, asset links and layout controls. Preserve structured examples and HTML and JSON literals, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:21:32 +03:00
Lauri Ojansivu 75513fb5bf Translate Nepali rule conditions and actions
Fill 50 Nepali placeholders covering rule-builder fragments for card, label, member, attachment, checklist and checklist-item conditions, card movement and restoration, labels, members, colors and checklist actions and email and movement descriptions. Extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:19:55 +03:00
Lauri Ojansivu d068455a81 Translate Nepali scheduled automation
Fill 50 Nepali placeholders covering n8n and Node-RED workflow imports, scheduled and button triggers, one-time, daily, weekday, weekly and monthly schedules, due-date and list-duration conditions, sorting, completion, bulk movement and relative-date actions. Preserve product names, count tokens and symbolic schedule notation, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:18:23 +03:00
Lauri Ojansivu 9a72c3a235 Translate Nepali visual automation rules
Fill 50 Nepali placeholders covering label, attachment and custom-field activity, visual rule building, card, label, member, checklist, attachment and daily triggers, JSON and CSV rule transfer and Trello Butler command mapping. Preserve activity, time and count tokens and technical formats, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:17:11 +03:00
Lauri Ojansivu 403bf220cc Translate Nepali card dates and subtasks
Fill 50 Nepali placeholders covering received and end dates, card, list and swimlane colors, assignment sources, destructive confirmations, duplicate-list cleanup, subtask and card settings, minicard details and parent-card paths and labels. Preserve board and activity tokens and counter notation, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:15:18 +03:00
Lauri Ojansivu 2f2bfd03a5 Translate Nepali runtime and organization settings
Fill 50 Nepali placeholders covering database and FerretDB details, reactivity and DDP settings, operating-system metrics, time units, custom-field display, account and board visibility and organization tenancy, domains, administrators and authentication sync. Preserve runtime modes, environment names and example hostnames, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:13:56 +03:00
Lauri Ojansivu 883ba218dc Translate Nepali transfer and webhook settings
Fill 50 Nepali placeholders covering attachment and API transfer limits, avatar upload controls, registration and invitation emails, SMTP and TLS settings and testing, authorization errors, webhooks and runtime labels. Preserve invitation tokens and named technical literals, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:12:06 +03:00
Lauri Ojansivu 591f6d2ef1 Translate Nepali tracking and branding
Fill 50 Nepali placeholders covering starred boards, time tracking, assignee and label shortcuts, uploads, custom logo and help URLs, automatic URL schemes, welcome-board and template names, archive warnings and WIP feedback. Preserve numeric ranges and technical literals, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:10:24 +03:00
Lauri Ojansivu 972658f6b1 Translate Nepali privacy and shortcuts
Fill 50 Nepali placeholders covering watch notifications, private and public page guidance, previews, member removal and Sandstorm access, description rescue, search and WIP controls, keyboard shortcuts, sidebars, signup and starred and default-board actions. Preserve link markup, member tokens and named technical products, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:08:43 +03:00
Lauri Ojansivu 0057603c5f Translate Nepali board and selection actions
Fill 50 Nepali placeholders covering last-administrator protection, board departure, list links and archival, administrative settings, swimlane and list imports, selection movement and copying, archive empty states, restricted roles and notifications. Preserve the board-title token and import format names, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:06:47 +03:00
Lauri Ojansivu 247df11428 Translate Nepali Trello import controls
Fill 50 Nepali placeholders covering Trello ZIP path and size validation, workspace placement, direct API imports, resumable job controls, member mapping, date and user validation, invitations, keyboard shortcuts and labels. Preserve the API URL, technical literals, year example and format token, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:05:09 +03:00
Lauri Ojansivu d46eddcb04 Translate Nepali advanced filters and imports
Fill 50 Nepali placeholders covering assignee and custom-field filters, advanced filter syntax, imported members, board-import guidance for project tools and file formats and Trello ZIP validation. Preserve operators, regex, source tokens, API paths and file suffixes, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:03:32 +03:00
Lauri Ojansivu 1066d1b96e Translate Nepali exports and filters
Fill 50 Nepali placeholders covering account and naming errors, PDF and Excel card exports, attachment and disk-space details, list sorting and date, label and member filters. Preserve export format names, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 17:01:15 +03:00
Lauri Ojansivu e2baf05aa2 Translate Nepali emails and validation
Fill 50 Nepali placeholders covering permanent-delete safeguards, WIP limits, profile and card-field editing, account and invitation emails, password resets, verification, board authorization and JSON, CSV, TSV and import validation. Preserve every email placeholder and technical literal, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 16:59:53 +03:00
Lauri Ojansivu da94d8b49b Translate Nepali roles and custom fields
Fill 50 Nepali placeholders covering remaining palette colors, restricted board roles, deletion confirmations, clipboard actions, linked and copied cards, JSON bulk-card input and custom-field types and options. Preserve and parse-test the JSON field names and structure, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 16:58:04 +03:00
Lauri Ojansivu e55ca47a07 Translate Nepali card aging and colors
Fill 50 Nepali placeholders covering starring, automatic list widths, clipboard input, three-tier card aging, directional movement, accessible dialog navigation, board restoration guidance and the board color palette. Preserve numeric tier markers, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 16:56:32 +03:00
Lauri Ojansivu 66cec44a8c Translate Nepali themes and permissions
Fill 50 Nepali placeholders covering member and restoration dialogs, linked cards and boards, imported-member mapping safeguards, themes, font previews and sizes, text colors, avatars, language and permissions. Preserve CAS and preview digits, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 16:55:11 +03:00
Lauri Ojansivu d75a6f47c9 Translate Nepali voting and imports
Fill 50 Nepali placeholders covering card membership and custom fields, voting, Planning Poker, dependencies, organizations, teams, backgrounds, domains, imported-member mapping and checklist and board-item imports. Extend locale-wide regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 16:53:51 +03:00
Lauri Ojansivu db2c9b202f Translate Nepali board views and archives
Fill 50 Nepali placeholders covering public-board guidance, board ordering and workspace assignment, colors and backgrounds, desktop and mobile views, zoom, calendars, archive guidance and card editing. Preserve HTML, workspace, percentage-range and format tokens, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 16:52:28 +03:00
Lauri Ojansivu 15a5773f39 Translate Nepali administration and archives
Fill 50 Nepali placeholders covering administrator announcements, offline reconnection, archives, board restoration and visibility, attachment deletion, background images, summaries, members and assignees. Preserve count, avatar, format and strong-markup tokens, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 16:50:53 +03:00
Lauri Ojansivu e9489315f8 Translate Nepali board layout controls
Fill 50 Nepali placeholders covering workspace deletion and board selection, home-board controls, dates, personal and shared list widths, keyboard shortcuts, swimlane heights and common card actions. Preserve date format tokens and numeric constraints, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 16:49:15 +03:00
Lauri Ojansivu f838d57bd2 Translate Nepali activity and workspaces
Fill 50 Nepali placeholders covering card moves and restoration, detailed activity and checklist history and All Boards workspace names, menus and settings. Preserve every old and new board, list, swimlane, card, checklist and format token, extend regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 16:47:42 +03:00
Lauri Ojansivu 08a505f383 Start completing Nepali translations
Fill 50 Nepali placeholders covering activity history for boards, lists, swimlanes, cards, attachments, labels, checklists, comments, custom fields and members, plus organization and team restrictions. Preserve all format and entity tokens, add locale-wide token and tag regression coverage and record the next checkpoint. These direct translations are low confidence and welcome review by a Nepali speaker.

Thanks to xet7 !
2026-08-27 16:46:05 +03:00
Lauri Ojansivu 836637221b Complete Northern Ndebele translations
Fill the final 17 Northern Ndebele placeholders covering diagnostic addresses, filesystem integrity, export selection, import feedback and numeric card search. Preserve WeKan, source names, file suffixes, IP variants and the search token and tags, extend locale-wide regression coverage and advance the recorded checkpoint to Nepali. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.

Thanks to xet7 !
2026-08-27 16:43:27 +03:00
Lauri Ojansivu 5f3cd0fb1c Translate Northern Ndebele repositories and diagnostics
Fill 50 remaining Northern Ndebele placeholders covering repositories, account and OTP access, login validation, Problems guidance, broken-card repair, CPU metrics and event fields. Preserve OTP, API, CPU and named repair tokens, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.

Thanks to xet7 !
2026-08-27 16:42:29 +03:00
Lauri Ojansivu 305da80565 Translate Northern Ndebele migration monitoring
Fill 50 remaining Northern Ndebele placeholders covering attachment migration targets, batch and CPU thresholds, lifecycle controls, monitoring, navigation and storage totals. Preserve GridFS, S3, CPU and numeric ranges, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:40:27 +03:00
Lauri Ojansivu 06f54fb64c Translate Northern Ndebele repair steps and jobs
Fill 50 remaining Northern Ndebele placeholders covering repair steps, board conversion, CPU and storage metrics, schedules, monitoring and job queues. Preserve URL, ID, CPU and GridFS literals, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:39:10 +03:00
Lauri Ojansivu e517786914 Translate Northern Ndebele board repairs
Fill 50 remaining Northern Ndebele placeholders covering comprehensive board repair, duplicate lists, lost cards, archives, missing lists, URL repair and migration progress. Preserve schema field and URL literals, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:38:01 +03:00
Lauri Ojansivu 13af052aef Translate Northern Ndebele migration storage
Fill 50 remaining Northern Ndebele placeholders covering Google Cloud and GridFS attachment storage, migration controls, S3 settings, scheduled board operations and writable paths. Preserve cloud, database, SSL/TLS and region literals, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:36:35 +03:00
Lauri Ojansivu 304a0efcd6 Translate Northern Ndebele backup and cloud setup
Fill 50 remaining Northern Ndebele placeholders covering scheduled backups, restore modes, Google Cloud credentials, S3, Azure and GCS setup paths and cloud connection diagnostics. Preserve provider names, console paths, JSON terminology and backup formats, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:34:47 +03:00
Lauri Ojansivu bf51b010ac Translate Northern Ndebele security and backups
Fill 50 remaining Northern Ndebele placeholders covering Sandstorm cleanup, adaptive card loading, safe rendering, import and export privacy, activity and notification suppression and backups. Preserve HTML, code, environment variables, identity providers and storage paths, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:33:10 +03:00
Lauri Ojansivu 94a75f0d94 Translate Northern Ndebele storage migrations
Fill 50 remaining Northern Ndebele placeholders covering scheduled-job recovery, filesystem, S3 and Azure storage, MongoDB–FerretDB transfer and Sandstorm migration status. Preserve environment variables, database URLs, products and named database token, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:30:48 +03:00
Lauri Ojansivu 224797bdf9 Translate Northern Ndebele lockout and scheduled jobs
Fill 50 remaining Northern Ndebele placeholders covering accessibility, login lockout protection and administration, scheduled jobs, file paths and board archive, backup and cleanup operations. Extend locale-wide vocabulary regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:29:09 +03:00
Lauri Ojansivu fefcb37b19 Translate Northern Ndebele board status and support
Fill 50 remaining Northern Ndebele placeholders covering board status, upload limits, custom translations, checklist display and support and accessibility pages. Preserve workspace, ISO, PDF, ZIP and JSON literals, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:27:21 +03:00
Lauri Ojansivu 218c6f636d Translate Northern Ndebele attachment storage
Fill 50 remaining Northern Ndebele placeholders covering bulk attachment movement, storage selection, repair, file statistics and MongoDB GridFS compaction. Preserve GridFS, S3, MongoDB, oplog and Meteor terminology, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:25:59 +03:00
Lauri Ojansivu 9c3dcc0f8f Translate Northern Ndebele teams and attachment actions
Fill 50 remaining Northern Ndebele placeholders covering teams, invitations, Node diagnostics, organizations, legal notices, checklist actions and attachment movement. Preserve URL, GridFS, S3 and Node terminology, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:24:23 +03:00
Lauri Ojansivu 81fe91d348 Translate Northern Ndebele recovery and tickets
Fill 50 remaining Northern Ndebele placeholders covering office and REST API reports, recovery, swimlane copying, wait indicators, deletion safeguards, tickets, requests and card details. Preserve API, environment and network literals, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:23:00 +03:00
Lauri Ojansivu 98c00179e0 Translate Northern Ndebele maps and reports
Fill 50 remaining Northern Ndebele placeholders covering maps, server troubleshooting, sorting, activities, string templates, file diagnostics, Problems reports, impersonation, recovery and office summaries. Preserve shell commands, format tokens, HTML entities and network literals, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:21:43 +03:00
Lauri Ojansivu 7469f65201 Translate Northern Ndebele sorting and dependencies
Fill 50 remaining Northern Ndebele placeholders covering sorting, completion, stickers, card dependencies and JSON/SVG imports, board backgrounds and card locations. Preserve format literals and named size and import tokens, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:19:56 +03:00
Lauri Ojansivu 9acb86bfda Translate Northern Ndebele search instructions
Fill 50 remaining Northern Ndebele placeholders covering search validation, paging and detailed operator guidance for board structure, people, dates, statuses, fields, sorting and combined conditions. Preserve every named token, code example and markup tag, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:18:41 +03:00
Lauri Ojansivu 929e10874c Translate Northern Ndebele search operators
Fill 50 remaining Northern Ndebele placeholders covering missing-result messages and global-search operators and predicates for board structure, people, dates, statuses, custom fields, attachments and checklists. Preserve result count and percent tokens, extend locale-wide terminology coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:16:47 +03:00
Lauri Ojansivu f3357246ee Translate Northern Ndebele shared templates and card views
Fill 50 remaining Northern Ndebele placeholders covering linked-list safeguards, tasks, domains, shared templates, My Cards and Due Cards views, global search and missing board-item messages. Preserve markdown emphasis, examples and percent tokens, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:15:27 +03:00
Lauri Ojansivu d215e9796f Translate Northern Ndebele roles and editor behavior
Fill 50 remaining Northern Ndebele placeholders covering multi-card windows, editor submission, management dialogs, notifications, board-role permissions, weekdays, ownership and linked-card safeguards. Preserve keyboard shortcut literals, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:14:06 +03:00
Lauri Ojansivu 44597eeb48 Translate Northern Ndebele counters and date activity
Fill 50 remaining Northern Ndebele placeholders covering member lists, custom body HTML, authentication display, duplication, counters, date activity and reminders, account deletion and resizing controls. Preserve HTML tags, named and percent tokens and technical literals, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:12:51 +03:00
Lauri Ojansivu 9737912fee Translate Northern Ndebele rule actions and manifests
Fill 50 remaining Northern Ndebele placeholders covering concrete rule actions, checklist and date behavior, authentication and custom product, HTML, manifest and asset-links settings. Preserve format and filename literals, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:11:20 +03:00
Lauri Ojansivu 0ea01c6232 Translate Northern Ndebele rule grammar
Fill 50 remaining Northern Ndebele placeholders covering board and list movement, archive restoration, labels, members, attachments, checklists, colors, card placement and email actions. Extend locale-wide terminology regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:10:06 +03:00
Lauri Ojansivu a9e8e25189 Translate Northern Ndebele rule schedules
Fill 50 remaining Northern Ndebele placeholders covering visual workflow imports, scheduled and button triggers, repeat periods, due and list conditions, sorting, completion, relative dates and rule units. Preserve named tokens and workflow product names, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:08:58 +03:00
Lauri Ojansivu 20f1c3ee2f Translate Northern Ndebele visual rules
Fill 50 remaining Northern Ndebele placeholders covering activity messages and visual board-rule triggers, actions, editing, selection and JSON, CSV and Trello Butler transfer. Preserve percent and named tokens and technical formats, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:07:40 +03:00
Lauri Ojansivu c998928fb7 Translate Northern Ndebele card and subtask settings
Fill 50 remaining Northern Ndebele placeholders covering card dates and colors, destructive actions, subtask and card settings, minicard display, parent paths and label activity. Preserve board and percent tokens, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:06:13 +03:00
Lauri Ojansivu 27f6dc93e6 Translate Northern Ndebele diagnostics and organizations
Fill 50 remaining Northern Ndebele placeholders covering database, FerretDB, reactivity and OS diagnostics, custom-field display, visibility, accounts and organization, team, domain and multitenancy controls. Preserve environment-variable and product literals, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:04:55 +03:00
Lauri Ojansivu c38300c8e3 Translate Northern Ndebele transfer limits and invitations
Fill 50 remaining Northern Ndebele placeholders covering attachment and API limits, avatar policy, registration, invitations, SMTP, webhooks and server-version labels. Preserve exact invitation tokens and technical names, extend locale-wide regression coverage and advance the recorded checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:03:30 +03:00
Lauri Ojansivu 6850555777 Translate Northern Ndebele starring and uploads
Fill 50 remaining Northern Ndebele placeholders covering starred boards, time tracking, uploads, custom branding, watching, welcome templates and WIP warnings. Preserve locale-wide placeholders and markup and advance the recorded translation checkpoint. These direct translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 16:01:50 +03:00
Lauri Ojansivu 86be744b32 Translate Northern Ndebele privacy and shortcuts
Translate the next 50 missing Northern Ndebele values for watch notices, private pages, member removal, search, WIP limits, shortcuts and default boards. Preserve HTML, placeholders and product names and record the next safe resume point.\n\nThese direct Northern Ndebele translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 15:53:11 +03:00
Lauri Ojansivu c6d3b665af Translate Northern Ndebele roles and selection
Translate the next 50 missing Northern Ndebele values for board departure, list actions, settings, multi-card selection, archives and roles. Preserve placeholders and product-format literals and record the next safe resume point.\n\nThese direct Northern Ndebele translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 15:51:03 +03:00
Lauri Ojansivu fc7b363f7b Translate Northern Ndebele Trello imports
Translate the next 50 missing Northern Ndebele values for safe Trello ZIP and API imports, member mapping, validation and labels. Preserve URLs, API names, numeric examples and placeholders and record the next safe resume point.\n\nThese direct Northern Ndebele translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 15:49:48 +03:00
Lauri Ojansivu da50014757 Translate Northern Ndebele filters and imports
Translate the next 50 missing Northern Ndebele values for advanced filters, member states and board imports from supported formats. Preserve operators, endpoints, placeholders and file types and record the next safe resume point.\n\nThese direct Northern Ndebele translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 15:48:23 +03:00
Lauri Ojansivu f6cf967f9b Translate Northern Ndebele exports and filters
Translate the next 50 missing Northern Ndebele values for account errors, PDF and Excel exports, attachment metadata, sorting and filters. Preserve product formats and record the next safe resume point.\n\nThese direct Northern Ndebele translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 15:46:11 +03:00
Lauri Ojansivu 945663bf4e Translate Northern Ndebele email and validation
Translate the next 50 missing Northern Ndebele values for custom fields, WIP limits, account email and validation errors. Preserve every email placeholder and data-format literal and record the next safe resume point.\n\nThese direct Northern Ndebele translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 15:44:49 +03:00
Lauri Ojansivu 94c34f20ce Translate Northern Ndebele roles and custom fields
Translate the next 50 missing Northern Ndebele values for board roles, deletion confirmations, clipboard actions, multi-card templates and custom fields. Preserve valid JSON structure and technical literals and record the next safe resume point.\n\nThese direct Northern Ndebele translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 15:43:32 +03:00
Lauri Ojansivu 04dcc85da9 Translate Northern Ndebele card aging and colors
Translate the next 50 missing Northern Ndebele values for settings, card aging, keyboard movement, dialog navigation and colors. Replace every English-equal color placeholder and record the next safe resume point.\n\nThese direct Northern Ndebele translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 15:42:22 +03:00
Lauri Ojansivu f81dd12709 Translate Northern Ndebele themes and member mapping
Translate the next 50 missing Northern Ndebele values for member dialogs, imported-user mapping, themes, fonts, avatars, language and permissions. Preserve product terms and numeric preview content and record the next safe resume point.\n\nThese direct Northern Ndebele translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 15:40:59 +03:00
Lauri Ojansivu 824e629bed Translate Northern Ndebele card voting and imports
Translate the next 50 missing Northern Ndebele values for card voting, Planning Poker, dependencies, organizations, teams and imports. Preserve product terminology and record the next safe resume point.\n\nThese direct Northern Ndebele translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 15:39:56 +03:00
Lauri Ojansivu d04c446111 Translate Northern Ndebele board views
Translate the next 50 missing Northern Ndebele values for public boards, views, zoom, calendar navigation, archives and card editing. Preserve placeholders and HTML markup and record the next safe resume point.\n\nThese direct Northern Ndebele translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 15:38:35 +03:00
Lauri Ojansivu 20331fb686 Translate Northern Ndebele archives and display
Translate the next 50 missing Northern Ndebele values for administration, archives, templates, attachments, board backgrounds and member display. Preserve placeholders and HTML markup and record the next safe resume point.\n\nThese direct Northern Ndebele translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 15:37:16 +03:00
Lauri Ojansivu b9b7bd3d01 Translate Northern Ndebele board layout
Translate the next 50 missing Northern Ndebele values for workspaces, Home boards, list widths, shortcuts, swimlanes, checklists and members. Preserve format tokens and numeric constraints and record the next safe resume point.\n\nThese direct Northern Ndebele translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 15:36:09 +03:00
Lauri Ojansivu e9e7e0b54f Translate Northern Ndebele workspaces and activity
Translate the next 50 missing Northern Ndebele values for card movement, activity phrases, checklists, dates and workspaces. Preserve placeholders and technical literals and record the next safe resume point.\n\nThese direct Northern Ndebele translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 15:34:55 +03:00
Lauri Ojansivu a708ead04a Translate Northern Ndebele activity messages
Translate the first 50 missing Northern Ndebele values for board, card, list, swimlane, member, attachment, checklist and import activity. Add locale-wide placeholder and markup invariants and record the next safe resume point.\n\nThese direct Northern Ndebele translations are low confidence and welcome review by a Northern Ndebele speaker.\n\nThanks to xet7 !
2026-08-27 15:33:39 +03:00
Lauri Ojansivu 6df3857b97 Complete the Nahuatl translation
Translate the final 17 missing Nahuatl values for diagnostic events, filesystem integrity, scoped exports and imports and numeric search. Preserve placeholders, tags and import formats, mark Nahuatl complete and advance the safe resume point to Northern Ndebele.\n\nThese direct Nahuatl translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:32:02 +03:00
Lauri Ojansivu 21d8d8548a Translate Nahuatl repositories and diagnostics
Translate the next 50 missing Nahuatl values for repositories, account access, problem reports, broken-card repair and diagnostic events. Preserve placeholders and technical abbreviations and record the final Nahuatl resume point.\n\nThese direct Nahuatl translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:30:59 +03:00
Lauri Ojansivu 3a98e99464 Translate Nahuatl migration controls
Translate the next 50 missing Nahuatl values for attachment migration tuning, resource limits, monitoring, navigation and storage totals. Preserve thresholds and storage names and record the next safe resume point.\n\nThese direct Nahuatl translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:29:50 +03:00
Lauri Ojansivu c05c77d642 Translate Nahuatl migration monitoring
Translate the next 50 missing Nahuatl values for board repair steps, conversion, CPU and filesystem monitoring, intervals and scheduled jobs. Preserve identifiers and technical names and record the next safe resume point.\n\nThese direct Nahuatl translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:28:52 +03:00
Lauri Ojansivu fa77b195e7 Translate Nahuatl board repair migrations
Translate the next 50 missing Nahuatl values for comprehensive board repair, lost cards, archived items, lists and broken file references. Preserve schema identifiers and record the next safe resume point.\n\nThese direct Nahuatl translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:27:53 +03:00
Lauri Ojansivu 71c6049764 Translate Nahuatl storage migrations
Translate the next 50 missing Nahuatl values for Google Cloud and GridFS storage, migration controls, S3 settings and scheduled board operations. Preserve service and protocol literals and record the next safe resume point.\n\nThese direct Nahuatl translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:26:43 +03:00
Lauri Ojansivu 63f74a7168 Translate Nahuatl cloud backup settings
Translate the next 50 missing Nahuatl values for backup schedules, restore modes and Google Cloud, S3 and Azure configuration. Preserve paths, service names and configuration literals and record the next safe resume point.\n\nThese direct Nahuatl translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:25:46 +03:00
Lauri Ojansivu 32d4a45c04 Translate Nahuatl security and backup settings
Translate the next 50 missing Nahuatl values for Sandstorm cleanup, card loading, secure rendering, import and export restrictions and backups. Preserve markup, configuration names and storage paths and record the next safe resume point.\n\nThese direct Nahuatl translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:24:32 +03:00
Lauri Ojansivu 112eeb6363 Translate Nahuatl scheduled migrations
Translate the next 50 missing Nahuatl values for scheduled jobs, storage configuration, database migration and Sandstorm migration status. Preserve endpoints, environment variables and placeholders and record the next safe resume point.\n\nThese direct Nahuatl translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:22:36 +03:00
Lauri Ojansivu 16bec78b29 Translate Nahuatl account lockout settings
Translate the next 50 missing Nahuatl values for accessibility, brute-force lockouts, scheduled jobs, attachment paths and board maintenance. Preserve behavior distinctions and record the next safe resume point.\n\nThese direct Nahuatl translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:21:15 +03:00
Lauri Ojansivu 715f5065ca Translate Nahuatl board status and support
Translate the next 50 missing Nahuatl values for board status, uploads, custom translations, checklist display and support and accessibility pages. Preserve placeholders and technical literals and record the next safe resume point.\n\nThese direct Nahuatl translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:20:07 +03:00
Lauri Ojansivu 78c823850e Translate Nahuatl attachment storage reports
Translate the next 50 missing Nahuatl values for attachment migration, storage repair, file statistics and MongoDB compaction. Preserve storage and database literals and record the next safe resume point.\n\nThese direct Nahuatl translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:18:58 +03:00
Lauri Ojansivu 76f27b4140 Translate Nahuatl teams and attachment actions
Translate the next 50 missing Nahuatl values for teams, organizations, Node memory diagnostics, legal notices, checklists and attachment storage. Preserve technical literals and record the next safe resume point.\n\nThese direct Nahuatl translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:17:46 +03:00
Lauri Ojansivu d5fbeeb7c3 Translate Nahuatl recovery and support reports
Translate the next 50 missing Nahuatl values for Office and API reports, recovery, wait indicators, swimlane copying and support tickets. Preserve technical literals and record the next safe resume point.\n\nThese direct Nahuatl translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:16:30 +03:00
Lauri Ojansivu d05f419f22 Translate Nahuatl reports and map settings
Translate the next 50 missing Nahuatl values for map detection, troubleshooting, string templates, activities and administrative reports. Preserve code tokens and record the next safe resume point.\n\nThese direct Nahuatl translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:15:08 +03:00
Lauri Ojansivu 48f0a53000 Translate Nahuatl card metadata
Translate the next 50 Nahuatl placeholders for sorting, completion, stickers, dependency relationships, imports, board backgrounds, and map locations. Preserve JSON, SVG, import counters, and size tokens and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:10:38 +03:00
Lauri Ojansivu e24f08ecfc Translate Nahuatl search instructions
Translate the next 50 Nahuatl placeholders for global-search validation, pagination, operators, predicates, and usage notes. Preserve every search token and extend the locale progress regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:09:21 +03:00
Lauri Ojansivu 6e66ed6f99 Translate Nahuatl search terms
Translate the next 50 Nahuatl placeholders for lookup results and global-search operators and predicates. Preserve search result, range, and format tokens and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:07:43 +03:00
Lauri Ojansivu 327ebe1492 Translate Nahuatl templates and card views
Translate the next 50 Nahuatl placeholders for linked-list safety, tasks, domains, shared templates, My Cards, Due Cards, global search, and not-found errors. Preserve domain examples and lookup tokens and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:06:32 +03:00
Lauri Ojansivu 27a960169e Translate Nahuatl roles and editor settings
Translate the next 50 Nahuatl placeholders for multi-card windows, editor submission, administration, notifications, rename and invite permissions, role status, weekdays, ownership, and voting. Preserve editor key combinations and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:04:58 +03:00
Lauri Ojansivu ea8d76466d Translate Nahuatl authentication and reminders
Translate the next 50 Nahuatl placeholders for custom HTML, authentication, board duplication, date activity, reminders, deletion, minicard labels, resizing, and editor submission. Preserve HTML tags, date and activity tokens, OIDC, LDAP, and Enter literals and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:03:40 +03:00
Lauri Ojansivu 3937ded696 Translate Nahuatl rule actions and manifests
Translate the next 50 Nahuatl placeholders for automation actions, dates, linked cards, authentication, custom head tags, manifests, asset links, layout, and card counters. Preserve list formats and HTML, JSON, and assetlinks literals and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:01:59 +03:00
Lauri Ojansivu 7fdd2b64a9 Translate Nahuatl rule actions
Translate the next 50 Nahuatl placeholders for rule conditions and actions involving boards, lists, cards, labels, members, attachments, checklists, archives, colors, and email. Extend the locale terminology regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 15:00:38 +03:00
Lauri Ojansivu c5adf8fc9f Translate Nahuatl scheduled rules
Translate the next 50 Nahuatl placeholders for workflow imports, schedules, due-date and list triggers, rule buttons, sorting, completion, movement, and relative dates. Preserve workflow product names, count tokens, and schedule literals and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:59:35 +03:00
Lauri Ojansivu 66defbc386 Translate Nahuatl rule workflows
Translate the next 50 Nahuatl placeholders for rule editing, workflow triggers, activity history, schedules, and JSON, CSV, and Trello Butler imports. Preserve activity, time, and count tokens and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:58:36 +03:00
Lauri Ojansivu 48cb82ae87 Translate Nahuatl dates and subtasks
Translate the next 50 Nahuatl placeholders for card dates, colors, deletion, duplicate cleanup, subtask and minicard settings, counts, parent paths, and label activity. Preserve board, count, and activity tokens and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:57:30 +03:00
Lauri Ojansivu d1cb56189d Translate Nahuatl runtime and tenancy
Translate the next 50 Nahuatl placeholders for database and OS diagnostics, reactivity, card fields, visibility, organizations, teams, authentication sync, multitenancy, and administrators. Preserve runtime modes, environment variables, and domain examples and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:55:47 +03:00
Lauri Ojansivu aed2707e23 Translate Nahuatl transfer limits and webhooks
Translate the next 50 Nahuatl placeholders for attachment and API limits, registration, SMTP invitations, authorization, webhooks, and runtime labels. Preserve mail tokens and API, TLS, SMTP, Node.js, and Meteor terminology and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:53:56 +03:00
Lauri Ojansivu 5b9c70e408 Translate Nahuatl tracking and branding
Translate the next 50 Nahuatl placeholders for tracking, overtime, shortcuts, uploads, custom branding, URL schemes, watching, welcome templates, and WIP errors. Preserve numeric ranges, URL terminology, and defaults and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:52:27 +03:00
Lauri Ojansivu bfcbd73bad Translate Nahuatl notifications and shortcuts
Translate the next 50 Nahuatl placeholders for notifications, private pages, board visibility, member removal, Sandstorm guidance, rescue dialogs, WIP limits, shortcuts, sidebars, signup, and default boards. Preserve HTML and member tokens and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:51:19 +03:00
Lauri Ojansivu df96411d15 Translate Nahuatl roles and selection
Translate the next 50 Nahuatl placeholders for last-admin safety, leaving boards, archives, settings, item imports, multi-selection, muted boards, roles, and participation notices. Preserve board and import tokens and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:49:12 +03:00
Lauri Ojansivu 50813241f0 Translate Nahuatl Trello imports
Translate the next 50 Nahuatl placeholders for Trello ZIP validation, API imports, workspace placement, cancellation, results, and member mapping. Preserve the API URL, credentials terminology, year example, and label token and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:47:51 +03:00
Lauri Ojansivu dcf1d6c031 Translate Nahuatl filters and imports
Translate the next 50 Nahuatl placeholders for advanced filtering, imported members, multi-source board imports, Trello archives, and import errors. Preserve operators, API paths, source tokens, file formats, and credentials terminology and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:46:31 +03:00
Lauri Ojansivu b7b442876e Translate Nahuatl exports and filters
Translate the next 50 Nahuatl placeholders for user validation, card PDF and Excel exports, attachment metadata, list sorting, and date, label, and member filters. Preserve technical format names and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:44:55 +03:00
Lauri Ojansivu 254f660236 Translate Nahuatl email and validation
Translate the next 50 Nahuatl placeholders for fields, permanent deletion, profiles, WIP limits, transactional email, and import and access errors. Preserve every mail token and technical data format and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:43:27 +03:00
Lauri Ojansivu bd678d5f95 Translate Nahuatl roles and custom fields
Translate the next 50 Nahuatl placeholders for board roles, deletion prompts, clipboard actions, multi-card templates, and custom-field types. Validate the translated JSON template structure and extend locale progress coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:42:03 +03:00
Lauri Ojansivu 7d9e127e9b Translate Nahuatl card aging and colors
Translate the next 50 Nahuatl placeholders for starring, list width, card aging, movement, dialogs, and color names. Preserve numeric tiers and extend the locale progress regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:40:47 +03:00
Lauri Ojansivu f928157ac1 Translate Nahuatl themes and member mapping
Translate the next 50 Nahuatl placeholders for dialogs, linked items, imported-member mapping, themes, fonts, avatars, language, and permissions. Preserve CAS and numeric preview literals and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:39:35 +03:00
Lauri Ojansivu 5993f3d250 Translate Nahuatl voting and imports
Translate the next 50 Nahuatl placeholders for voting, Planning Poker, dependencies, organizations, teams, domains, and board-item imports and exports. Preserve product terminology and extend the locale progress regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:38:12 +03:00
Lauri Ojansivu da652b1bed Translate Nahuatl board views and archives
Translate the next 50 Nahuatl placeholders for public boards, workspace assignment, display modes, zoom, calendar navigation, archive guidance, and card editing. Preserve HTML, workspace, percentage, and count tokens and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:37:09 +03:00
Lauri Ojansivu ffb78babe7 Translate Nahuatl archives and board display
Translate the next 50 Nahuatl placeholders for administration, announcements, archives, templates, attachments, board backgrounds, and member display. Preserve count, size, URL, and HTML tokens and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:35:42 +03:00
Lauri Ojansivu 8657ff09c5 Translate Nahuatl board layout controls
Translate the next 50 Nahuatl placeholders for workspaces, multi-board selection, Home boards, list widths, swimlane heights, shortcuts, and card controls. Preserve source formats and extend the locale progress regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:34:19 +03:00
Lauri Ojansivu fd5a9191de Translate Nahuatl activity and workspaces
Translate the next 50 Nahuatl placeholders for card movement, checklist activity, comments, dates, and workspace controls. Preserve source activity tokens and extend the locale progress regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:33:07 +03:00
Lauri Ojansivu 870ab4fcdd Start Nahuatl activity translations
Translate the first 50 Nahuatl placeholders for board, card, checklist, comment, archive, and import activity messages. Add whole-file token and tag invariants and locale progress regression coverage.\n\nThese direct translations are low confidence and welcome review by a Nahuatl speaker.\n\nThanks to xet7 !
2026-08-27 14:31:33 +03:00
Lauri Ojansivu 2b0c274c2f Complete Burmese translations
Translate the final 17 Burmese placeholders for diagnostic events, filesystem integrity, scoped imports and exports, and numeric search. Preserve IP versions, file extensions, and search tokens and prove the Burmese placeholder backlog is zero.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 14:29:46 +03:00
Lauri Ojansivu 69a452c706 Translate Burmese accounts and diagnostics
Translate the next 50 Burmese placeholders for repositories, account authentication, problem reports, card repair, CPU status, and diagnostic events. Preserve OTP, API, CPU, and repair placeholders and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 14:28:39 +03:00
Lauri Ojansivu 43ba870922 Translate Burmese migration controls
Translate the next 50 Burmese placeholders for attachment migration tuning, resource thresholds, monitoring, navigation, and operation totals. Preserve backend names, units, and numeric ranges and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 14:26:54 +03:00
Lauri Ojansivu 1f27843a79 Translate Burmese migration monitoring
Translate the next 50 Burmese placeholders for board repair steps, conversion, CPU and storage monitoring, recurring schedules, and job details. Preserve identifiers and backend terminology and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 14:25:00 +03:00
Lauri Ojansivu 1628820e67 Translate Burmese board migrations
Translate the next 50 Burmese placeholders for board integrity migrations, recovery actions, file URL repair, confirmations, and progress. Preserve data identifiers and URL terminology and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 14:23:39 +03:00
Lauri Ojansivu e50a7fed4e Translate Burmese storage migrations
Translate the next 50 Burmese placeholders for cloud and GridFS attachment storage, migration controls, S3 settings, and scheduled board operations. Preserve backend, region, and protocol terminology and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 14:20:50 +03:00
Lauri Ojansivu 1f357ce0f0 Translate Burmese cloud backups
Translate the next 50 Burmese placeholders for scheduled backups, restore modes, cloud credentials, provider setup, and connection testing. Preserve time formats, provider terminology, key fields, and file extensions and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 14:19:06 +03:00
Lauri Ojansivu d0528b5d79 Translate Burmese security and backups
Translate the next 50 Burmese placeholders for Sandstorm cleanup, card loading, secure rendering, feature restrictions, anonymization, and backups. Preserve HTML, configuration names, paths, and formats and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 14:14:36 +03:00
Lauri Ojansivu 3b448f8c9a Translate Burmese database migration
Translate the next 50 Burmese placeholders for scheduled migrations, storage backends, database migration, and Sandstorm migration status. Preserve URLs, environment variables, paths, and placeholder tokens and extend regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 14:04:38 +03:00
Lauri Ojansivu 3a4c1a1bae Translate Burmese login protections
Translate the next 50 Burmese placeholders for accessibility, login lockouts, people filters, scheduled jobs, attachment paths, and board maintenance. Extend the locale progress regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 14:02:05 +03:00
Lauri Ojansivu 2969080ec5 Translate Burmese board settings
Translate the next 50 Burmese placeholders for board status, upload limits, custom translations, checklist display, support, and accessibility. Preserve workspace and file-format literals and extend the locale progress regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 14:00:08 +03:00
Lauri Ojansivu 4573324d32 Translate Burmese attachment storage
Translate the next 50 Burmese placeholders for attachment migration, location repair, storage statistics, and MongoDB compaction. Preserve storage and database terminology and extend the locale progress regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 13:54:09 +03:00
Lauri Ojansivu fba52a862a Translate Burmese administration controls
Translate the next 50 Burmese placeholders for teams, organizations, Node.js memory diagnostics, legal notices, checklists, and attachment storage. Preserve technical storage names and extend the locale progress regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 13:50:58 +03:00
Lauri Ojansivu 5db340718e Translate Burmese reports and tickets
Translate the next 50 Burmese placeholders for API and recovery reports, wait spinners, deletion safeguards, support tickets, and card details. Preserve technical literals and extend the locale progress regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 13:47:19 +03:00
Lauri Ojansivu cf64a98612 Translate Burmese maps and reports
Translate the next 50 Burmese placeholders for map detection, troubleshooting, string templates, and administration reports. Preserve formatting tokens and diagnostic commands and extend the locale progress regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 13:44:28 +03:00
Lauri Ojansivu a6f3ed9a2e Translate Burmese card metadata
Translate the next 50 Burmese placeholders for sorting, completion, stickers, dependency relationships, board backgrounds, and map locations. Preserve source tokens and extend the locale progress regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 13:42:09 +03:00
Lauri Ojansivu 0f08ec8f35 Translate Burmese search instructions
Translate the next 50 Burmese placeholders for global-search validation, navigation, operators, predicates, and usage notes. Preserve every source token and extend the locale progress regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 13:40:50 +03:00
Lauri Ojansivu 7ab3341bc9 Translate Burmese search terms
Translate the next 50 Burmese placeholders for lookup results and global-search operators and predicates. Preserve source placeholders and extend the locale progress regression coverage.\n\nThese direct translations are low confidence and welcome review by a Burmese speaker.\n\nThanks to xet7 !
2026-08-27 13:38:57 +03:00
Lauri Ojansivu 90ab7ab5e4 Translate Burmese shared templates and card views.
Translate the twenty-sixth 50 Burmese placeholders covering linked-list deletion, checklist hiding, tasks, domains, shared templates, domain users, My Cards and Due Cards views, global search and not-found errors. Preserve domain examples and every lookup token. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:37:05 +03:00
Lauri Ojansivu fd4a50a787 Translate Burmese roles and editor behavior.
Translate the twenty-fifth 50 Burmese placeholders covering multi-card windows, editor keys, card display, organization, team and user editing, notification state, rename and invite permissions, board-role capabilities, weekdays, status, ownership, activity and voting. Preserve every editor keyboard literal. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:35:25 +03:00
Lauri Ojansivu f96fd2abbe Translate Burmese date reminders and layout controls.
Translate the twenty-fourth 50 Burmese placeholders covering body HTML, LDAP and OIDC, duplication, totals, swimlane deletion, card dates and reminders, item placement, mentions, destructive confirmations, minicard labels, drag handles and Enter submission. Preserve body tags and every activity token. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:33:57 +03:00
Lauri Ojansivu 4b755fef3d Translate Burmese rule actions and custom manifests.
Translate the twenty-third 50 Burmese placeholders covering rule email, archive, label, member, checklist, swimlane, date and link actions, authentication, product naming, HTML head tags, web manifests, assetlinks, layout and card counters. Preserve comma-separated examples and HTML, JSON, manifest and assetlinks literals. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:32:07 +03:00
Lauri Ojansivu d0a374ebc3 Translate Burmese rule actions and conditions.
Translate the twenty-second 50 Burmese placeholders covering board, list, archive, label, member, attachment, checklist and item rule phrases and card movement, restoration, removal, color, checklist and email actions. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:30:39 +03:00
Lauri Ojansivu 6fa63eeb1d Translate Burmese scheduled rule workflows.
Translate the twenty-first 50 Burmese placeholders covering n8n and Node-RED workflows, scheduled and button triggers, recurring schedules, due and duration triggers, sorting, completion, bulk movement, relative dates and time units. Preserve the count token and workflow, weekday and variable literals. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:29:23 +03:00
Lauri Ojansivu 3962ea7cd9 Translate Burmese rule workflows and transfers.
Translate the twentieth 50 Burmese placeholders covering activity messages, board-rule creation and workflow editing, event and daily triggers, and JSON, CSV and Trello Butler rule transfers. Preserve all percent, time and count tokens and transfer-format names. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:27:52 +03:00
Lauri Ojansivu 29a25acd56 Translate Burmese card dates and subtask settings.
Translate the nineteenth 50 Burmese placeholders covering received and end dates, colors, attribution, deletion safety, duplicate-list cleanup, subtask and card settings, minicard display, checklist counts and parent-card paths. Preserve board and percent tokens and count notation. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:26:27 +03:00
Lauri Ojansivu fbbe89a826 Translate Burmese runtime and tenancy settings.
Translate the eighteenth 50 Burmese placeholders covering database and FerretDB provenance, Meteor reactivity, DDP transport, OS metrics, custom fields, account changes, board visibility, templates, authentication sync, multitenant domains and organization administration. Preserve every runtime constant and example hostname. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:24:42 +03:00
Lauri Ojansivu 44b0a3e0d3 Translate Burmese transfer and webhook settings.
Translate the seventeenth 50 Burmese placeholders covering attachment and API limits, avatars, registration, invitations, SMTP and TLS, authorization, webhooks and runtime version labels. Preserve all invitation tokens and API, SMTP, TLS, Webhook, Node and Meteor terminology. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:22:42 +03:00
Lauri Ojansivu 293551dbad Translate Burmese tracking and branding.
Translate the sixteenth 50 Burmese placeholders covering starred boards, time tracking, assignee and label shortcuts, uploads, branding, URL schemes, watching, welcome content, templates and WIP errors. Preserve numeric ranges, logo height and URL Scheme terminology. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:20:30 +03:00
Lauri Ojansivu 971b5ea77a Translate Burmese shortcuts and member removal.
Translate the fifteenth 50 Burmese placeholders covering watched notifications, private-page login, previews, board visibility, member removal, Sandstorm access, description recovery, search, WIP limits, shortcuts, sidebars, signup, starring and default boards. Preserve HTML, runtime tokens and product literals. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:18:42 +03:00
Lauri Ojansivu c46af5341f Translate Burmese selection and board roles.
Translate the fourteenth 50 Burmese placeholders covering last-admin protection, board leaving and linking, list archiving, settings, card imports, list deletion, selection movement, multi-selection, notification muting, archives and restricted Normal roles. Preserve board, Trello and spreadsheet literals. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:16:20 +03:00
Lauri Ojansivu 4eba65bdf5 Translate Burmese Trello import controls.
Translate the thirteenth 50 Burmese placeholders covering Trello ZIP safety, workspace and API imports, selection, progress, cancellation, recovery, member mapping, validation, invitations, shortcuts and labels. Preserve the API URL, token terminology, year and percent token. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:14:31 +03:00
Lauri Ojansivu 7b1c63eb2d Translate Burmese advanced filters and imports.
Translate the twelfth 50 Burmese placeholders covering assignee and advanced filters, imported-member state, impersonation, and Kanboard, NextCloud Deck, OpenProject, issue, Asana, ZenKit, Trello, CSV/TSV, Jira, Excel and WeKan imports. Preserve operators, regex, source tokens, product names and file extensions. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:12:32 +03:00
Lauri Ojansivu f73df5a8f5 Translate Burmese exports and filters.
Translate the eleventh 50 Burmese placeholders covering account errors, board and card exports, PDF and Excel diagnostics, list sorting, and card, list, date, label and member filters. Preserve PDF and Excel literals. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:09:42 +03:00
Lauri Ojansivu f26d56f647 Translate Burmese email and import errors.
Translate the tenth 50 Burmese placeholders covering permanent deletion, WIP limits, dates, reactions, notifications, account email flows, scrolling, and board, role, import and linked-card errors. Preserve every email token and WIP, JSON, CSV, TSV and WeKan literals. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:08:05 +03:00
Lauri Ojansivu 668b8463ea Translate Burmese roles and custom fields.
Translate the ninth 50 Burmese placeholders covering colors, restricted roles, checklist deletion, clipboard actions, linked and bulk-copied cards, templates, labels and custom-field types. Preserve valid JSON structure and the JSON and Enter literals. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:05:57 +03:00
Lauri Ojansivu c78c38413d Translate Burmese card aging and colors.
Translate the eighth 50 Burmese placeholders covering starring, automatic list widths, clipboard input, three-tier card aging, movement, dialog navigation, board closing and 23 color names. Preserve the tier count. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:04:10 +03:00
Lauri Ojansivu 04e7e82344 Translate Burmese themes and member mapping.
Translate the seventh 50 Burmese placeholders covering member and template dialogs, archived-item restoration, rules, CAS sign-in, linked cards and boards, imported-member mapping, themes, fonts, colors, avatars, language and permissions. Preserve CAS and numeric preview literals. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:02:30 +03:00
Lauri Ojansivu d478817450 Translate Burmese voting and imports.
Translate the sixth 50 Burmese placeholders covering card members and dates, voting, Planning Poker, dependencies, organizations, teams, domains, deletion, imported-member mapping and scoped imports. Preserve Planning Poker terminology. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 13:00:43 +03:00
Lauri Ojansivu 6416b237a7 Translate Burmese board views and archives.
Translate the fifth 50 Burmese placeholders covering board visibility and workspace movement, mobile and desktop views, zoom, calendars, archive guidance, card deletion safety, dates, time, attachments, custom fields, labels and members. Preserve HTML, workspace, percent and numeric literals. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 12:59:12 +03:00
Lauri Ojansivu 581e959df3 Translate Burmese archives and board settings.
Translate the fourth 50 Burmese placeholders covering administrator announcements, offline recovery, archives, templates, attachment deletion, board backgrounds, All Boards settings, and member and assignee summaries. Preserve HTML and runtime placeholders. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 12:57:25 +03:00
Lauri Ojansivu 00fedd1261 Translate Burmese board layout controls.
Translate the third 50 Burmese placeholders covering workspace deletion, board selection, Home-board controls, due and end dates, list sizing, keyboard shortcuts, swimlane height, checklists, labels and members. Preserve runtime values and numeric constraints. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 12:55:39 +03:00
Lauri Ojansivu 4eb8b9cc92 Translate Burmese workspaces and activity.
Translate the second 50 Burmese placeholders covering card movement, compact activity messages, checklist state changes, and workspace creation, editing and navigation. Preserve every percent and underscore-delimited runtime token. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 12:53:23 +03:00
Lauri Ojansivu cec8680864 Translate Burmese activity messages.
Translate the first 50 Burmese placeholders covering organization and team membership, comments, due dates, and board, card, list, swimlane, attachment, label and checklist activity messages. Preserve all percent and underscore-delimited runtime tokens. These direct translations are low confidence and welcome review by a Burmese speaker.

Thanks to xet7 !
2026-08-27 12:51:37 +03:00
Lauri Ojansivu a4a6ff2732 Complete remaining Marathi translations.
Translate the final 17 Marathi placeholders covering event IP fields, filesystem integrity, scoped export and import, numbered-card search, and WeKan import formats. Preserve the search operator token, number tags, protocol labels, and filename extensions. These direct translations are low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:48:42 +03:00
Lauri Ojansivu 964306ce9b Translate Marathi repositories and repair reports.
Translate 50 remaining Marathi placeholders covering account and OTP flows, repositories and API endpoints, login failures, problem acknowledgement, broken-card repair, CPU load and event-log fields. Preserve repair placeholders and the OTP, API and CPU literals. These direct translations are low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:44:48 +03:00
Lauri Ojansivu 06601b1325 Translate Marathi migration controls and system monitoring.
Translate 50 remaining Marathi placeholders covering bulk storage migrations, processing thresholds, migration logs, monitoring navigation, minicard settings, storage distribution, and system resources. Extend regression coverage for provider names and numeric ranges.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:41:50 +03:00
Lauri Ojansivu bd496d3d74 Translate Marathi migration monitoring and job queues.
Translate 50 remaining Marathi placeholders covering board-repair steps, conversion status, runtime metrics, recurring intervals, attachment monitoring, storage sizes, and job queues. Extend regression coverage for URL, ID, CPU, GridFS, and interval literals.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:40:30 +03:00
Lauri Ojansivu f7898aa563 Translate Marathi board repair migrations.
Translate 50 remaining Marathi placeholders covering comprehensive board repair, duplicate-list cleanup, lost-card restoration, URL repair, migration confirmations, and structural repair steps. Extend regression coverage for swimlaneId, listId, and URL identifiers.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:39:04 +03:00
Lauri Ojansivu 09927c38ee Translate Marathi migrations and S3 settings.
Translate 50 remaining Marathi placeholders covering GCS attachment movement, GridFS and CollectionFS, migration controls, S3 authentication, scheduled board operations, and attachment monitoring. Extend regression coverage for storage providers, regions, and transport-security literals.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:36:57 +03:00
Lauri Ojansivu c4062124b1 Translate Marathi backup schedules and cloud storage.
Translate 50 remaining Marathi placeholders covering backup schedules and restore modes, GCS credentials and permissions, cloud-provider setup paths, connection tests, and Azure attachment movement. Extend regression coverage for schedule formats, identifiers, permissions, and provider terminology.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:35:23 +03:00
Lauri Ojansivu 34ba445b57 Translate Marathi privacy controls and backups.
Translate 50 remaining Marathi placeholders covering Sandstorm cleanup, card-loading modes, secure text rendering, import and export privacy controls, activity settings, and backups. Extend regression coverage for HTML, comments, environment-variable names, and backup path formats.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:33:41 +03:00
Lauri Ojansivu 8b88695485 Translate Marathi storage and database migration.
Translate 50 remaining Marathi placeholders covering scheduled-job recovery, filesystem and cloud storage, database migration, and Sandstorm migration status. Extend regression coverage for the database placeholder, connection URLs, environment variables, and Sandstorm storage paths.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:29:56 +03:00
Lauri Ojansivu 1deb094f17 Translate Marathi account protection and scheduled jobs.
Translate 50 remaining Marathi placeholders covering accessibility, brute-force lockout controls, scheduled jobs, storage paths, and board maintenance scheduling. Extend regression coverage for security terminology, time units, active state, storage, and backups.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:28:14 +03:00
Lauri Ojansivu 329b6163ab Translate Marathi board status and accessibility.
Translate 50 remaining Marathi placeholders covering board status and time summaries, upload limits, custom translations, checklist display, support, and accessibility. Extend regression coverage for the workspace token, PDF, ISO 8601, JSON, .zip, and accessibility terminology.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:26:28 +03:00
Lauri Ojansivu 0c91c8645e Translate Marathi attachment repair and compaction.
Translate 50 remaining Marathi placeholders covering bulk attachment movement, storage-location repair, default storage, file counts and identifiers, and MongoDB GridFS compaction. Extend regression coverage for GridFS, S3, storage repair, identifiers, Meteor, and MongoDB Compact terminology.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:25:00 +03:00
Lauri Ojansivu b4dd19b9c0 Translate Marathi teams and attachment storage.
Translate 50 remaining Marathi placeholders covering teams and organizations, Node heap and memory metrics, legal notices, checklist actions, subtasks, and attachment movement across filesystem, GridFS, and S3 storage. Extend regression coverage for Node memory terminology, URLs, ordering, and storage providers.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:23:04 +03:00
Lauri Ojansivu 2baa0a9986 Translate Marathi recovery reports and tickets.
Translate 50 remaining Marathi placeholders covering office and REST API reports, recovery maintenance, swimlanes, wait spinners, card sizing, deletion safeguards, tickets, requests, and statuses. Extend regression coverage for REST API, WITH_API, MongoDB, spinner, and carbon-copy literals.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:21:39 +03:00
Lauri Ojansivu 802c239932 Translate Marathi maps and diagnostic reports.
Translate 50 remaining Marathi placeholders covering map detection, server troubleshooting, sorting, activities, string templates, diagnostic reports, problem acknowledgement, impersonation, recovery, and office logins. Extend regression coverage for troubleshooting commands, template tokens, space entities, and IPv4 and IPv6 literals.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:20:05 +03:00
Lauri Ojansivu d7a13b231f Translate Marathi dependencies and board backgrounds.
Translate 50 remaining Marathi placeholders covering sorting, completion, stickers, dependency types and imports, board backgrounds, and card map locations. Extend regression coverage for sort direction, JSON and SVG formats, dependency-import tokens, background size, and location vocabulary.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:18:48 +03:00
Lauri Ojansivu 1870541071 Translate Marathi global search instructions.
Translate 50 remaining Marathi placeholders covering search predicates, validation, pagination, full operator syntax, status and presence filters, sorting, limits, combinations, and date ranges. Extend regression coverage for operator, presence-filter, and period-predicate token inventories.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:17:10 +03:00
Lauri Ojansivu 934ff77932 Translate Marathi global search operators.
Translate 50 remaining Marathi placeholders covering missing-entity diagnostics, paginated result counts, global-search operators, and archive, date, content, checklist, and assignee predicates. Extend regression coverage for pagination tokens and Marathi operator and predicate vocabulary.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:15:19 +03:00
Lauri Ojansivu 858c330e07 Translate Marathi shared templates and card views.
Translate 50 remaining Marathi placeholders covering linked-list safety, checklist visibility, tasks, domains, shared templates, My Cards and Due Cards, global search scope, and missing-entity diagnostics. Extend regression coverage for the domain example, My Cards label, and diagnostic format tokens.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:14:04 +03:00
Lauri Ojansivu 9e5567abb3 Translate Marathi role status and weekday settings.
Translate 50 remaining Marathi placeholders covering multi-card windows, keyboard submission, entity dialogs, notification state, renaming, board-role permissions, weekdays, ownership, voting, and linked-card deletion safety. Extend regression coverage for keyboard chords, role terminology, and weekday names.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:12:38 +03:00
Lauri Ojansivu cfbd815dbd Translate Marathi authentication and due reminders.
Translate 50 remaining Marathi placeholders covering custom body HTML, authentication and OIDC, board duplication, swimlane deletion, date activity and reminders, placement controls, destructive account actions, and resizable navigation. Extend regression coverage for body tags, OIDC, date tokens, and mention tokens.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:10:48 +03:00
Lauri Ojansivu bcc1893988 Translate Marathi generated rules and web customization.
Translate 50 remaining Marathi placeholders covering generated rule actions, checklist syntax, date fields, card links, authentication, custom product and head tags, web manifests, asset links, layout, logos, and counters. Extend regression coverage for comma-separated items and HTML, JSON, and assetlinks literals.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:09:12 +03:00
Lauri Ojansivu ab06638a74 Translate Marathi rule actions and triggers.
Translate 50 remaining Marathi placeholders covering board and list movement, archive restoration, label, member, attachment, and checklist triggers, card movement, membership, colors, checklist actions, email, and generated rule descriptions. Extend regression coverage for core Marathi rule vocabulary.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:07:48 +03:00
Lauri Ojansivu 6c06d8921f Translate Marathi scheduled rule workflows.
Translate 50 remaining Marathi placeholders covering visual workflow imports, scheduled and button triggers, due and list-age conditions, sorting, completion, bulk moves, relative dates, and time units. Extend regression coverage for workflow providers, count tokens, weekday ranges, and schedule units.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:06:41 +03:00
Lauri Ojansivu 961cecbb2c Translate Marathi rule workflows and activity.
Translate 50 remaining Marathi placeholders covering label, attachment, and custom-field activity, rule builders and triggers, scheduling, received dates, JSON and CSV rule transfer, Trello Butler imports, workspaces, and all-board scope. Extend regression coverage for activity, time, count, and rule-transfer tokens and literals.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:05:21 +03:00
Lauri Ojansivu 0cc6ee7635 Translate Marathi card dates and parent settings.
Translate 50 remaining Marathi placeholders covering received and end dates, colors, assignment, destructive confirmations, subtask and card settings, minicard details, parent-card paths, and label activity. Extend regression coverage for board and format tokens, checklist counts, and parent terminology.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:03:59 +03:00
Lauri Ojansivu 8d039059f7 Translate Marathi runtime and organization settings.
Translate 50 remaining Marathi placeholders covering database provenance, reactivity, DDP, operating-system metrics, custom fields, visibility, organization and team state, auth sync, multitenancy, domains, and scoped administration. Extend regression coverage for runtime identifiers, host examples, and the multitenancy setting.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:02:32 +03:00
Lauri Ojansivu b4ae51589e Translate Marathi transfer limits and webhooks.
Translate 50 remaining Marathi placeholders covering attachment and API limits, avatar uploads, registration, SMTP and TLS, invitations, authorization, webhooks, card filters, and runtime labels. Extend regression coverage for invitation tokens and API, SMTP, TLS, Node, and Meteor terminology.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 12:01:10 +03:00
Lauri Ojansivu 371af801ba Translate Marathi tracking and branded links.
Translate 50 remaining Marathi placeholders covering starred boards, tracking, time, keyboard assignment slots, uploads, branded logos and links, URL schemes, welcome templates, watching, and WIP errors. Extend regression coverage for numeric slots, logo height, URL terminology, and WIP wording.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:59:43 +03:00
Lauri Ojansivu 515adbaeb0 Translate Marathi privacy and keyboard shortcuts.
Translate 50 remaining Marathi placeholders covering watch notifications, private pages, previews, public and private boards, member removal, Sandstorm access, description rescue, search, WIP limits, keyboard shortcuts, sidebars, signup, and default boards. Extend regression coverage for login markup, member tokens, Sandstorm, and WIP.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:58:25 +03:00
Lauri Ojansivu d82a8cfd1b Translate Marathi board roles and bulk selection.
Translate 50 remaining Marathi placeholders covering leave-board safety, bulk list actions, administrative settings, imports, login, multi-selection, muted watching, archives, roles, and participation notifications. Extend regression coverage for the board-title token, import formats, movement, selection, and My Boards labels.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:56:37 +03:00
Lauri Ojansivu 49e0e4f47a Translate Marathi Trello imports and member mapping.
Translate 50 remaining Marathi placeholders covering Trello ZIP safety, API imports, workspaces, progress and cancellation, imported-member mapping, validation messages, invitations, keyboard shortcuts, and labels. Extend regression coverage for the API URL, API terminology, year example, and label token.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:55:09 +03:00
Lauri Ojansivu 2860da7c9a Translate Marathi advanced filters and board imports.
Translate 50 remaining Marathi placeholders covering assignee and custom-field filters, advanced-filter syntax, imported-member status, and board-import guidance and failures across supported formats. Preserve filter operators, source placeholders, endpoints, file extensions, and data-format names with regression coverage.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:53:26 +03:00
Lauri Ojansivu 35e439d4b7 Translate Marathi card exports and list filters.
Translate 50 remaining Marathi placeholders covering account conflicts, PDF and Excel card exports, disk-space reporting, export fields, list sorting, and date, title, label, and member filters. Extend regression coverage for export formats, board-field terminology, and filter vocabulary.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:51:15 +03:00
Lauri Ojansivu e041159835 Translate Marathi account emails and validation errors.
Translate 50 remaining Marathi placeholders covering permanent deletion, profile and WIP settings, account emails, scrollbars, authorization and existence errors, data-format validation, empty imports, and linked-card safeguards. Extend regression coverage for email tokens and WIP, JSON, CSV, and TSV literals.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:49:57 +03:00
Lauri Ojansivu 3237440e0b Translate Marathi roles and custom fields.
Translate 50 remaining Marathi placeholders covering colors, restricted roles, deletion confirmations, clipboard actions, multi-card templates, labels, and custom-field types. Extend regression coverage for Marathi role and color vocabulary and the embedded JSON schema.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:48:17 +03:00
Lauri Ojansivu 2e3c0a558e Translate Marathi card aging and interface colors.
Translate 50 remaining Marathi placeholders covering starring, automatic list widths, clipboard input, card aging, movement controls, accessible dialogs, board restoration, and interface colors. Extend regression coverage for tier counts, movement labels, and Marathi color terminology.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:46:46 +03:00
Lauri Ojansivu 24f575abc5 Translate Marathi member mapping and appearance settings.
Translate 50 remaining Marathi placeholders covering member dialogs, restored items, rule transfer, linked cards and boards, imported-member mapping, themes, fonts, colors, avatars, languages, and permissions. Extend regression coverage for CAS, linked-board vocabulary, mapping semantics, and font-preview digits.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:45:34 +03:00
Lauri Ojansivu a94646198e Translate Marathi voting and import dialogs.
Translate 50 remaining Marathi placeholders covering card membership, voting, Planning Poker, dependencies, organization and team assignment, account and background administration, member mapping, and imports. Extend regression coverage for Planning Poker and Marathi dependency, checklist, and swimlane terminology.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:44:02 +03:00
Lauri Ojansivu de0ce7b02e Translate Marathi board views and archive guidance.
Translate 50 remaining Marathi placeholders covering public boards, workspace movement, appearance, view modes, zoom, calendar navigation, archive and deletion guidance, and card editing. Extend regression coverage for HTML, workspace, zoom, format, and custom-field values.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:42:54 +03:00
Lauri Ojansivu 68a34e9fe0 Translate Marathi archives and board presentation.
Translate 50 remaining Marathi placeholders covering administration, announcements, archives, templates, attachment deletion, board backgrounds, member summaries, stars, and private visibility. Extend regression coverage for count, size, format, URL, and HTML tokens.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:41:17 +03:00
Lauri Ojansivu e39c5c6239 Translate Marathi board layout and selection controls.
Translate 50 remaining Marathi placeholders covering workspace deletion, board selection, Home boards, list widths, keyboard shortcuts, swimlane heights, checklists, labels, and members. Extend regression coverage for date tokens, numeric limits, layout units, and Marathi UI vocabulary.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 5aa3a66b28 Translate Marathi activity summaries and workspaces.
Translate 50 remaining Marathi placeholders covering card movement and restoration, concise activity summaries, checklist actions, dates, and All Boards workspace management. Extend regression coverage for cross-board tokens, format placeholders, checklist history, and workspace vocabulary.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 200f0c6fa5 Start direct Marathi activity translations.
Translate the first 50 Marathi placeholders covering board, list, swimlane, card, label, checklist, comment, attachment, subtask, and custom-field activity history. Add locale-wide placeholder and HTML-tag regression coverage with targeted Marathi vocabulary checks.

These direct translations have low confidence and welcome review by a Marathi speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu a46b334232 Complete remaining Malayalam translations.
Translate the final 17 Malayalam placeholders covering event IP fields, filesystem integrity, scoped import and export, the numbered-card search operator, and WeKan import formats. Preserve the search placeholder, HTML-like number markers, protocol labels, and file extensions with regression coverage.

These direct translations have low confidence and welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 1b4be861d5 Translate Malayalam repositories and repair reports.
Translate 50 remaining Malayalam placeholders covering account and OTP flows, repository uploads, API endpoints, problem acknowledgement, broken-card repair, CPU load, and event fields. Preserve repair placeholders and technical OTP, API, and CPU literals with regression coverage.

These direct translations have low confidence and welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 5fddc2c735 Translate Malayalam migration controls and system monitoring.
Translate 50 remaining Malayalam placeholders covering bulk storage migrations, processing thresholds, migration logs, monitoring navigation, minicard settings, storage distribution, and system resources. Preserve provider names and numeric ranges with regression coverage.

These direct translations have low confidence and welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 7e3df03352 Translate Malayalam migration monitoring and job queues.
Translate 50 remaining Malayalam placeholders covering board-repair steps, conversion status, runtime metrics, recurring intervals, attachment monitoring, storage sizes, and job queues. Preserve URL, ID, CPU, GridFS, and interval literals with regression coverage.

These direct translations have low confidence and welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu dc89a723e8 Translate Malayalam board repair migrations.
Translate 50 remaining Malayalam placeholders covering comprehensive board repair, duplicate-list cleanup, lost-card restoration, URL repair, migration confirmations, and structural repair steps. Preserve the swimlaneId, listId, and URL identifiers with regression coverage.

These direct translations have low confidence and welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu a7db6b9fe4 Translate Malayalam migrations and S3 settings.
Translate 50 remaining Malayalam placeholders covering GCS attachment movement, GridFS and CollectionFS, migration controls, S3 authentication, scheduled board operations, and attachment monitoring. Preserve storage-provider, region, and transport-security literals with regression coverage.

These direct translations have low confidence and welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 9b40b16a9f Translate Malayalam backup schedules and cloud storage.
Translate 50 remaining Malayalam placeholders covering backup schedules and restore modes, GCS credentials and permissions, cloud-provider setup paths, connection tests, and Azure attachment movement. Preserve schedule formats, identifiers, and provider terminology with regression coverage.

These direct translations have low confidence and welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 8061d3a3e5 Translate Malayalam privacy controls and backups.
Translate 50 remaining Malayalam placeholders covering Sandstorm cleanup, card-loading modes, secure text rendering, import and export privacy controls, activity settings, and backups. Preserve HTML, environment-variable names, and backup path formats with regression coverage.

These direct translations have low confidence and welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 53544f14f0 Translate Malayalam storage and database migration.
Translate 50 remaining Malayalam placeholders covering scheduled-job recovery, filesystem and cloud storage, database migration, and Sandstorm migration status. Preserve the database placeholder, connection URLs, ports, environment variables, and Sandstorm storage paths with regression coverage.

These direct translations have low confidence and welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 31a102df3f Translate Malayalam account protection and scheduled jobs.
Translate 50 remaining Malayalam placeholders covering accessibility, brute-force lockout controls, scheduled jobs, storage paths, and board maintenance scheduling. Add regression coverage for the security terminology and operational labels.

These direct translations have low confidence and welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 4ffc399f41 Translate Malayalam board status and accessibility.
Translate 50 remaining Malayalam placeholders covering board status and time summaries, upload constraints, custom translations, checklist display, support, and accessibility. Preserve the workspace placeholder and technical PDF, ISO 8601, JSON, and .zip literals with regression coverage.

These direct translations have low confidence and welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 34e4218aa9 Translate Malayalam attachment repair and compaction.
Translate 50 remaining Malayalam values covering bulk attachment movement, storage-location repair, default storage, file statistics and identifiers, and MongoDB GridFS compaction guidance. Preserve every backend, database, oplog and Meteor term and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu fad03ff7ce Translate Malayalam teams and attachment storage.
Translate 50 remaining Malayalam values covering teams and organizations, invitations, Node heap and memory metrics, legal notices, checklist actions, subtasks, and attachment movement across filesystem, GridFS and S3. Preserve storage and runtime terms and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu b5ee1b97f9 Translate Malayalam API and recovery reports.
Translate 50 remaining Malayalam values covering Office and REST API reports, recovery maintenance, swimlane copying, wait spinners, card sizing, deletion safeguards, tickets, requests, and statuses. Preserve REST, environment, database and mail terms and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu f0c52c0154 Translate Malayalam maps and problem reports.
Translate 50 remaining Malayalam values covering map detection, server troubleshooting, sorting, board activities, custom-field templates, operational reports, and office-login summaries. Preserve commands, format placeholders, HTML entities, IPv4 and IPv6 terms and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 64c608e773 Translate Malayalam dependencies and board backgrounds.
Translate 50 remaining Malayalam values covering card and board sorting, completion, stickers, dependency relationships and JSON/SVG import, board backgrounds, and card locations. Preserve dependency counts and background-size placeholders and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu ee24144f67 Translate Malayalam advanced search guidance.
Translate 50 remaining Malayalam values covering predicate validation, pagination, and complete advanced-search operator, status, existence, sorting, limit, and combination guidance. Preserve every embedded operator and predicate token and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 1a2dfc0485 Translate Malayalam global search operators.
Translate 50 remaining Malayalam values covering missing entities, card-result totals, and global-search operators and predicates for boards, users, dates, text, attachments, and checklists. Preserve every paging and format placeholder and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu c24858af18 Translate Malayalam shared templates and card views.
Translate 50 remaining Malayalam values covering linked-card safeguards, checklist display, domain-scoped shared templates, My Cards, Due Cards, global search, and missing-item messages. Preserve search emphasis and every format placeholder and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu b701e6c21c Translate Malayalam roles and editor behavior.
Translate 50 remaining Malayalam values covering multi-card and editor behavior, entity dialogs, notifications, board-role permissions, weekdays, status and ownership, voting, and linked-card deletion safeguards. Preserve keyboard chord names and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 9dc958bb63 Translate Malayalam date reminders and layout controls.
Translate 50 remaining Malayalam values covering member lists, custom body HTML, authentication display, duplication, counts, swimlane deletion, card date reminders and placement, account deletion, resizing, and editor submission. Preserve HTML tags and every date, card and board placeholder and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 8d8f278ff7 Translate Malayalam rule actions and custom manifests.
Translate 50 remaining Malayalam values covering concrete rule actions, checklist and swimlane creation, date fields, card links, authentication, custom product and head tags, web manifests, assetlinks, layout, and counters. Preserve HTML, JSON and assetlinks terms and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 90f8a95a08 Translate Malayalam rule action grammar.
Translate 50 remaining Malayalam values covering visual-rule grammar for movement, archives, labels, members, attachments, checklists, colors, list positions, and email actions. Extend locale regression coverage for the concrete rule phrases. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu f525d36186 Translate Malayalam scheduled rule workflows.
Translate 50 remaining Malayalam values covering imported n8n and Node-RED workflows, scheduled and button triggers, due-date events, sorting, completion, bulk movement, relative dates, and rule time units. Preserve the count placeholder and product names and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 3b0bb2d24a Translate Malayalam activity and rule workflows.
Translate 50 remaining Malayalam values covering label, attachment and custom-field activities, visual rule creation, triggers, workflow views, event conditions, and JSON, CSV and Trello Butler import/export. Preserve every format and rule placeholder and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 2ea35cc7b9 Translate Malayalam card dates and subtask settings.
Translate 50 remaining Malayalam values covering card dates and colors, assignment metadata, permanent deletion safeguards, duplicate cleanup, subtask/card settings, minicard badges, and parent-card display. Preserve board and format placeholders and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 129593d0f6 Translate Malayalam metrics and tenancy controls.
Translate 50 remaining Malayalam values covering database and runtime metrics, reactivity, DDP, operating-system details, custom-field display, account changes, visibility, and organization/team tenancy. Preserve environment names, host examples and product terms and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu e388d28c24 Translate Malayalam transfer limits and webhooks.
Translate 50 remaining Malayalam values covering attachment and API limits, avatar policy, registration, invitations, SMTP, authorization errors, webhooks, packages, databases, Node, and Meteor. Preserve all email placeholders and technical product terms and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu dff9d87447 Translate Malayalam tracking and branding controls.
Translate 50 remaining Malayalam values covering starred boards, time tracking, keyboard toggles, uploads, custom branding and URL schemes, watching, welcome and template boards, and WIP-limit guidance. Preserve numeric and product terms and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 805992681d Translate Malayalam privacy and shortcut controls.
Translate 50 remaining Malayalam values covering watching, page visibility, previews, member removal, Sandstorm access, card-description rescue, search, WIP limits, keyboard shortcuts, sidebars, signup, and default boards. Preserve HTML, format and member placeholders and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 611194f931 Translate Malayalam board roles and selection controls.
Translate 50 remaining Malayalam values covering board departure, list archival, swimlane and organization settings, card/list links, multi-selection, muted watching, archive states, board roles, and notifications. Preserve the board-title placeholder and import product/file-format terms, and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu c48532be02 Translate Malayalam Trello imports and member mapping.
Translate 50 remaining Malayalam values covering Trello API workspaces, multi-board imports, cancellation and recovery, imported-member mapping, date validation, invitations, shortcuts, and labels. Preserve the API URL, percent placeholder, product names, and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
Lauri Ojansivu 8c8f51b779 Translate Malayalam filters and board imports.
Translate 50 remaining Malayalam values covering assignment and custom-field filters, activity visibility, imported-member states, multi-format board-import guidance, and Trello JSON/ZIP validation. Preserve every source placeholder, operator and product/file-format term, and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 11:41:02 +03:00
github-actions[bot] 13baf07c5b Bump versions for v11.16 2026-08-27 07:31:00 +00:00
Lauri Ojansivu aefc07c924 Prepare v11.16 release 2026-08-27 10:29:45 +03:00
Lauri Ojansivu 5370e3c12b Document SessionBleed in upcoming release
Lead the Upcoming notes with the owner-scoped global-search pagination fix, its disclosure credit, monitoring behavior, and regression evidence.

Thanks to crypto-nidh and xet7 !
2026-08-27 10:21:24 +03:00
Lauri Ojansivu 18bcbdd0d4 Bind search pagination sessions to their users
Require both pagination publications to authenticate the subscriber and load only a search session owned by that same user. Record attributable logged-out probes as SessionBleed without letting logging affect the denial.

Thanks to crypto-nidh and xet7 !
2026-08-27 10:19:56 +03:00
Lauri Ojansivu e63abd1545 Document restored card detail gutters
Add the #6638 fix and its regression evidence to the Upcoming WeKan release notes.

Thanks to rmb82 and xet7 !
2026-08-27 10:15:22 +03:00
Lauri Ojansivu b26c6d3c59 Restore card detail content gutters
Keep the visible card header and body inside the padded card-details canvas, and verify the real content edges on the widened desktop panel instead of only checking an empty wrapper's computed style.

Thanks to rmb82 and xet7 !

Fixes #6638,
2026-08-27 10:14:46 +03:00
Lauri Ojansivu e54db47bc5 Translate Malayalam card exports and filters.
Translate 50 remaining Malayalam values covering user, organization, team and email validation, card PDF and Excel exports, attachment metadata, list sorting, and date, label and member filters. Preserve PDF and Excel format names and extend locale regression coverage for export and filtering terminology. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 10:05:53 +03:00
Lauri Ojansivu f626841670 Translate Malayalam account emails and errors.
Translate 50 remaining Malayalam values covering custom text and date formats, permanent deletion, WIP and date and time editing, profiles, localized account emails, scrolling, and board, JSON, CSV, TSV, import, linked-card and user errors. Preserve every email placeholder, acronym, and product name and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 10:04:15 +03:00
Lauri Ojansivu aca74a0d9a Translate Malayalam roles and custom fields.
Translate 50 remaining Malayalam values covering the remaining initial colors, comment, read-only and worker roles, subtask and checklist deletion, list movement, clipboard links, bulk-card JSON, templates, labels, and custom-field types. Preserve valid embedded JSON structure and property names and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 10:02:31 +03:00
Lauri Ojansivu 06e1da8718 Translate Malayalam navigation and board colors.
Translate 50 remaining Malayalam values covering starring, automatic list widths, clipboard and drag-and-drop actions, card aging tiers, keyboard movement, accessible closing and navigation controls, and most of the board color palette. Preserve the three aging levels and archive recovery semantics and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 10:00:15 +03:00
Lauri Ojansivu 04fed79720 Translate Malayalam member mapping and themes.
Translate 50 remaining Malayalam values covering member and restoration dialogs, rule exchange, bookmarks, templates, CAS, linked cards and boards, safe imported-member mapping, themes, fonts, text colors, avatars, language, and permissions. Preserve CAS and the numeric font preview and extend locale regression coverage for identity-mapping semantics. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 09:58:12 +03:00
Lauri Ojansivu f67c4db740 Translate Malayalam voting and import dialogs.
Translate 50 remaining Malayalam values covering card members and dates, voting and Planning Poker, dependencies, organization, team and domain dialogs, avatars, backgrounds, accounts, imported-member mapping, and imports. Preserve Planning Poker terminology and extend locale regression coverage for dependency, checklist and swimlane operations. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 09:56:04 +03:00
Lauri Ojansivu a1aece4c08 Translate Malayalam board views and archives.
Translate 50 remaining Malayalam values covering public-board guidance, drag-and-drop workspace assignment, appearance and board views, zoom and calendars, archive and deletion guidance, dates, time, and card editing. Preserve public markup, workspace, zoom and comment placeholders and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 09:54:03 +03:00
Lauri Ojansivu ca033231bb Translate Malayalam board administration.
Translate 50 remaining Malayalam values covering administrator roles and announcements, public and offline states, board, list and swimlane archives, templates, attachments, backgrounds, and All Boards member and assignee settings. Preserve count, avatar-size, URL and star placeholders and privacy markup, and extend locale regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 09:52:04 +03:00
Lauri Ojansivu a1b8920f70 Translate Malayalam workspace and layout controls.
Translate 50 remaining Malayalam values covering workspace deletion, multi-board selection, Home-board controls, dates, templates, list widths, keyboard shortcuts, swimlane height, subtasks, checklists, covers, labels, and members. Preserve date placeholders and numeric layout limits and extend locale regression coverage for width and pixel terminology. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 09:49:45 +03:00
Lauri Ojansivu 1b0cda52b5 Translate Malayalam activity and workspaces.
Translate 50 remaining Malayalam values covering card movement and restoration, parameterized activity, checklist, comment, received-date and start-date events, and workspace navigation and settings. Preserve cross-board, checklist and percent placeholders exactly and extend locale regression coverage for workspace terminology and markdown. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 09:47:19 +03:00
Lauri Ojansivu 80d3997515 Begin the Malayalam translation.
Translate the first 50 Malayalam placeholders covering organization and team membership, replies, due-date changes, and detailed activity for boards, swimlanes, lists, cards, attachments, subtasks, labels, checklists, comments, custom fields, archives, and imports. Preserve every percent and underscore-delimited placeholder exactly and add locale-wide token, tag, script, count, and terminology regression coverage. These low-confidence direct translations welcome review by a Malayalam speaker.

Thanks to xet7 !
2026-08-27 09:45:19 +03:00
Lauri Ojansivu c9743b01c7 Complete the Māori translation.
Translate the final 67 Māori placeholders covering account and repository access, problem and repair reporting, CPU and event metadata, filesystem integrity, scoped import and export, and numeric global search. Preserve OTP, API, IP versions, CPU, file extensions, product names, and every repair and search placeholder exactly, and make the locale regression test prove that no Māori placeholders remain. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:42:07 +03:00
Lauri Ojansivu 2afe114a07 Translate Māori migration resource controls.
Translate 50 remaining Māori values covering migration batching and resource thresholds, logs, monitoring navigation, attachment totals, storage distribution, and minicard display. Preserve GridFS, S3, CPU, percent and millisecond notation, and every configuration range, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:39:57 +03:00
Lauri Ojansivu 3472818685 Translate Māori migration monitoring.
Translate 50 remaining Māori values covering migration repair steps, board conversion, CPU and timing metrics, export monitoring, filesystem and GridFS statistics, and job-queue details. Preserve URL, ID, CPU, GridFS, and every scheduled interval and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:38:28 +03:00
Lauri Ojansivu 2333ea2f00 Translate Māori board integrity migrations.
Translate 50 remaining Māori values covering board-integrity migrations for duplicate lists, lost cards, archives, missing structures, file and avatar URLs, confirmations, progress, and repair steps. Preserve swimlaneId, listId, URL, and ID syntax and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:37:20 +03:00
Lauri Ojansivu 52217a77d6 Translate Māori migration and S3 controls.
Translate 50 remaining Māori values covering Google Cloud and GridFS storage, migration lifecycle controls, CollectionFS guidance, S3 credentials and TLS, scheduled board operations, writable paths, and attachment and board migration settings. Preserve backend, protocol, and region identifiers and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:35:30 +03:00
Lauri Ojansivu 7c1e2ecff2 Translate Māori backup and cloud storage guidance.
Translate 50 remaining Māori values covering backup scheduling and restoration, Google Cloud service accounts, S3, Azure and GCS credentials, console navigation, connection testing, and Azure attachment storage. Preserve formats, JSON, provider names, filenames, quoted labels, and official console paths, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:34:23 +03:00
Lauri Ojansivu 6e0c6264f0 Translate Māori security and backup controls.
Translate 50 remaining Māori values covering Sandstorm cleanup, lazy card loading, secure text rendering, import and export restrictions, user anonymization, activities, notifications, watch controls, and scoped backups. Preserve security examples, integration names, environment variables, mention syntax, paths, and cloud backends exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:32:48 +03:00
Lauri Ojansivu b3d3f7062c Translate Māori scheduled migrations and databases.
Translate 50 remaining Māori values covering scheduled-job and migration controls, filesystem, S3 and Azure storage, MongoDB and FerretDB migration, and Sandstorm migration status. Preserve backend names, versions, URLs, environment variables, commands, paths, and the database placeholder exactly, and extend locale regression coverage for that operational syntax. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:30:11 +03:00
Lauri Ojansivu 18a7a73448 Translate Māori account lockout settings.
Translate 50 remaining Māori values covering accessibility content, brute-force lockout policy and unlock actions, active-user filtering, scheduled jobs, attachment paths, and scheduled board operations. Preserve the security distinctions between known and unknown users and between lockout and failure windows, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:28:14 +03:00
Lauri Ojansivu a9f870bffe Translate Māori status and support settings.
Translate 50 remaining Māori values covering board status, transfer progress, upload and avatar constraints, board dragging, custom translations, checklist display, support, and accessibility. Preserve Mongo, PDF, workspace, ISO 8601, ZIP, and JSON syntax and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:26:58 +03:00
Lauri Ojansivu 42780c50bd Translate Māori attachment storage administration.
Translate 50 remaining Māori values covering bulk attachment migration, file-location repair, storage selection and statistics, identifiers, and MongoDB Compact guidance. Reuse established avatar terminology, preserve GridFS, S3, MongoDB, Compact, oplog, Meteor, and ID technical terms, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:25:39 +03:00
Lauri Ojansivu dee9f23224 Translate Māori teams and storage controls.
Translate 50 remaining Māori values covering team and organization management, Node heap and memory diagnostics, legal notices, checklist conversion and movement, and attachment storage actions. Preserve Node, malloc, URL, GridFS, and S3 technical terms and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:24:05 +03:00
Lauri Ojansivu 156431b7e1 Translate Māori recovery and support reports.
Translate 50 remaining Māori values covering office, REST API, and recovery reports, recovery maintenance, swimlane copying, wait animations, deletion safeguards, and support-ticket states. Preserve REST API, WITH_API=true, MongoDB, and Cc literals and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:22:38 +03:00
Lauri Ojansivu 0fb46487fd Translate Māori maps and reports.
Translate 50 remaining Māori values covering map coordinates and detection, server troubleshooting, activity visibility, swimlane movement, string-template fields, and administrative reports. Preserve the literal snap and Docker troubleshooting commands and the string-template placeholder, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:21:23 +03:00
Lauri Ojansivu 7b39289ea1 Translate Māori card organization controls.
Translate 50 remaining Māori values covering board and card sorting, completion, stickers, dependency relationships and imports, board backgrounds, and card locations. Preserve dependency import counters and the background size token exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:19:52 +03:00
Lauri Ojansivu 63c63e8a9b Translate Māori global search instructions.
Translate 50 remaining Māori values covering search predicates, operator validation, pagination, and global-search guidance for scopes, users, dates, statuses, field existence, sorting, limits, and combined conditions. Preserve every operator and predicate placeholder exactly and extend locale regression coverage for the translated search syntax. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:17:38 +03:00
Lauri Ojansivu 3bbfc53e69 Translate Māori global search vocabulary.
Translate 50 remaining Māori values covering user, comment, organization and team lookup errors, card-search result counts, and global-search operators and predicates for boards, swimlanes, lists, users, dates, content, status and time ranges. Preserve result placeholders exactly and extend locale regression coverage for the Māori search vocabulary. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:12:26 +03:00
Lauri Ojansivu f2044c5d36 Translate Māori shared views and domains.
Translate 50 remaining Māori values covering linked-list safeguards, tasks, domains, shared templates, names, people and time units, My Cards and Due Cards views and sorting, global-search scope, and board, swimlane, list and label lookup errors. Preserve domain syntax, emphasized search labels and lookup placeholders exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:10:27 +03:00
Lauri Ojansivu 5bffd4a0b1 Translate Māori board roles and editor controls.
Translate 50 remaining Māori values covering multi-card windows, editor shortcuts, card and minicard fields, organization, team and user dialogs, notification states, rename and board-role controls, weekdays, status, ownership, activity and voting. Preserve keyboard shortcut sequences exactly and extend locale regression coverage for role and weekday terminology. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:08:02 +03:00
Lauri Ojansivu 7d8b0c1dcd Translate Māori authentication and reminders.
Translate 50 remaining Māori values covering custom body HTML, authentication and OIDC controls, board duplication, organization, team and person counts, swimlane deletion, card placement, due-date activity and reminders, account, team and organization deletion, and editor and layout controls. Preserve HTML tags, OIDC, reminder placeholders and activity tokens exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:06:24 +03:00
Lauri Ojansivu 4a97f717cf Translate Māori rule actions and manifests.
Translate 50 remaining Māori values covering detailed rule actions for email, archive, labels, members, checklists, cards, swimlanes and relative dates, authentication, product naming, custom head HTML, web manifests, assetlinks.json, layout and card counters. Preserve structured example values, HTML, JSON and filenames exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:04:48 +03:00
Lauri Ojansivu c140d74dd1 Translate Māori rule phrase vocabulary.
Translate 50 remaining Māori values covering rule phrases for boards, lists, cards, labels, members, attachments, checklists and items, archive and movement states, color, member and checklist actions, email, and detailed top and bottom card moves. Extend locale regression coverage for core rule vocabulary and movement phrasing. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:03:34 +03:00
Lauri Ojansivu 68df44f2d7 Translate Māori workflow schedules and actions.
Translate 50 remaining Māori values covering n8n and Node-RED workflow imports, scheduled and button triggers, due-date and list-duration conditions, card and board buttons, list sorting, completion and movement actions, relative dates, and rule phrase units. Preserve workflow products, count placeholders, weekday ranges and symbolic N exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:02:02 +03:00
Lauri Ojansivu 5e8ba72ab8 Translate Māori activity and rule triggers.
Translate 50 remaining Māori values covering label, attachment and custom-field activity, visual rule editing, card, label, member, checklist, attachment and schedule triggers, and JSON, CSV and Trello Butler rule exchange. Preserve activity and schedule placeholders, file formats and product identifiers exactly, and extend locale regression coverage without imposing English word order. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 09:00:54 +03:00
Lauri Ojansivu c94ab85963 Translate Māori card metadata and safeguards.
Translate 50 remaining Māori values covering received and end dates, colors, assignment metadata, card sorting, permanent board and notification deletion, duplicate-list cleanup, subtask and card settings, minicard fields, attachment and checklist counts, parent paths, and label activity. Preserve board and activity placeholders and display ratios exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:59:26 +03:00
Lauri Ojansivu 91b59c1aba Translate Māori runtime and tenancy settings.
Translate 50 remaining Māori values covering database and FerretDB identity, reactivity and DDP configuration, operating-system diagnostics, time units, custom-field display, account changes, visibility, and organization and team tenancy, domains, administration and synchronization. Preserve runtime modes, environment variables, hostnames and product identifiers exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:58:20 +03:00
Lauri Ojansivu 2468085de9 Translate Māori registration and webhooks.
Translate 50 remaining Māori values covering attachment and API transfer limits, avatar upload blocking, registration and invitations, SMTP and TLS settings and test mail, localized registration email, authorization, webhooks, packages, database, and Node and Meteor versions. Preserve placeholders, product names and protocol identifiers exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:56:57 +03:00
Lauri Ojansivu 49b7e66a3f Translate Māori tracking and templates.
Translate 50 remaining Māori values covering starred boards and subscriptions, spent time and overtime, numbered assignee and label shortcuts, tracking, uploads, custom logos and URL schemes, username imports, watching, welcome and template boards, and WIP limit errors. Preserve numeric ranges, URL terminology and WIP identifiers exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:55:48 +03:00
Lauri Ojansivu a51f5868e5 Translate Māori visibility and shortcuts.
Translate 50 remaining Māori values covering watched updates, private-page login, image previews, board visibility, quick access, member removal and Sandstorm guidance, unsaved-description rescue, search, WIP limits, keyboard shortcuts, sidebars, signup, and starred and default-board controls. Preserve link tags, placeholders, product names and WIP terminology exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:54:18 +03:00
Lauri Ojansivu eae5b80e11 Translate Māori board roles and bulk actions.
Translate 50 remaining Māori values covering board leaving, list archive and bulk-card actions, user, team and organization settings, Trello and Excel imports, list deletion, navigation, multi-selection, notification muting, archives, and normal roles. Preserve board placeholders and import terminology exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:52:57 +03:00
Lauri Ojansivu dfd7b20b22 Translate Māori Trello import controls.
Translate 50 remaining Māori values covering Trello ZIP path safeguards, workspace placement, direct API imports, cancellable and resumable import jobs, member mapping, version and validation labels, invitations, keyboard shortcuts, and labels. Preserve URLs, API terminology, file formats, years and placeholders exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:51:39 +03:00
Lauri Ojansivu c93bfd4cef Translate Māori filters and board imports.
Translate 50 remaining Māori values covering assignee, custom-field and archive filters, advanced filter syntax, member states and impersonation, Kanboard, NextCloud Deck, OpenProject, Asana, ZenKit, Trello, Jira, Excel and WeKan board imports, and safe Trello ZIP handling. Preserve expressions, endpoints, placeholders, product names and file formats exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:50:06 +03:00
Lauri Ojansivu ede896313f Translate Māori exports and filters.
Translate 50 remaining Māori values covering user, organization, team and email validation, card PDF and Excel exports, attachment metadata, list sorting, and date, label and member filters. Preserve product and file-format terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:48:09 +03:00
Lauri Ojansivu 3632461f0c Translate Māori account mail and errors.
Translate 50 remaining Māori values covering custom formats, permanent deletion, WIP controls, card dates and time, profiles, localized account email templates, scrolling, and board, JSON, CSV, TSV, import, linked-card and user errors. Preserve every mail placeholder and product or data-format identifier exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:47:10 +03:00
Lauri Ojansivu 7267ed209a Translate Māori roles and custom fields.
Translate 50 remaining Māori values completing the board color palette and covering comment and read-only roles, worker permissions, subtask and checklist deletion, clipboard links, bulk-card JSON, templates, labels, and custom-field types and options. Preserve the bulk JSON schema and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:45:52 +03:00
Lauri Ojansivu 678f3d10b7 Translate Māori navigation and board colors.
Translate 50 remaining Māori values covering starring, automatic list widths, clipboard actions, card aging, keyboard movement, accessible dialog controls, board restoration, and most of the board color palette. Preserve numeric aging tiers and extend locale regression coverage for layout, movement, and color terminology. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:44:43 +03:00
Lauri Ojansivu 4f929bfe9b Translate Māori member and appearance settings.
Translate 50 remaining Māori values covering member and restoration dialogs, rule exchange, bookmarks, templates, CAS, linked cards and boards, imported-member mapping, themes, fonts, text colors, avatars, languages, and permissions. Preserve authentication and numeric preview terminology and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:43:46 +03:00
Lauri Ojansivu f5a06cc479 Translate Māori voting and imports.
Translate 50 remaining Māori values covering card members and dates, voting and Planning Poker, dependencies, organizations, teams, avatars, backgrounds, duplicate lists, accounts, domains, imported-member mapping, and checklist, swimlane, list, card and board imports. Preserve product terminology and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:42:31 +03:00
Lauri Ojansivu 76f1869139 Translate Māori board views and card editing.
Translate 50 remaining Māori values covering public-board guidance, workspace movement, appearance and views, zoom and calendars, permanent card deletion, archives, dates, time, and card attachment, field, label and member editing. Preserve workspace and comment placeholders, numeric ranges, and HTML tags exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:41:33 +03:00
Lauri Ojansivu b4939cff02 Translate Māori administration and archives.
Translate 50 remaining Māori values covering administrator roles and announcements, public boards, offline recovery, board, list and swimlane archives, templates, attachments, backgrounds, and All Boards member and assignee settings. Preserve count, size and format placeholders plus HTML tags exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:40:14 +03:00
Lauri Ojansivu 5ae715d3a5 Translate Māori board layout controls.
Translate 50 remaining Māori values covering workspace deletion, multi-board selection and Home boards, due and end dates, templates, list widths, keyboard shortcuts, swimlane heights, subtasks, checklists, covers, labels, and members. Preserve activity placeholders and numeric layout constraints exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:39:14 +03:00
Lauri Ojansivu 41ce0b0625 Translate Māori activity and workspaces.
Translate 50 remaining Māori values covering card movement and restoration, parameterized activity messages, checklist events, received and start dates, and workspace navigation and settings. Preserve every activity placeholder exactly and extend locale regression coverage for movement, imports, checklists, and workspace terminology. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:37:58 +03:00
Lauri Ojansivu ae9c0a1130 Begin the Māori translation.
Translate 50 remaining Māori values covering board membership rules, replies and due dates, and detailed activity messages for attachments, subtasks, labels, checklists, comments, custom fields, members, archives, and imports. Add locale-wide token and HTML preservation checks plus regression coverage for core Māori kanban terminology. These low-confidence direct translations welcome review by a Māori speaker.

Thanks to xet7 !
2026-08-27 08:36:44 +03:00
Lauri Ojansivu 8c20993bb2 Complete the Malagasy translation.
Translate the final 17 Malagasy values covering IP event details, filesystem integrity, scoped WeKan imports and exports, numeric global search, and import choices. Preserve product names, file extensions, address protocols, and the search placeholder exactly, and prove that Malagasy has no remaining placeholders. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:35:04 +03:00
Lauri Ojansivu 015c87413a Translate Malagasy repository and repair status.
Translate 50 remaining Malagasy values covering repositories, account access, login validation, problem reporting, broken-card repair, CPU status, and event metadata. Preserve OTP, API, CPU, and repair placeholders exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:34:18 +03:00
Lauri Ojansivu 5b0e166f12 Translate Malagasy migration monitoring.
Translate 50 remaining Malagasy values covering attachment migration targets, batch and resource thresholds, background processing, monitoring, schedules, progress, and storage statistics. Preserve CPU, GridFS, S3, units, numeric ranges, and operational identifiers exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:33:15 +03:00
Lauri Ojansivu 2e71b772c5 Translate Malagasy migration operations.
Translate 50 remaining Malagasy values covering repair steps, board conversion, CPU and filesystem metrics, schedules, GridFS monitoring, export monitoring, and job queue details. Preserve IDs, URLs, product names, numeric schedules, and storage identifiers exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:32:06 +03:00
Lauri Ojansivu 5f7e8a232b Translate Malagasy board repair migrations.
Translate 50 remaining Malagasy values covering comprehensive board repair, duplicate-list cleanup, lost-card and archive recovery, missing-list and file URL repair, migration confirmations, progress, and structure-repair steps. Preserve internal field names and URL terminology exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:31:10 +03:00
Lauri Ojansivu d8afac3810 Translate Malagasy storage migrations.
Translate 50 remaining Malagasy values covering Google Cloud and GridFS storage, migration lifecycle controls, CollectionFS movement, S3 authentication and TLS, scheduled board operations, writable storage paths, and attachment monitoring. Preserve provider names, identifiers, region values, and security protocols exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:30:02 +03:00
Lauri Ojansivu 09ee203762 Translate Malagasy cloud backup settings.
Translate 50 remaining Malagasy values covering backup schedules and restore modes, Google Cloud credentials and permissions, AWS S3, Azure and Google Cloud console paths, connection tests, and Azure attachment storage. Preserve schedule formats, provider names, console labels, identifiers, and file formats exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:29:00 +03:00
Lauri Ojansivu d8f0f707c0 Translate Malagasy privacy and backup controls.
Translate 50 remaining Malagasy values covering Sandstorm cleanup, adaptive card loading, safe rich-text rendering, import and export restrictions, identity anonymization, activity and notification controls, and streamed backups. Preserve HTML, storage paths, product names, and configuration identifiers exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:27:40 +03:00
Lauri Ojansivu bea02321f4 Translate Malagasy storage and migrations.
Translate 50 remaining Malagasy values covering scheduled-job recovery, filesystem, S3 and Azure storage, MongoDB and FerretDB data migration, and Sandstorm migration status. Preserve database URLs, configuration identifiers, paths, product names, and the __db__ placeholder exactly, and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:25:59 +03:00
Lauri Ojansivu 72e1635564 Translate Malagasy lockout and scheduled operations.
Translate 50 remaining Malagasy values covering accessibility content, brute-force login protection, locked-user administration, scheduled jobs, attachment and avatar paths, and scheduled board maintenance. Extend locale regression coverage for the security and operations terminology. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:24:35 +03:00
Lauri Ojansivu cbd8da7f2e Translate Malagasy status and support settings.
Translate 50 remaining Malagasy values covering board time status, upload progress and limits, account prompts, custom translations, board dragging, checklist display, support, and accessibility settings. Preserve the workspace placeholder and PDF, ISO 8601, ZIP, JSON, and markdown terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:21:47 +03:00
Lauri Ojansivu df927a52a5 Translate Malagasy storage repair and compaction.
Translate 50 remaining Malagasy values covering bulk attachment and avatar moves, storage repair and statistics, default storage, file IDs, and MongoDB GridFS compaction guidance and status. Preserve GridFS, S3, MongoDB, Compact, oplog, Meteor, and ID terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:20:48 +03:00
Lauri Ojansivu 046ca7a566 Translate Malagasy organization and attachment tools.
Translate 50 remaining Malagasy values covering team invitations, Node memory diagnostics, organization management, legal notices, checklist transformations, and attachment storage moves. Preserve Node, malloc, URL, GridFS, and S3 terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:19:45 +03:00
Lauri Ojansivu 91e4dc1cab Translate Malagasy API and recovery reports.
Translate 50 remaining Malagasy values covering office and REST API reporting, automatic data recovery and MongoDB remediation, swimlane copying, wait spinners, organization and team safeguards, mail copies, ticket and request states, sorting, and card details. Preserve REST API, WITH_API, MongoDB, and Cc terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:18:20 +03:00
Lauri Ojansivu 41954c3ec4 Translate Malagasy diagnostics and reports.
Translate 50 remaining Malagasy values covering map detection, server troubleshooting, sorting, board activity, string templates, file diagnostics, security, speed, test, CPU, database, rule, board, card, impersonation, recovery, and office reports. Preserve shell commands, template placeholders, HTML entities, IP versions, and CPU terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:17:02 +03:00
Lauri Ojansivu 7bdf0ad591 Translate Malagasy sorting and dependencies.
Translate 50 remaining Malagasy values covering board and card sorting, completion, stickers, dependency relationships and JSON and SVG imports, board backgrounds, upload limits, and card locations. Preserve sort arrows, format names, dependency placeholders, and background-size placeholder exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:16:02 +03:00
Lauri Ojansivu 501f9519ae Translate Malagasy global search guidance.
Translate 50 remaining Malagasy values covering remaining search predicates, operator validation, pagination, and comprehensive global-search instructions for scopes, users, organizations, teams, dates, statuses, field existence, sorting, limits, and combined conditions. Preserve every placeholder, code sample, angle-bracket term, and OR and AND emphasis exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:14:47 +03:00
Lauri Ojansivu e25ad27f9c Translate Malagasy search operators and predicates.
Translate 50 remaining Malagasy values covering user, comment, organization and team lookup errors, card-search result counts, and global-search operators and predicates for boards, swimlanes, lists, users, dates, content, status, and time ranges. Preserve percent and result-range placeholders exactly and pin core search vocabulary in locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:13:24 +03:00
Lauri Ojansivu 2c97e53600 Translate Malagasy shared templates and card views.
Translate 50 remaining Malagasy values covering linked-list safeguards, tasks, domains, shared templates, names, people and time units, My Cards and Due Cards views and sorting, global-search scope, and board, swimlane, list, and label lookup errors. Preserve domain examples, markdown emphasis, and lookup placeholders exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:12:33 +03:00
Lauri Ojansivu 4398b40172 Translate Malagasy roles and editor controls.
Translate 50 remaining Malagasy values covering multi-card windows, editor shortcuts, card and minicard fields, organization, team and user dialogs, notification states, rename and board-role controls, weekdays, status, ownership, activity, and voting. Preserve Enter, Shift+Enter, and Ctrl/Cmd+Enter shortcut notation exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:11:13 +03:00
Lauri Ojansivu c8f4cbd116 Translate Malagasy authentication and date activity.
Translate 50 remaining Malagasy values covering custom body HTML, authentication and OIDC controls, board duplication, organization, team and person counts, swimlane deletion, card placement, due-date activity and reminders, account, team and organization deletion, and editor and layout controls. Preserve HTML tags and every date, card, comment, list, swimlane, and board placeholder exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:10:03 +03:00
Lauri Ojansivu 5d28648e64 Translate Malagasy rule actions and branding.
Translate 50 remaining Malagasy values covering detailed rule actions for email, archive, labels, members, checklists, cards, swimlanes and relative dates, authentication, product naming, custom head HTML, web manifests, assetlinks.json, layout, and card counters. Preserve comma-separated examples and HTML, JSON, and assetlinks.json terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:09:00 +03:00
Lauri Ojansivu e91ff028ee Translate Malagasy rule trigger phrases.
Translate 50 remaining Malagasy values covering rule phrases for boards, lists, cards, labels, members, attachments, checklists and items, archive and movement states, color, member and checklist actions, email, and detailed top and bottom card moves. Pin core rule entities and generated action phrases in locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:07:55 +03:00
Lauri Ojansivu d7723f3747 Translate Malagasy scheduled rule workflows.
Translate 50 remaining Malagasy values covering n8n and Node-RED workflow imports, scheduled and button triggers, due-date and list-duration conditions, card and board buttons, list sorting, completion and movement actions, relative dates, and rule phrase units. Preserve workflow product names, count placeholders, weekday ranges, and variable notation exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:07:03 +03:00
Lauri Ojansivu 24649d6732 Translate Malagasy rule triggers and exchange.
Translate 50 remaining Malagasy values covering label, attachment and custom-field activity, visual rule editing, card, label, member, checklist, attachment and schedule triggers, and JSON, CSV and Trello Butler rule exchange. Preserve all percent, time and count placeholders and format names exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:06:01 +03:00
Lauri Ojansivu e73a401aa2 Translate Malagasy card dates and subtask settings.
Translate 50 remaining Malagasy values covering received and end dates, colors, assignment metadata, card sorting, permanent board and notification deletion, duplicate-list cleanup, subtask and card settings, minicard fields, attachment and checklist counts, parent paths, and label activity. Preserve the board placeholder, count notation, and activity placeholders exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:04:23 +03:00
Lauri Ojansivu afa897803d Translate Malagasy runtime and tenancy settings.
Translate 50 remaining Malagasy values covering database and FerretDB identity, reactivity and DDP configuration, operating-system diagnostics, time units, custom-field display, account changes, visibility, and organization and team tenancy, domain, administration, and synchronization settings. Preserve runtime modes, environment variables, hostnames, and product names exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:03:19 +03:00
Lauri Ojansivu 862964b765 Translate Malagasy transfer and mail settings.
Translate 50 remaining Malagasy values covering attachment and API transfer limits, avatar upload blocking, registration and invitations, SMTP and TLS settings and test mail, localized registration email, authorization, webhooks, packages, database, and Node and Meteor versions. Preserve API, SMTP, TLS, email placeholders, and runtime names exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:02:21 +03:00
Lauri Ojansivu 361aac6f0c Translate Malagasy tracking and branding.
Translate 50 remaining Malagasy values covering starred boards and subscriptions, spent time and overtime, numbered assignee and label shortcuts, tracking, uploads, custom logos and URL schemes, username imports, watching, welcome and template boards, and WIP limit errors. Preserve numeric ranges, defaults, URL, and WIP terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:01:07 +03:00
Lauri Ojansivu 39977fc4c7 Translate Malagasy visibility and shortcuts.
Translate 50 remaining Malagasy values covering watched updates, private-page login, image previews, board visibility, quick access, member removal and Sandstorm guidance, unsaved-description rescue, search, WIP limits, keyboard shortcuts, sidebars, signup, and starred and default-board controls. Preserve link markup, member placeholders, product names, and WIP terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 08:00:02 +03:00
Lauri Ojansivu baddee0009 Translate Malagasy board and list actions.
Translate 50 remaining Malagasy values covering board leaving, list archive and bulk-card actions, user, team and organization settings, Trello and Excel imports, list deletion, navigation, multi-selection, notification muting, archives, and normal roles. Preserve the board-title placeholder and import format names exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 07:58:54 +03:00
Lauri Ojansivu 48ad5617e4 Translate Malagasy Trello imports and mapping.
Translate 50 remaining Malagasy values covering Trello ZIP path safeguards, workspace placement, direct API imports, cancellable and resumable import jobs, member mapping, version and validation labels, invitations, keyboard shortcuts, and labels. Preserve the Trello API URL, product names, numeric year example, and label placeholder exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 07:57:47 +03:00
Lauri Ojansivu 356e49c26f Translate Malagasy advanced filters and board imports.
Translate 50 remaining Malagasy values covering assignee, custom-field and archive filters, advanced filter syntax, member states and impersonation, Kanboard, NextCloud Deck, OpenProject, Asana, ZenKit, Trello, Jira, Excel and WeKan board imports, and safe Trello ZIP handling. Preserve operators, escape examples, placeholders, endpoints, API fields, file extensions, and product names exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 07:56:32 +03:00
Lauri Ojansivu d932c1a8a3 Translate Malagasy exports and filters.
Translate 50 remaining Malagasy values covering user, organization, team, and email validation, card PDF and Excel exports and attachment metadata, list sorting, and date, label, and member filters. Preserve PDF and Excel terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 07:55:15 +03:00
Lauri Ojansivu 5050cf39cf Translate Malagasy emails and validation errors.
Translate 50 remaining Malagasy values covering custom text and date formats, permanent deletion, label and member disambiguation, WIP controls, card dates and time, profiles, localized account email templates, scrolling, and board, JSON, CSV, TSV, import, linked-card, and user errors. Preserve every email placeholder and technical format name exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 07:54:12 +03:00
Lauri Ojansivu ac42e68615 Translate Malagasy roles and custom fields.
Translate 50 remaining Malagasy values completing the board color palette and covering comment and read-only roles, worker permissions, subtask and checklist deletion, clipboard links, bulk card JSON, templates, labels, and custom-field types and options. Validate the translated bulk-card example as JSON and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 07:53:11 +03:00
Lauri Ojansivu 685299016d Translate Malagasy card aging and colors.
Translate 50 remaining Malagasy values covering starring, automatic list widths, clipboard actions, card aging, keyboard movement, accessible dialog controls, board restoration, and most of the board color palette. Replace the English-identical magenta placeholder with a Malagasy color phrase and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 07:52:17 +03:00
Lauri Ojansivu 14ee6708bf Translate Malagasy member and appearance dialogs.
Translate 50 remaining Malagasy values covering member and restoration dialogs, rule exchange, bookmarks, templates, CAS, linked cards and boards, imported-member mapping, themes, fonts, text colors, avatars, languages, and permissions. Preserve CAS and numeric preview content exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 07:51:02 +03:00
Lauri Ojansivu db084f8f3e Translate Malagasy voting and import dialogs.
Translate 50 remaining Malagasy values covering card members and dates, voting and Planning Poker, dependencies, organizations, teams, avatars, backgrounds, duplicate lists, accounts, domains, imported-member mapping, and checklist, swimlane, list, card, and board imports. Preserve Planning Poker terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 07:50:04 +03:00
Lauri Ojansivu 18c3b83a58 Translate Malagasy board views and card archives.
Translate 50 remaining Malagasy values covering public-board guidance, board movement between workspaces, appearance and views, zoom and calendars, permanent card deletion, archives, dates, time, and card attachment, field, label, and member editing. Preserve strong tags, the workspace placeholder, zoom range, and comment placeholder exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 07:49:13 +03:00
Lauri Ojansivu 8445f1644a Translate Malagasy administration and archives.
Translate 50 remaining Malagasy values covering administrator roles and announcements, public boards, offline recovery, board, list and swimlane archives, templates, attachments, backgrounds, and All Boards member and assignee settings. Preserve count, size, percent, URL, and strong-tag inventories exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 07:48:02 +03:00
Lauri Ojansivu 9a0b7b78dd Translate Malagasy workspace and layout controls.
Translate 50 remaining Malagasy values covering workspace deletion, multi-board selection and Home boards, due and end dates, templates, list widths, keyboard shortcuts, swimlane heights, subtasks, checklists, covers, labels, and members. Preserve activity placeholders and numeric constraints exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 07:47:04 +03:00
Lauri Ojansivu d72410a102 Translate Malagasy activities and workspaces.
Translate 50 remaining Malagasy values covering card movement and restoration, parameterized activity messages, checklist events, received and start dates, and workspace navigation and settings. Preserve all old and new board, list, swimlane, card, checklist, and percent placeholders exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 07:46:04 +03:00
Lauri Ojansivu e661f7c38a Start the Malagasy translation.
Translate 50 Malagasy placeholders covering board membership rules, replies and due dates, and detailed activity messages for attachments, subtasks, labels, checklists, comments, custom fields, members, archives, and imports. Preserve every percent and underscore-delimited activity placeholder exactly and add locale-wide token, HTML, progress, and terminology regression coverage. These low-confidence direct translations welcome review by a Malagasy speaker.

Thanks to xet7 !
2026-08-27 07:45:03 +03:00
Lauri Ojansivu 9ffff8bf43 Complete the Maithili translation.
Translate the final 17 Maithili placeholders covering IP event details, filesystem integrity, scoped WeKan imports and exports, numeric global search, and import choices. Correct the Latin-only API label exposed by the script audit, preserve the operator placeholder and number tags exactly, and pin the locale at zero remaining placeholders. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:43:04 +03:00
Lauri Ojansivu 431c7e5c4d Translate Maithili accounts and problem reporting.
Translate 50 remaining Maithili values covering repository accounts and uploads, OTP and login errors, problem status and acknowledgement, broken-card repair, CPU load, and event details. Preserve OTP, API, CPU, numeric requirements, and repair placeholders exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:41:58 +03:00
Lauri Ojansivu 494e18c692 Translate Maithili migration monitoring controls.
Translate 50 remaining Maithili values covering attachment migration targets, batch, CPU and delay controls, migration logs and lifecycle, monitoring and pagination, schedules, system resources, and aggregate statistics. Preserve GridFS, S3, CPU, percent, millisecond, and numeric range notation exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:40:56 +03:00
Lauri Ojansivu b30b293454 Translate Maithili repair and job monitoring.
Translate 50 remaining Maithili values covering board repair steps, conversion status, CPU and duration monitoring, scheduled intervals, filesystem and GridFS statistics, export monitoring, and background-job details. Preserve URL, ID, CPU, and GridFS terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:39:49 +03:00
Lauri Ojansivu 31ed6b14ff Translate Maithili board integrity migrations.
Translate 50 remaining Maithili values covering comprehensive board integrity migrations for duplicate lists, lost and archived items, missing lists, avatar and attachment URLs, confirmation dialogs, progress, and repair steps. Preserve swimlaneId, listId, ID, and URL identifiers exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:38:50 +03:00
Lauri Ojansivu 586bcc0cb4 Translate Maithili migration and S3 controls.
Translate 50 remaining Maithili values covering Google Cloud Storage and GridFS attachments, migration execution and status, S3 credentials and TLS, scheduled board operations, and attachment and board migration settings. Preserve storage product names, region names, and SSL/TLS terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:37:24 +03:00
Lauri Ojansivu 9dd819d892 Translate Maithili cloud backup configuration.
Translate 50 remaining Maithili values covering scheduled backup and restore settings, Google Cloud Storage credentials and permissions, detailed AWS S3, Azure and Google Cloud configuration paths, and connection status. Preserve times, JSON fields, console labels, product names, file extensions, and navigation paths exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:36:37 +03:00
Lauri Ojansivu c88a301646 Translate Maithili privacy and backup controls.
Translate 50 remaining Maithili values covering Sandstorm storage cleanup, adaptive card loading, safe link and code rendering, import and export controls, user anonymization, activity and notification privacy, and backups. Preserve HTML tags, code examples, product names, paths, archive names, and environment variables exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:35:24 +03:00
Lauri Ojansivu da084cd13b Translate Maithili migrations and storage settings.
Translate 50 remaining Maithili values covering scheduled-job and migration controls, filesystem, S3 and Azure storage settings, MongoDB and FerretDB database migration, and Sandstorm grain migration status. Preserve the database placeholder, product names, URLs, ports, paths, commands, and environment variable names exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:32:51 +03:00
Lauri Ojansivu d513b3ce08 Translate Maithili lockout and scheduled operations.
Translate 50 remaining Maithili values covering accessibility content, brute-force login protection, locked-user administration, scheduled jobs, attachment and avatar paths, and scheduled board maintenance. Extend locale regression coverage for the security and operations terminology. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:31:27 +03:00
Lauri Ojansivu 780742aafd Translate Maithili status and support settings.
Translate 50 remaining Maithili values covering board time status, upload progress and limits, account prompts, custom translations, board dragging, checklist display, support, and accessibility settings. Preserve the workspace placeholder and PDF, ISO 8601, ZIP, JSON, and markdown terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:30:14 +03:00
Lauri Ojansivu 86b43c1c76 Translate Maithili storage repair and compaction.
Translate 50 remaining Maithili values covering bulk attachment and avatar moves, storage repair and statistics, default storage, file IDs, and MongoDB GridFS compaction guidance and status. Preserve GridFS, S3, MongoDB, Compact, oplog, Meteor, and ID terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:28:58 +03:00
Lauri Ojansivu 698ba6bfb0 Translate Maithili organization and attachment tools.
Translate 50 remaining Maithili values covering team invitations, Node memory diagnostics, organization management, legal notices, checklist transformations, and attachment storage moves. Preserve Node memory names, URL terms, and GridFS and S3 storage names exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:25:37 +03:00
Lauri Ojansivu 09e81742f6 Translate Maithili API and recovery reports.
Translate 50 remaining Maithili values covering office and API reporting, automatic data recovery and MongoDB remediation, swimlane copying, wait spinners, organization and team safeguards, ticket and request states, and card details. Preserve REST API, environment variable, database, and mail-copy terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:24:22 +03:00
Lauri Ojansivu ace5435c87 Translate Maithili diagnostics and reports.
Translate 50 remaining Maithili values covering map detection, server troubleshooting, sorting, board activity, string templates, file diagnostics, security, speed, database, recovery, impersonation, and office reports. Preserve shell commands, format placeholders, entities, and IP version names exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:23:11 +03:00
Lauri Ojansivu e5f6850c2e Translate Maithili sorting and dependencies.
Translate 50 remaining Maithili values covering board and card sorting, completion, stickers, dependency relationships and JSON and SVG imports, board backgrounds, upload limits, and card locations. Preserve import placeholders, file formats, numeric tokens, and ordering symbols exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:21:38 +03:00
Lauri Ojansivu a83defb2ec Translate Maithili global search guidance.
Translate 50 remaining Maithili values covering search predicate validation, pagination, and detailed operator guidance for board, list, member, status, existence, sorting, limits, and combined searches. Preserve every operator placeholder, markup token, code example, and Boolean keyword exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:20:23 +03:00
Lauri Ojansivu 82f7623f42 Translate Maithili global search vocabulary.
Translate 50 remaining Maithili values covering missing user, comment, organization and team results, paginated card counts, and localized global-search operator and predicate vocabulary. Preserve pagination and format placeholders exactly and keep search terms compact and internally consistent. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:18:54 +03:00
Lauri Ojansivu d50fd13159 Translate Maithili shared templates and card views.
Translate 50 remaining Maithili values covering linked-list safeguards, tasks, domain and shared-template management, My Cards and Due Cards views, global search scope, and missing board-item messages. Preserve markdown emphasis, format placeholders, and domain examples exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:17:44 +03:00
Lauri Ojansivu 057bb03c39 Translate Maithili role and editor controls.
Translate 50 remaining Maithili values covering multi-card windows, editor shortcuts, organization, team and user dialogs, notification actions, rename and role permissions, weekdays, status, ownership, and voting. Preserve keyboard chord names and role semantics exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:16:36 +03:00
Lauri Ojansivu 4ebd4ec508 Translate Maithili authentication and date activity.
Translate 50 remaining Maithili values covering custom body HTML, authentication display, board duplication, destructive swimlane and account actions, date activity and reminders, relative placement, and resizable navigation. Preserve HTML tags and every activity placeholder exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:15:24 +03:00
Lauri Ojansivu 6c1bb13cd4 Translate Maithili rule actions and branding.
Translate 50 remaining Maithili values covering detailed rule actions and date fields, authentication controls, custom product and head tags, web manifests, asset links, layout, logo, and card counters. Preserve HTML, JSON, asset filenames, and rule example structure exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:13:56 +03:00
Lauri Ojansivu 01231cf292 Translate Maithili rule trigger phrases.
Translate 50 remaining Maithili values covering rule phrases for board, list, card, label, member, attachment and checklist triggers and card movement, membership, color, checklist, and email actions. Keep the rule vocabulary internally consistent and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:12:56 +03:00
Lauri Ojansivu 32604d6661 Translate Maithili scheduled rule workflows.
Translate 50 remaining Maithili values covering n8n and Node-RED workflow imports, schedule and due-date triggers, card and board buttons, list sorting, completion and movement actions, relative dates, and rule units. Preserve workflow product names, count placeholders, weekday ranges, and symbolic values exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:11:58 +03:00
Lauri Ojansivu 7f4757f50c Translate Maithili rule editing and exchange.
Translate 50 remaining Maithili values covering label, attachment and custom-field activity, visual rule editing, card and checklist triggers, scheduled rules, and JSON, CSV and Trello Butler rule exchange. Preserve all activity and schedule placeholders, format names, and product terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:10:47 +03:00
Lauri Ojansivu 9f8041f689 Translate Maithili dates and subtask settings.
Translate 50 remaining Maithili values covering received and end dates, colors and attribution, destructive board and notification actions, subtask settings, minicard display, parent paths, and label activity. Preserve board and format placeholders and numeric checklist examples exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:09:41 +03:00
Lauri Ojansivu e7a8f39826 Translate Maithili runtime and tenancy settings.
Translate 50 remaining Maithili values covering database and runtime diagnostics, reactivity, DDP and OS metrics, custom-field display, account changes, board visibility, and organization and team tenancy controls. Preserve environment variables, hostnames, runtime modes, and product names exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:08:37 +03:00
Lauri Ojansivu 9af97115c1 Translate Maithili transfer and mail settings.
Translate 50 remaining Maithili values covering attachment and API transfer limits, registration and invitations, SMTP setup and test emails, outgoing webhooks, and database, Node and Meteor version labels. Preserve API, SMTP, TLS, product names, and invitation placeholders exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:07:15 +03:00
Lauri Ojansivu 99547dc60e Translate Maithili tracking and branding.
Translate 50 remaining Maithili values covering starring and subscriptions, time and overtime tracking, numeric shortcuts, uploads, custom branding and URL schemes, welcome and template boards, and WIP-limit errors. Preserve numeric ranges, URL terminology, and WIP names exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:06:07 +03:00
Lauri Ojansivu 0830252e6d Translate Maithili visibility and shortcuts.
Translate 50 remaining Maithili values covering watched updates, private-page login, board visibility, member removal and Sandstorm warnings, rescue and search behavior, WIP limits, shortcuts, sidebars, and default-board controls. Preserve HTML, format and member placeholders, and product names exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:04:58 +03:00
Lauri Ojansivu 8b9a3bd3f5 Translate Maithili board and list actions.
Translate 50 remaining Maithili values covering board leaving, list archive and deletion flows, user, team and organization settings, swimlane actions, bulk selection, notification muting, and normal board roles. Preserve the board-title placeholder and import product names exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:03:26 +03:00
Lauri Ojansivu d5f0d7f9f5 Translate Maithili Trello imports and mapping.
Translate 50 remaining Maithili values covering safe Trello ZIP and API imports, import cancellation and cleanup, member mapping, date and user validation, keyboard shortcuts, and label creation. Preserve URLs, API and archive terminology, format tokens, and numeric examples exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:02:11 +03:00
Lauri Ojansivu 18a16c409d Translate Maithili advanced filters and imports.
Translate 50 remaining Maithili values covering advanced filtering, member-state labels, and Kanboard, NextCloud Deck, OpenProject, issue, Asana, ZenKit, Trello, CSV, Jira, Excel, and WeKan board imports. Preserve operators, request paths, placeholders, product names, and file formats exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 07:00:50 +03:00
Lauri Ojansivu 3b5f97d285 Translate Maithili exports and filters.
Translate 50 remaining Maithili values covering account validation errors, card PDF and Excel exports, attachment metadata, list sorting, and date, label, and member filters. Preserve product and export-format terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 06:59:06 +03:00
Lauri Ojansivu 2184f405ff Translate Maithili account emails and errors.
Translate 50 remaining Maithili values covering permanent deletion and WIP controls, profile and date dialogs, enrollment, invitation, reset and verification emails, and board, import, and linked-card errors. Preserve all email placeholders, product names, and data-format terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 06:58:05 +03:00
Lauri Ojansivu 67849c2015 Translate Maithili roles and custom fields.
Translate 50 remaining Maithili values completing the board color palette and covering restricted board roles, deletion confirmations, clipboard and card-link actions, bulk JSON card copying, template and label creation, and custom-field types. Preserve JSON field names and structure exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 06:56:48 +03:00
Lauri Ojansivu 5d272766f3 Translate Maithili navigation and board colors.
Translate 50 remaining Maithili values covering starring, automatic list widths, card aging tiers, keyboard movement and navigation, archive restoration guidance, and most of the board color palette. Preserve numeric tier guidance exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 06:55:35 +03:00
Lauri Ojansivu cdfc61e245 Translate Maithili member mapping and themes.
Translate 50 remaining Maithili values covering member and restoration dialogs, linked cards and boards, safe imported-user mapping, themes, fonts, colors, avatars, language, and permissions. Preserve CAS and numeric preview content exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 06:54:40 +03:00
Lauri Ojansivu 39ba5f0e98 Translate Maithili voting and import dialogs.
Translate 50 remaining Maithili values covering voting and Planning Poker, card dependencies, organization and team dialogs, account and background administration, and checklist, swimlane, list, and card imports. Preserve product terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 06:53:41 +03:00
Lauri Ojansivu 94691d4a48 Translate Maithili board views and archives.
Translate 50 remaining Maithili values covering public-board guidance, board movement and appearance, mobile and desktop views, zoom and calendar controls, archive warnings, and card dates and editing actions. Preserve workspace and format placeholders, numeric zoom ranges, and HTML markup exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 06:52:37 +03:00
Lauri Ojansivu b117fb57e7 Translate Maithili board administration.
Translate 50 remaining Maithili values covering administrator announcements, archive and restore flows, template containers, attachment handling, background images, All Boards display choices, and board membership summaries. Preserve placeholders, URL terminology, and HTML markup exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 06:51:39 +03:00
Lauri Ojansivu aa2f979a3e Translate Maithili workspace and sizing controls.
Translate 50 remaining Maithili values covering workspace deletion, multi-board selection, Home-board behavior, date activity, shared and personal list widths, keyboard shortcuts, swimlane sizing, and checklist controls. Preserve format placeholders, numeric limits, and markdown terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 06:50:47 +03:00
Lauri Ojansivu 4ca3b9bbb9 Translate Maithili activity and workspaces.
Translate 50 remaining Maithili values covering card moves and restoration, compact activity phrases, checklist and comment activity, and workspace navigation and settings. Preserve all activity placeholders and markdown terminology exactly and extend locale regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 06:49:05 +03:00
Lauri Ojansivu 23894a46c0 Start direct Maithili translations.
Translate 50 remaining Maithili values covering board membership rules, replies and due dates, and activity messages for attachments, subtasks, labels, checklists, comments, boards, cards, lists, and archives. Preserve all format and activity placeholders exactly and add locale-wide token and markup regression coverage. These low-confidence direct translations welcome review by a Maithili speaker.

Thanks to xet7 !
2026-08-27 06:47:36 +03:00
Lauri Ojansivu d28252eb7e Complete Kyrgyz translations.
Translate the final 17 Kyrgyz placeholders covering event network details, filesystem integrity, scoped WeKan imports and exports, and numeric search guidance. Preserve IP versions, product and file-format names, markup, and search placeholders exactly. Kyrgyz now has no English placeholders; these direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:45:38 +03:00
Lauri Ojansivu 209590585a Translate Kyrgyz account and problem reporting.
Translate 50 remaining Kyrgyz values covering account and repository access, authentication errors, system-problem reporting, broken-card repair results, CPU load, and event metadata. Preserve OTP, API, CPU, and repair-result placeholders exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:44:53 +03:00
Lauri Ojansivu 566682084a Translate Kyrgyz attachment migration controls.
Translate 50 remaining Kyrgyz values covering attachment migration targets and tuning, CPU and memory limits, background migration guidance, monitoring, pagination, scheduling, and progress totals. Preserve technical storage names and numeric ranges exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:43:56 +03:00
Lauri Ojansivu 2fbe160987 Translate Kyrgyz migration monitoring.
Translate 50 remaining Kyrgyz values covering migration steps, board conversion, CPU and runtime monitoring, recurring schedules, filesystem and GridFS metrics, and job-queue controls. Preserve identifiers and storage terminology exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:42:54 +03:00
Lauri Ojansivu 76443a4107 Translate Kyrgyz board integrity migrations.
Translate 50 remaining Kyrgyz values covering comprehensive board integrity migrations, recovery of lost and archived cards, list and file-reference repair, migration confirmations, progress, and validation steps. Preserve internal identifiers and URL terminology exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:41:52 +03:00
Lauri Ojansivu 961c1304a9 Translate Kyrgyz storage migrations.
Translate 50 remaining Kyrgyz values covering GridFS and attachment migration, migration lifecycle controls, S3 authentication and connection settings, scheduled board operations, and writable storage paths. Preserve storage product names, region examples, protocols, and technical terminology exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:38:59 +03:00
Lauri Ojansivu 037eda28a6 Translate Kyrgyz cloud backup settings.
Translate 50 remaining Kyrgyz values covering scheduled backups, restore modes, Google Cloud Storage credentials and permissions, S3 and Azure setup guidance, cloud connection tests, and Azure attachment moves. Preserve time formats, console navigation labels, product names, file formats, and credential terminology exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:37:27 +03:00
Lauri Ojansivu 79ec1132d6 Translate Kyrgyz privacy and backups.
Translate 50 remaining Kyrgyz values covering Sandstorm storage cleanup, card-loading modes, secure plain-text rendering, import and export privacy controls, activity and notification controls, and backup scope and storage. Preserve product names, paths, variables, markup examples, and counter placeholders exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:35:34 +03:00
Lauri Ojansivu 0285717971 Translate Kyrgyz database migrations.
Translate 50 remaining Kyrgyz values covering scheduled-job controls and migration errors, filesystem, S3 and Azure storage, MongoDB and FerretDB database migration, and Sandstorm migration status. Preserve URLs, environment variables, commands, placeholders, paths, and product terminology exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:33:38 +03:00
Lauri Ojansivu 22d5d334e7 Translate Kyrgyz lockout and maintenance.
Translate 50 remaining Kyrgyz values covering accessibility content, brute-force lockout administration, active-user filters, scheduled Cron jobs, attachment paths, and scheduled board maintenance. Preserve security terminology and consistent user, board, and storage vocabulary and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:32:06 +03:00
Lauri Ojansivu 8167d5b31c Translate Kyrgyz board status and support.
Translate 50 remaining Kyrgyz values covering board and upload status, account and file limits, workspace dragging, custom translations, checklist display, support, and accessibility. Preserve workspace placeholders, file formats, standards, and product terminology exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:30:43 +03:00
Lauri Ojansivu 995ab45ab2 Translate Kyrgyz attachment maintenance.
Translate 50 remaining Kyrgyz values covering bulk attachment and avatar moves, storage repair and statistics, default storage, file IDs, and MongoDB GridFS compaction guidance and status. Preserve GridFS, S3, ID, MongoDB, Compact, oplog, and Meteor terminology exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:29:19 +03:00
Lauri Ojansivu 88fddd80df Translate Kyrgyz teams and attachment storage.
Translate 50 remaining Kyrgyz values covering team invitations, Node memory diagnostics, organization management, legal notices, checklist transformations, and attachment storage moves. Preserve Node heap, malloc, URL, GridFS, and S3 terminology exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:28:16 +03:00
Lauri Ojansivu 150b5e3efb Translate Kyrgyz recovery and tickets.
Translate 50 remaining Kyrgyz values covering office and API reporting, automatic data recovery and MongoDB remediation, swimlane copying, wait spinners, organization and team safeguards, ticket and request states, and card details. Preserve REST API, environment-variable, MongoDB, spinner, and Cc terminology exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:27:04 +03:00
Lauri Ojansivu d3c272507d Translate Kyrgyz diagnostics and reports.
Translate 50 remaining Kyrgyz values covering map detection, server troubleshooting, sorting, board activity, string templates, file diagnostics, security, speed, database, recovery, impersonation, and office reports. Preserve shell commands, format placeholders, HTML entities, protocol names, and CPU terminology exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:26:10 +03:00
Lauri Ojansivu 4af40394e6 Translate Kyrgyz dependencies and locations.
Translate 50 remaining Kyrgyz values covering board and card sorting, completion, stickers, dependency relationships and JSON and SVG imports, board backgrounds, upload limits, and card locations. Preserve sort notation, format names, import counts, and the size placeholder exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:24:32 +03:00
Lauri Ojansivu 527de2a7dc Translate Kyrgyz search guidance.
Translate 50 remaining Kyrgyz values covering search predicate validation, pagination, and detailed operator guidance for board, list, member, status, existence, sorting, limits, and combined searches. Preserve every placeholder, operator example, code span, markdown emphasis, logical operator, and line break exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:23:33 +03:00
Lauri Ojansivu 32f7477652 Translate Kyrgyz global search terms.
Translate 50 remaining Kyrgyz values covering missing user, comment, organization and team results, paginated card counts, and localized global-search operator and predicate vocabulary. Preserve every printf and pagination placeholder exactly and pin native no-space operator terms in regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:21:49 +03:00
Lauri Ojansivu 8e7f06afd2 Translate Kyrgyz domains and card views.
Translate 50 remaining Kyrgyz values covering linked-list safeguards, tasks, domain and shared-template management, My Cards and Due Cards views, global search scope, and missing board-item messages. Preserve domain examples, markdown emphasis, and every printf placeholder exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:20:52 +03:00
Lauri Ojansivu 49a208525a Translate Kyrgyz role and editor settings.
Translate 50 remaining Kyrgyz values covering multi-card windows, editor shortcuts, organization, team and user dialogs, notification actions, rename and role permissions, weekdays, status, ownership, and voting. Preserve keyboard shortcut notation and consistent role and calendar terminology and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:19:34 +03:00
Lauri Ojansivu a5ee97ebbf Translate Kyrgyz authentication and date activity.
Translate 50 remaining Kyrgyz values covering custom body HTML, authentication display, board duplication, destructive swimlane and account actions, date activity and reminders, relative placement, and resizable navigation. Preserve body tags, OIDC terminology, line breaks, and every activity placeholder exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:18:34 +03:00
Lauri Ojansivu 2b007cd3df Translate Kyrgyz rule details and branding.
Translate 50 remaining Kyrgyz values covering detailed rule actions and date fields, authentication controls, custom product and head tags, web manifests, asset links, layout, logo, and card counters. Preserve comma-separated examples, HTML and JSON format names, and assetlinks.json exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:17:07 +03:00
Lauri Ojansivu b47c786e14 Translate Kyrgyz rule phrases.
Translate 50 remaining Kyrgyz values covering rule phrases for board, list, card, label, member, attachment and checklist triggers and card movement, membership, color, checklist, and email actions. Keep native kanban terminology consistent and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:16:09 +03:00
Lauri Ojansivu 74bc72f176 Translate Kyrgyz scheduled rule actions.
Translate 50 remaining Kyrgyz values covering n8n and Node-RED workflow imports, schedule and due-date triggers, card and board buttons, list sorting, completion and movement actions, relative dates, and rule units. Preserve the count placeholder, weekday range, product names, and literal N exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:15:10 +03:00
Lauri Ojansivu 4a8e3d9b11 Translate Kyrgyz rule workflows.
Translate 50 remaining Kyrgyz values covering label, attachment and custom-field activity, visual rule editing, card and checklist triggers, scheduled rules, and JSON, CSV, and Trello Butler rule exchange. Preserve every printf, time, and count placeholder and product and format name exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:14:17 +03:00
Lauri Ojansivu da1ba4c03f Translate Kyrgyz subtask and minicard settings.
Translate 50 remaining Kyrgyz values covering received and end dates, colors and attribution, destructive board and notification actions, subtask settings, minicard display, parent paths, and label activity. Preserve the board and printf placeholders and checklist count notation exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:13:08 +03:00
Lauri Ojansivu 1f6a5de877 Translate Kyrgyz runtime and tenancy settings.
Translate 50 remaining Kyrgyz values covering database and runtime diagnostics, reactivity, DDP and OS metrics, custom-field display, account changes, board visibility, and organization and team tenancy controls. Preserve environment variables, modes, host examples, protocols, and product names exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:12:04 +03:00
Lauri Ojansivu a1223aa213 Translate Kyrgyz transfer limits and webhooks.
Translate 50 remaining Kyrgyz values covering attachment and API transfer limits, registration and invitations, SMTP setup and test emails, outgoing webhooks, and database, Node, and Meteor version labels. Preserve every invitation placeholder, line break, protocol, API, runtime, and product name exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:10:56 +03:00
Lauri Ojansivu ff935cd9f7 Translate Kyrgyz tracking and branding.
Translate 50 remaining Kyrgyz values covering starring and subscriptions, time and overtime tracking, numeric shortcuts, uploads, custom branding and URL schemes, welcome and template boards, and WIP-limit errors. Preserve numeric ranges, URL and WIP terminology, and kanban vocabulary exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:09:39 +03:00
Lauri Ojansivu da622c592e Translate Kyrgyz visibility and shortcuts.
Translate 50 remaining Kyrgyz values covering watched updates, private-page login, board visibility, member removal and Sandstorm warnings, rescue and search behavior, WIP limits, shortcuts, sidebars, and default-board controls. Preserve link markup, member placeholders, product names, and WIP terminology exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:08:37 +03:00
Lauri Ojansivu afa3d42517 Translate Kyrgyz board roles and selection.
Translate 50 remaining Kyrgyz values covering board leaving, list archive and deletion flows, user, team and organization settings, swimlane actions, bulk selection, notification muting, and normal board roles. Preserve the board-title placeholder and import format terminology exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:07:20 +03:00
Lauri Ojansivu e30afbdbc2 Translate Kyrgyz Trello API imports.
Translate 50 remaining Kyrgyz values covering safe Trello ZIP and API imports, import cancellation and cleanup, member mapping, date and user validation, keyboard shortcuts, and label creation. Preserve the API URL, API terminology, numeric year example, and printf placeholder exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:06:08 +03:00
Lauri Ojansivu 989571bd51 Translate Kyrgyz filters and board imports.
Translate 50 remaining Kyrgyz values covering advanced filtering, member-state labels, and Kanboard, NextCloud Deck, OpenProject, issue, Asana, ZenKit, Trello, CSV, Jira, Excel, and WeKan board imports. Preserve every operator, regex and escape example, API path, JSON property, endpoint placeholder, file extension, and product name exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:04:56 +03:00
Lauri Ojansivu a3551178db Translate Kyrgyz exports and filters.
Translate 50 remaining Kyrgyz values covering account validation errors, card PDF and Excel exports, attachment metadata, list sorting, and date, label, and member filters. Preserve product and file-format terminology and consistent kanban terms and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:03:14 +03:00
Lauri Ojansivu 39789db52a Translate Kyrgyz emails and import errors.
Translate 50 remaining Kyrgyz values covering permanent deletion and WIP controls, profile and date dialogs, enrollment, invitation, reset and verification emails, and board, import, and linked-card errors. Preserve every email placeholder, line break, format name, and product term exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:02:05 +03:00
Lauri Ojansivu d94bd7592a Translate Kyrgyz roles and custom fields.
Translate 50 remaining Kyrgyz values covering restricted board roles, deletion confirmations, clipboard and card-link actions, bulk JSON card copying, template and label creation, and custom-field types. Keep the embedded JSON example valid with its property names unchanged and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 06:00:54 +03:00
Lauri Ojansivu 9c9758a6b9 Translate Kyrgyz navigation and colors.
Translate 50 remaining Kyrgyz values covering starring, automatic list widths, card aging tiers, keyboard movement and navigation, archive restoration guidance, and the board color palette. Preserve numeric tier information and consistent board, list, and card terminology and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 05:59:51 +03:00
Lauri Ojansivu be5e6ec0c1 Translate Kyrgyz user mapping and themes.
Translate 50 remaining Kyrgyz values covering member and restoration dialogs, linked cards and boards, safe imported-user mapping, themes, fonts, colors, avatars, language, and permissions. Preserve CAS terminology and numeric font-preview coverage and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 05:58:45 +03:00
Lauri Ojansivu 0aed45bc6f Translate Kyrgyz voting and imports.
Translate 50 remaining Kyrgyz values covering voting and Planning Poker, card dependencies, organization and team dialogs, account and background administration, and checklist, swimlane, list, and card imports. Preserve Planning Poker terminology and consistent native kanban terms and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 05:57:47 +03:00
Lauri Ojansivu 2e564d3f07 Translate Kyrgyz board views and card archives.
Translate 50 remaining Kyrgyz values covering public-board guidance, board movement and appearance, mobile and desktop views, zoom and calendar controls, archive warnings, and card dates and editing actions. Preserve strong markup, workspace and printf placeholders, and numeric zoom constraints exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 05:56:51 +03:00
Lauri Ojansivu 6e15c45830 Translate Kyrgyz administration and archives.
Translate 50 remaining Kyrgyz values covering administrator announcements, archive and restore flows, template containers, attachment handling, background images, All Boards display choices, and board membership summaries. Preserve count, size, and printf placeholders and strong markup exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 05:55:43 +03:00
Lauri Ojansivu cd579e294c Translate Kyrgyz board layout controls.
Translate 50 remaining Kyrgyz values covering workspace deletion, multi-board selection, Home behavior, date activity, shared and personal list widths, keyboard shortcuts, swimlane sizing, and checklist controls. Preserve printf placeholders and numeric constraints exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 05:54:50 +03:00
Lauri Ojansivu 368feac24f Translate Kyrgyz activity and workspaces.
Translate 50 remaining Kyrgyz values covering card moves and restoration, compact activity phrases, checklist and comment activity, and workspace navigation and settings. Preserve every printf and underscore placeholder and markdown terminology exactly and extend locale regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 05:53:46 +03:00
Lauri Ojansivu a9106b3896 Begin the Kyrgyz translation.
Translate 50 remaining Kyrgyz values covering board membership rules, replies and due dates, and detailed activity messages for attachments, subtasks, labels, checklists, comments, boards, cards, lists, and archives. Preserve every printf and underscore placeholder exactly and add locale-wide token, tag, script, terminology, and progress regression coverage. These direct translations welcome review by a Kyrgyz speaker.

Thanks to xet7 !
2026-08-27 05:52:41 +03:00
Lauri Ojansivu b79b1d4969 Complete the Cornish translation.
Translate the final 17 Cornish values covering event network details, filesystem integrity, scoped WeKan imports and exports, and numeric search guidance. Preserve IP protocol names, file extensions, product names, markup, and the search placeholder exactly and pin the zero-placeholder completion state in regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:51:10 +03:00
Lauri Ojansivu 050d18e716 Translate Cornish account diagnostics.
Translate 50 remaining Cornish values covering account and repository access, authentication errors, system-problem reporting, broken-card repair results, CPU load, and event metadata. Preserve OTP, API, CPU, and repair-result placeholders exactly and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:50:21 +03:00
Lauri Ojansivu 55edfaf520 Translate Cornish migration controls.
Translate 50 remaining Cornish values covering attachment migration targets and tuning, CPU and memory limits, background-migration guidance, monitoring, pagination, scheduling, and progress totals. Preserve numeric ranges, units, product names, and storage terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:49:31 +03:00
Lauri Ojansivu 04fd91a178 Translate Cornish migration monitoring.
Translate 50 remaining Cornish values covering migration step labels, board conversion status, CPU and runtime monitoring, recurring schedules, filesystem and GridFS metrics, and job-queue controls. Preserve internal IDs, URLs, CPU, and GridFS terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:48:46 +03:00
Lauri Ojansivu 94e86b94c4 Translate Cornish board integrity migrations.
Translate 50 remaining Cornish values covering comprehensive board integrity migrations, recovery of lost and archived cards, list and file-reference repair, migration confirmations, progress, and validation steps. Preserve internal identifiers and URL terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:48:01 +03:00
Lauri Ojansivu 10cf656a23 Translate Cornish storage migrations.
Translate 50 remaining Cornish values covering GridFS and attachment migration, migration lifecycle controls, S3 authentication and connection settings, scheduled board operations, and writable storage paths. Preserve storage product names, region examples, protocols, and technical terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:46:55 +03:00
Lauri Ojansivu 3f926d28e8 Translate Cornish cloud backup settings.
Translate 50 remaining Cornish values covering scheduled backups, restore modes, Google Cloud Storage credentials and permissions, S3 and Azure setup guidance, cloud connection tests, and Azure attachment moves. Preserve time formats, console navigation labels, product names, file formats, and credential terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:46:03 +03:00
Lauri Ojansivu 6c4bc73d67 Translate Cornish privacy and backups.
Translate 50 remaining Cornish values covering Sandstorm storage cleanup, card-loading modes, secure plain-text rendering, import and export privacy controls, activity and notification controls, and backup scope and storage. Preserve product names, paths, variables, markup examples, and counter placeholders and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:44:50 +03:00
Lauri Ojansivu 0e66cea6b0 Translate Cornish database migrations.
Translate 50 remaining Cornish values covering scheduled-job controls and migration errors, filesystem, S3 and Azure storage, MongoDB and FerretDB database migration, and Sandstorm migration status. Preserve URLs, environment variables, commands, placeholders, paths, and product terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:42:38 +03:00
Lauri Ojansivu be5910fc3a Translate Cornish lockout administration.
Translate 50 remaining Cornish values covering accessibility content, brute-force lockout administration, active-user filters, scheduled Cron jobs, attachment paths, and scheduled board maintenance. Preserve Cron terminology and extend locale regression coverage for security-sensitive lockout controls. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:39:58 +03:00
Lauri Ojansivu 43e20ea9b4 Translate Cornish status and support.
Translate 50 remaining Cornish values covering board and upload status, account and file limits, workspace dragging, custom translations, checklist display, support, and accessibility. Preserve workspace placeholders and Mongo, PDF, ISO 8601, ZIP, JSON, and markdown terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:38:46 +03:00
Lauri Ojansivu 378b3e28e0 Translate Cornish attachment maintenance.
Translate 50 remaining Cornish values covering bulk attachment moves, storage repair, default backends, progress and file statistics, MongoDB Compact guidance, and resource identifiers. Preserve MongoDB, GridFS, Compact, oplog, Meteor, S3, and ID terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:37:21 +03:00
Lauri Ojansivu 87bab316c9 Translate Cornish teams and storage.
Translate 50 remaining Cornish values covering team invitations, Node memory diagnostics, organization controls, legal notices, checklist actions, and attachment storage targets. Preserve Node, malloc, URL, GridFS, and S3 terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:36:04 +03:00
Lauri Ojansivu 86392b2bc2 Translate Cornish recovery and tickets.
Translate 50 remaining Cornish values covering office and API activity, recovery health and maintenance, swimlane copying, loading-spinner styles, card sizing, deletion safeguards, tickets, requests, sorting, and card details. Preserve environment variables, product and spinner names, and technical labels and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:34:54 +03:00
Lauri Ojansivu f4533157c7 Translate Cornish maps and reports.
Translate 50 remaining Cornish values covering map detection, server troubleshooting, sorting, string templates, invisible filenames, administration reports, recovery, and office-login summaries. Preserve exact commands, template tokens, HTML entities, and technical protocol labels and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:33:18 +03:00
Lauri Ojansivu 7d844bc327 Translate Cornish dependencies and locations.
Translate 50 remaining Cornish values covering board and card sorting, stickers, dependency editing and JSON or SVG imports, board backgrounds, and location entry. Preserve dependency-result placeholders, size tokens, formats, and established vocabulary and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:32:10 +03:00
Lauri Ojansivu 38b5ce445b Translate Cornish search guidance.
Translate 50 remaining Cornish values covering predicate validation, pagination, and complete global-search operator syntax, status, existence, sorting, limits, boolean combinations, and date guidance. Preserve every token and pseudo-tag and enforce no-space predicate and boolean semantics with regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:31:02 +03:00
Lauri Ojansivu 4fa0202ed5 Translate Cornish global search operators.
Translate 50 remaining Cornish values covering missing user, comment, organization, and team results, result counts, and localized global-search operators and predicates. Preserve count placeholders and enforce the no-whitespace search-token invariant with regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:29:08 +03:00
Lauri Ojansivu 1a9131aaa7 Translate Cornish shared templates and views.
Translate 50 remaining Cornish values covering linked-list safeguards, tasks, domain-scoped shared templates, personal and due-card views, global search scopes, and missing board, swimlane, list, and label messages. Preserve domain examples, emphasis, and result placeholders and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:27:52 +03:00
Lauri Ojansivu 5f9b29929a Translate Cornish roles and editor behavior.
Translate 50 remaining Cornish values covering multi-card windows, editor submission shortcuts, entity dialogs, notification states, rename and invite permissions, board-role status, weekdays, ownership, and voting. Preserve keyboard combinations and administrative semantics and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:26:06 +03:00
Lauri Ojansivu c22a364e67 Translate Cornish lifecycle and layout controls.
Translate 50 remaining Cornish values covering authentication display, board duplication, custom body HTML, lifecycle activity, due reminders, deletion confirmations, and resizable layout controls. Preserve body tags, OIDC, activity placeholders, and keyboard labels and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:24:39 +03:00
Lauri Ojansivu c9dfbe830f Translate Cornish rule details and metadata.
Translate 50 remaining Cornish values covering detailed rule actions for cards, checklists, swimlanes, and dates plus authentication, custom product HTML and JSON metadata, layout, and counters. Preserve examples, separators, formats, and technical filenames and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:23:32 +03:00
Lauri Ojansivu 9c260a0918 Translate Cornish automation actions.
Translate 50 remaining Cornish values covering board and list movement, archive restoration, labels, members, attachments, checklists, card positioning, colors, and email actions. Reuse established board, list, archive, checklist, member, and email terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:22:32 +03:00
Lauri Ojansivu a2ff5c5d4f Translate Cornish workflow schedules.
Translate 50 remaining Cornish values covering n8n and Node-RED workflow imports, scheduled and button triggers, due-date conditions, list and card automation, relative dates, units, and movement phrases. Preserve workflow names, placeholders, schedule ranges, and symbolic examples and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:21:28 +03:00
Lauri Ojansivu 102fd0bfeb Translate Cornish visual rules.
Translate 50 remaining Cornish values covering label and custom-field activity, visual rule building, event triggers, and JSON, CSV, and Trello Butler rule exchange. Preserve every placeholder and format and product name and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:20:17 +03:00
Lauri Ojansivu 05a23e4a75 Translate Cornish card lifecycle and safeguards.
Translate 50 remaining Cornish values covering card lifecycle dates, colors and attribution, destructive-action safeguards, subtask placement, minicard metadata, parent paths, and label activity. Preserve placeholders, counters, and irreversible-action meaning and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:19:07 +03:00
Lauri Ojansivu c47c51fbc7 Translate Cornish database diagnostics.
Translate 50 remaining Cornish values covering database and FerretDB diagnostics, reactivity, DDP and OS metrics, custom-field display, account changes, visibility, and organization and team multitenancy administration. Preserve modes, environment variables, sample domains, and administrative boundaries and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:17:51 +03:00
Lauri Ojansivu 599cde76bc Translate Cornish limits and webhooks.
Translate 50 remaining Cornish values covering attachment and API limits, avatar-upload blocking, registration and invitations, SMTP and TLS setup, test mail, webhooks, and runtime version labels. Preserve invitation placeholders and protocol and product terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:16:34 +03:00
Lauri Ojansivu bb6292bda8 Translate Cornish tracking and uploads.
Translate 50 remaining Cornish values covering starred boards, time tracking, assignee and label shortcuts, uploads, custom logos and URL schemes, welcome and template boards, and WIP-limit guidance. Preserve numeric shortcuts, technical labels, and product behavior and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:15:18 +03:00
Lauri Ojansivu fbb143f910 Translate Cornish visibility and shortcuts.
Translate 50 remaining Cornish values covering watched updates, private-page login markup, previews, visibility, member removal and Sandstorm access, rescue dialogs, search, WIP limits, shortcuts, and default-board behavior. Preserve markup, placeholders, product names, and keyboard labels and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:14:03 +03:00
Lauri Ojansivu 7ae9da0fd7 Translate Cornish archives and roles.
Translate 50 remaining Cornish values covering last-admin protection, leaving boards, list and card archive actions, settings and import dialogs, multi-selection, notification modes, and normal roles. Preserve placeholders, UI paths, formats, and established archive terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:12:45 +03:00
Lauri Ojansivu 9c6a28f7ca Translate Cornish Trello API imports.
Translate 50 remaining Cornish values covering ZIP path safety, workspace placement, direct Trello API import and job controls, member mapping, validation, keyboard shortcuts, and label lifecycle. Preserve URLs, product names, irreversible-action meaning, year examples, and placeholders and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:11:25 +03:00
Lauri Ojansivu 5651723a55 Translate Cornish filters and board imports.
Translate 50 remaining Cornish values covering assignee and advanced filters, member status and impersonation, multi-source board import guidance, and secure Trello JSON and ZIP handling. Preserve operators, endpoints, placeholders, paths, formats, and product names and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:10:01 +03:00
Lauri Ojansivu 28e446c53f Translate Cornish exports and filters.
Translate 50 remaining Cornish values covering account-conflict errors, card exports to PDF and Excel, disk-space diagnostics, attachment metadata, sorting, and date, label, and member filters. Preserve format and board-structure terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:07:15 +03:00
Lauri Ojansivu 317708a9f4 Translate Cornish email and import errors.
Translate 50 remaining Cornish values covering permanent-delete controls, profile and WIP dialogs, transactional email templates, authorization errors, import schemas, empty-export recovery, and linked-card safety. Preserve every email placeholder and JSON, CSV, TSV, and WeKan terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:05:45 +03:00
Lauri Ojansivu 3ed34e0d4f Translate Cornish roles and custom fields.
Translate 50 remaining Cornish values covering permission roles, comment visibility, deletion confirmations, clipboard and template copying, labels, and custom-field types. Keep the localized card-template example valid JSON and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:03:11 +03:00
Lauri Ojansivu 781d5cbb7d Translate Cornish card aging and colors.
Translate 50 remaining Cornish values covering starring, automatic list widths, card-aging tiers, keyboard movement, accessible dialog controls, and 23 color names. Use descriptive Cornish forms where a bare color name would remain identical to English and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 05:01:10 +03:00
Lauri Ojansivu 08173bbd4a Translate Cornish members and appearance.
Translate 50 remaining Cornish values covering member and restore dialogs, linked items, safe imported-member mapping, themes, fonts, text colors, avatars, language, and permissions. Preserve CAS and mapping semantics and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 04:59:06 +03:00
Lauri Ojansivu 294ba6a478 Translate Cornish voting and imports.
Translate 50 remaining Cornish values covering card membership, voting and Planning Poker, dependencies, board organization, team and domain assignment, backgrounds, and import dialogs. Preserve product names and reuse established Cornish import, export, board, list, swimlane, and checklist terminology with regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 04:57:43 +03:00
Lauri Ojansivu 66d4921b87 Translate Cornish board views and archives.
Translate 50 remaining Cornish values covering board visibility and navigation, mobile and desktop views, zoom, calendar modes, and card, list, and swimlane archive guidance. Preserve markup, workspace placeholders, ranges, and count tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 04:56:28 +03:00
Lauri Ojansivu a9272c9996 Translate Cornish administration and archives.
Translate 50 remaining Cornish values covering administration announcements, archive and template actions, reconnection guidance, board backgrounds, member summaries, and privacy. Preserve counts, size placeholders, and strong tags and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 04:55:10 +03:00
Lauri Ojansivu 3f8b34ae92 Translate Cornish board layout controls.
Translate 50 remaining Cornish values covering workspace deletion, board bulk actions, Home-board selection, list widths, keyboard shortcuts, swimlane height, and creation controls. Preserve Admin navigation, numeric limits, units, and source placeholders and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 04:53:59 +03:00
Lauri Ojansivu ae78ae343f Translate Cornish activity and workspaces.
Translate 50 remaining Cornish values covering card movement, membership, checklist and date activity, and workspace creation and editing. Preserve every activity placeholder, reuse established board terminology, and introduce consistent Le ober workspace wording with locale regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 04:52:22 +03:00
Lauri Ojansivu 6a85ae2ce8 Begin completing the Cornish translation.
Translate 50 remaining Cornish values covering activity history for board, swimlane, list, card, checklist, label, attachment, comment, and member changes. Reuse the locale existing Estyllen, Karten, Rol, Hyns, Stagell, Rol checkya, and Kampoellyans terminology and add locale-wide token and tag regression coverage. These direct translations are low-confidence and welcome review by a Cornish speaker.

Thanks to xet7 !
2026-08-27 04:51:12 +03:00
Lauri Ojansivu fa8b2b4a36 Complete the Kurmanji Kurdish translation.
Translate the final 17 Kurmanji Kurdish values covering IP event fields, filesystem integrity, scoped import and export controls, and numeric search syntax. Preserve the search placeholder, pseudo-tags, file formats, and product names and prove the locale has no English placeholders left. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:49:16 +03:00
Lauri Ojansivu fffef11ab2 Translate Kurmanji Kurdish accounts and repairs.
Translate 50 remaining Kurmanji Kurdish values covering OTP account access, repository management, API endpoints, login errors, problem reporting, card repair, CPU status, and event metadata. Preserve technical labels, numeric requirements, and repair placeholders and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:48:22 +03:00
Lauri Ojansivu 4597ce15a0 Translate Kurmanji Kurdish migration resources.
Translate 50 remaining Kurmanji Kurdish values covering storage migration targets and tuning, CPU limits, background migration guidance, monitoring controls, progress, scheduling, and resource totals. Preserve storage names, numeric ranges, and technical units and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:47:18 +03:00
Lauri Ojansivu 6cb8e09e7a Translate Kurmanji Kurdish migration monitoring.
Translate 50 remaining Kurmanji Kurdish values covering attachment and missing-ID repair steps, board conversion, CPU and timing status, scheduled intervals, filesystem and GridFS monitoring, and job-queue details. Preserve URL, ID, CPU, and GridFS terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:45:57 +03:00
Lauri Ojansivu 9bc040398d Translate Kurmanji Kurdish board repairs.
Translate 50 remaining Kurmanji Kurdish values covering comprehensive board repair, duplicate-list cleanup, lost and archived item recovery, file URL fixes, migration confirmations, progress, and validation steps. Preserve data-field and identifier terminology and extend locale regression coverage for migration semantics. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:44:53 +03:00
Lauri Ojansivu 5621f4c068 Translate Kurmanji Kurdish storage migrations.
Translate 50 remaining Kurmanji Kurdish values covering GridFS and GCS storage, migration lifecycle controls, CollectionFS movement, S3 authentication and TLS, scheduled board operations, and attachment monitoring. Preserve storage product names, region syntax, and SSL/TLS terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:43:25 +03:00
Lauri Ojansivu e7dc122dc8 Translate Kurmanji Kurdish cloud backups.
Translate 50 remaining Kurmanji Kurdish values covering backup schedules and restore modes, Google Cloud credentials and permissions, S3, Azure and GCS setup paths, secret retention, and cloud connection status. Preserve schedule formats, JSON fields, console labels, and provider terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:42:14 +03:00
Lauri Ojansivu 126b8bd851 Translate Kurmanji Kurdish security and backups.
Translate 50 remaining Kurmanji Kurdish values covering Sandstorm cleanup, card-loading performance, secure plain-text rendering, import and export restrictions, identity anonymization, activity controls, and streamed backups. Preserve HTML, environment variables, formats, product names, and backup paths and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:40:47 +03:00
Lauri Ojansivu 7d4bd847b7 Translate Kurmanji Kurdish database migrations.
Translate 50 remaining Kurmanji Kurdish values covering scheduled-job controls and migration errors, filesystem, S3 and Azure storage, MongoDB and FerretDB database migration, and Sandstorm migration status. Preserve URLs, environment variables, commands, placeholders, paths, and product terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:38:26 +03:00
Lauri Ojansivu 0c52409150 Translate Kurmanji Kurdish lockout administration.
Translate 50 remaining Kurmanji Kurdish values covering accessibility content, brute-force lockout administration, active-user filters, scheduled Cron jobs, attachment paths, and scheduled board maintenance. Preserve Cron terminology and extend locale regression coverage for security-sensitive lockout controls. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:36:48 +03:00
Lauri Ojansivu d936b95f91 Translate Kurmanji Kurdish status and support.
Translate 50 remaining Kurmanji Kurdish values covering board and upload status, account and file limits, workspace dragging, custom translations, checklist display, support, and accessibility. Preserve workspace placeholders and Mongo, PDF, ISO 8601, ZIP, JSON, and markdown terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:35:31 +03:00
Lauri Ojansivu 5f8452b8a3 Translate Kurmanji Kurdish attachment maintenance.
Translate 50 remaining Kurmanji Kurdish values covering bulk attachment moves, storage repair, default backends, progress and file statistics, MongoDB Compact guidance, and resource identifiers. Preserve MongoDB, GridFS, Compact, oplog, Meteor, S3, and ID terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:34:13 +03:00
Lauri Ojansivu 26277db89b Translate Kurmanji Kurdish teams and storage.
Translate 50 remaining Kurmanji Kurdish values covering team invitations, Node memory diagnostics, organization controls, legal notices, checklist actions, and attachment storage targets. Preserve Node, malloc, URL, GridFS, and S3 terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:32:46 +03:00
Lauri Ojansivu a0bfcd5cc9 Translate Kurmanji Kurdish recovery and tickets.
Translate 50 remaining Kurmanji Kurdish values covering office and API activity, recovery health and maintenance, swimlane copying, loading-spinner styles, card sizing, organization and team deletion safeguards, tickets, requests, sorting, and card details. Preserve environment variables and product and spinner names and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:30:03 +03:00
Lauri Ojansivu d52f6f5c51 Translate Kurmanji Kurdish maps and reports.
Translate 50 remaining Kurmanji Kurdish values covering map detection, server troubleshooting, sorting, links, swimlane movement, string templates, invisible filenames, administration reports, recovery, and office-login summaries. Preserve shell commands, format tokens, entities, IP versions, and technical terms and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:28:47 +03:00
Lauri Ojansivu cc9d6f2f50 Translate Kurmanji Kurdish dependencies and locations.
Translate 50 remaining Kurmanji Kurdish values covering board and card sorting, stickers, dependency editing and JSON or SVG imports, board backgrounds, and location entry. Preserve named and moustache tokens, formats, and arrows and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:27:25 +03:00
Lauri Ojansivu a3a9837cf0 Translate Kurmanji Kurdish search guidance.
Translate 50 remaining Kurmanji Kurdish values covering predicate validation, pagination, and complete global-search operator syntax, status, existence, sorting, limits, boolean combinations, and date guidance. Preserve every named token, pseudo-tag, example, and format and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:25:54 +03:00
Lauri Ojansivu b686c16def Translate Kurmanji Kurdish global search tokens.
Translate 50 remaining Kurmanji Kurdish values covering missing-user, comment, organization and team results, result counts, and localized global-search operators and predicates. Preserve range tokens and the parser requirement that every operator remain a single space-free token, and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:23:55 +03:00
Lauri Ojansivu 244772d87a Translate Kurmanji Kurdish shared templates and views.
Translate 50 remaining Kurmanji Kurdish values covering linked-list safeguards, checklist tasks, domain-scoped shared templates, personal and due-card views, global search scopes, and missing board, swimlane, list and label messages. Preserve markdown emphasis, printf tokens, and domain examples and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:22:49 +03:00
Lauri Ojansivu a46d0158a2 Translate Kurmanji Kurdish roles and editor behavior.
Translate 50 remaining Kurmanji Kurdish values covering multi-card windows, editor submission shortcuts, organization, team and user dialogs, notification states, rename and invite permissions, board-role status, weekdays, ownership, activity, voting, and linked-card deletion safety. Preserve shortcut syntax and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:21:18 +03:00
Lauri Ojansivu 4464bb94c7 Translate Kurmanji Kurdish authentication and activity.
Translate 50 remaining Kurmanji Kurdish values covering authentication display, board duplication, custom body HTML, lifecycle activity, due reminders, destructive confirmations, account self-deletion, and resizable layout controls. Preserve every HTML and named token and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:20:07 +03:00
Lauri Ojansivu 0ab20e090d Translate Kurmanji Kurdish rule details and metadata.
Translate 50 remaining Kurmanji Kurdish values covering detailed rule actions for cards, checklists, swimlanes and dates, authentication, custom product HTML and JSON metadata, layout, and counters. Preserve technical formats and examples and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:18:47 +03:00
Lauri Ojansivu aca9ca6b6d Translate Kurmanji Kurdish rule action phrases.
Translate 50 remaining Kurmanji Kurdish values covering rule sentence fragments for board and list movement, archive restoration, labels, members, attachments, checklists, card positioning, colors, and email actions. Keep the composable terminology consistent and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:17:44 +03:00
Lauri Ojansivu cf6b7a39e3 Translate Kurmanji Kurdish scheduled automation.
Translate 50 remaining Kurmanji Kurdish values covering n8n and Node-RED workflow imports, scheduled and button triggers, due-date conditions, list and card automation, relative dates, units, and movement phrases. Preserve product names, named counts, weekday and duration syntax, and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:16:43 +03:00
Lauri Ojansivu 9198c37685 Translate Kurmanji Kurdish rule import and activity.
Translate 50 remaining Kurmanji Kurdish values covering label and custom-field activity, visual rule building, event triggers, and JSON, CSV, and Trello Butler rule exchange. Preserve all named and printf tokens and format names and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:15:31 +03:00
Lauri Ojansivu 172ac6a7c0 Translate Kurmanji Kurdish subtasks and card metadata.
Translate 50 remaining Kurmanji Kurdish values covering card lifecycle dates, colors and attribution, destructive board, notification and duplicate-list actions, subtask placement, minicard metadata, parent paths, and label activity. Preserve named and printf tokens and numeric display syntax and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:14:11 +03:00
Lauri Ojansivu c25b8bf287 Translate Kurmanji Kurdish runtime and tenancy.
Translate 50 remaining Kurmanji Kurdish values covering database and FerretDB diagnostics, reactivity, DDP and OS metrics, durations, custom-field display, account changes, visibility, and organization and team multitenancy administration. Preserve environment variables, modes, hostnames, and product names and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:13:01 +03:00
Lauri Ojansivu 011ca6481a Translate Kurmanji Kurdish limits and webhooks.
Translate 50 remaining Kurmanji Kurdish values covering attachment and API limits, avatar-upload blocking, registration and invitations, SMTP and TLS configuration, test mail, webhooks, and runtime version labels. Preserve all invitation tokens and technical names and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:11:46 +03:00
Lauri Ojansivu 1f91115956 Translate Kurmanji Kurdish tracking and customization.
Translate 50 remaining Kurmanji Kurdish values covering starred boards, time tracking, assignee and label shortcuts, uploads, custom logos and URL schemes, welcome and template boards, and WIP-limit guidance. Preserve numeric ranges and technical terms and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:10:34 +03:00
Lauri Ojansivu 81edde4e50 Translate Kurmanji Kurdish visibility and shortcuts.
Translate 50 remaining Kurmanji Kurdish values covering watched updates, private-page login markup, previews, public and private visibility, member removal and Sandstorm access, rescue dialogs, search, WIP limits, keyboard shortcuts, sidebars, signup, and default-board behavior. Preserve HTML, printf and named tokens and product terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:09:14 +03:00
Lauri Ojansivu 2fcbc97045 Document automatic AppImage releases.
Update the upcoming release summary and AppImage packaging notes for the full release workflow integration.

Thanks to xet7 !
2026-08-27 04:08:39 +03:00
Lauri Ojansivu cfe73ab69e Publish AppImages in the full release workflow.
Call the reusable AppImage workflow after the core GitHub Release publishes its amd64 and arm64 bundles. Pin the exact release tag, grant release-asset write permission, keep downstream packaging parallel, and add regression coverage for the dependency and reusable-workflow interface.

Thanks to xet7 !
2026-08-27 04:08:06 +03:00
Lauri Ojansivu c970db9741 Translate Kurmanji Kurdish selection and board roles.
Translate 50 remaining Kurmanji Kurdish values covering last-admin protection, leaving boards, list and card archive actions, settings and import dialogs, multi-selection, notification modes, archive states, and normal roles. Preserve named tokens and technical formats and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:05:57 +03:00
Lauri Ojansivu e98df1978f Translate Kurmanji Kurdish Trello API imports.
Translate 50 remaining Kurmanji Kurdish values covering ZIP path safety, workspace placement, direct Trello API import and job controls, member mapping, validation messages, keyboard shortcuts, and label lifecycle. Preserve URLs, API terms, numeric examples, and printf tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:04:49 +03:00
Lauri Ojansivu d0744bc34d Translate Kurmanji Kurdish filters and board imports.
Translate 50 remaining Kurmanji Kurdish values covering assignee and advanced filters, member status and impersonation, multi-source board import guidance, and secure Trello JSON and ZIP handling. Preserve every operator, endpoint, named token, API path, and format and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:03:28 +03:00
Lauri Ojansivu f0907be731 Translate Kurmanji Kurdish card exports and filters.
Translate 50 remaining Kurmanji Kurdish values covering account conflicts, card exports to PDF and Excel, disk-space errors, attachment metadata, sorting, and date, label, and member filters. Preserve format names and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:01:32 +03:00
Lauri Ojansivu db3f58658e Translate Kurmanji Kurdish email and import errors.
Translate 50 remaining Kurmanji Kurdish values covering permanent-delete controls, profile and WIP dialogs, transactional email templates, authorization failures, import schemas, empty-export recovery, linked-card safety, and disabled accounts. Preserve all email placeholders and technical formats and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 04:00:27 +03:00
Lauri Ojansivu c81750a22b Translate Kurmanji Kurdish roles and custom fields.
Translate 50 remaining Kurmanji Kurdish values covering permission roles, comment visibility, deletion confirmations, clipboard and template copying, valid JSON examples, labels, and custom-field types. Preserve the JSON structure and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 03:59:13 +03:00
Lauri Ojansivu 8b5a5aca6f Translate Kurmanji Kurdish card aging and colors.
Translate 50 remaining Kurmanji Kurdish values covering starring, automatic list widths, card-aging tiers, keyboard movement, accessible dialog controls, and color names. Preserve numeric tiers and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 03:58:11 +03:00
Lauri Ojansivu 54f36e5e70 Translate Kurmanji Kurdish member mapping and themes.
Translate 50 remaining Kurmanji Kurdish values covering member and restore dialogs, linked items, safe imported-member mapping, themes, fonts, text colors, avatars, language, and permissions. Preserve CAS and numeric examples and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 03:56:56 +03:00
Lauri Ojansivu ff2738125b Translate Kurmanji Kurdish voting and imports.
Translate 50 remaining Kurmanji Kurdish values covering card membership, voting and Planning Poker, dependencies, organization, team and domain assignment, backgrounds, account deletion, and import dialogs. Preserve product terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 03:55:56 +03:00
Lauri Ojansivu c707892b64 Translate Kurmanji Kurdish board views and archives.
Translate 50 remaining Kurmanji Kurdish values covering board visibility and navigation, mobile and desktop views, zoom, calendar modes, and card, list, and swimlane archive guidance. Preserve HTML, named and printf tokens, and numeric ranges and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 03:55:00 +03:00
Lauri Ojansivu 263f4f5936 Translate Kurmanji Kurdish board administration.
Translate 50 remaining Kurmanji Kurdish values covering announcements, archive and template actions, reconnection guidance, board backgrounds, member summaries, and privacy. Preserve all named, printf, and HTML tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 03:53:50 +03:00
Lauri Ojansivu 50ac223590 Translate Kurmanji Kurdish board layout controls.
Translate 50 remaining Kurmanji Kurdish values covering workspace bulk actions, Home-board selection, list widths, keyboard shortcuts, swimlane height, and creation controls. Preserve numeric constraints and printf tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 03:52:38 +03:00
Lauri Ojansivu e5c19ed7a8 Translate Kurmanji Kurdish workspace activity.
Translate 50 remaining Kurmanji Kurdish values covering card movement, membership, checklist and date activity, and workspace creation and editing. Preserve all named and printf tokens exactly and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 03:51:30 +03:00
Lauri Ojansivu e128543766 Translate Kurmanji Kurdish activity history.
Translate the first 50 remaining Kurmanji Kurdish values covering board, swimlane, list, card, checklist, label, attachment, and comment activity history. Preserve all named and printf tokens exactly and add locale-wide regression coverage. These direct translations are low-confidence and welcome review by a Kurmanji speaker.

Thanks to xet7 !
2026-08-27 03:50:34 +03:00
Lauri Ojansivu e07a346744 Complete the Kashmiri translation.
Translate the final 17 Kashmiri placeholders covering IP event fields, filesystem integrity, scoped import and export controls, and numeric search syntax. Preserve named tokens, pseudo-tags, formats, and product names exactly and pin the zero-remaining invariant. These direct translations are low-confidence and welcome review by a Kashmiri speaker.

Thanks to xet7 !
2026-08-27 03:49:00 +03:00
Lauri Ojansivu 2557aa4e9d Translate Kashmiri account and repair status.
Translate 50 remaining Kashmiri values covering repositories, account access, authentication errors, problem reporting, card repair, CPU status, and event metadata. Preserve repair counters exactly and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.

Thanks to xet7 !
2026-08-27 03:47:46 +03:00
Lauri Ojansivu 50d5a3872f Translate Kashmiri attachment migration monitoring.
Translate 50 remaining Kashmiri values covering attachment migration targets and tuning, CPU limits, monitoring controls, progress, storage statistics, and pagination. Preserve numeric ranges and technical terms and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.

Thanks to xet7 !
2026-08-27 03:46:15 +03:00
Lauri Ojansivu 22a694b4c9 Translate Kashmiri migration job monitoring.
Translate 50 remaining Kashmiri values covering board repair steps, one-time conversion, CPU and storage monitoring, recurring schedules, and migration job labels. Preserve technical terms and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.

Thanks to xet7 !
2026-08-27 03:45:03 +03:00
Lauri Ojansivu 34eaaf07ba Translate Kashmiri board integrity migrations.
Translate 50 remaining Kashmiri values covering board-integrity migrations, duplicate-list cleanup, lost-card and archive recovery, missing-list and URL repairs, migration confirmations, and progress steps. Preserve technical identifiers and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.

Thanks to xet7 !
2026-08-27 03:43:31 +03:00
Lauri Ojansivu 45c24faf35 Translate Kashmiri storage migration controls.
Translate 50 remaining Kashmiri values covering GCS and GridFS storage, migration lifecycle controls, S3 credentials, scheduled board operations, and attachment monitoring. Preserve product, region, protocol, and path tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:39:46 +03:00
Lauri Ojansivu 99ec4c8210 Translate Kashmiri cloud backup configuration.
Translate 50 remaining Kashmiri values covering backup scheduling and restore, Google Cloud credentials, S3, Azure and GCS setup guidance, connection tests, and cloud storage status. Preserve product, JSON, path, credential, and format tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:38:27 +03:00
Lauri Ojansivu 3e01d08a62 Document rebuilt AppImage heap protection.
Record why a rerun around the already-published v11.15 bundle still failed and how AppRun now supplies the architecture-safe default independently of that older launcher.\n\nThanks to xet7 !
2026-08-27 03:36:10 +03:00
Lauri Ojansivu 404a79e007 Make rebuilt 32-bit AppImages start old bundles.
Set a 1 GiB V8 heap default in the generated AppRun for i686 and armhf while preserving explicit NODE_OPTIONS. This protects AppImages rebuilt around an already-published bundle whose launcher predates the 32-bit heap fix, which is why the v11.15 i686 rerun still died during snapshot deserialization. Extend regression coverage for the old-bundle boundary.\n\nThanks to xet7 !
2026-08-27 03:35:43 +03:00
Lauri Ojansivu b43296e197 Translate Kashmiri privacy and backup settings.
Translate 50 remaining Kashmiri values covering Sandstorm cleanup, adaptive card loading, safe text rendering, import and export privacy controls, activity controls, and backup scope. Preserve named, HTML, path, environment, product, and configuration tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:32:37 +03:00
Lauri Ojansivu a9a1ba9b3f Translate Kashmiri scheduled jobs and migration.
Translate 50 remaining Kashmiri values covering scheduled-job lifecycle and error handling, filesystem and cloud storage, database migration, and Sandstorm migration status. Preserve named, URL, command, configuration, and product tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:29:20 +03:00
Lauri Ojansivu 15e4c65eb9 Translate Kashmiri lockout and scheduled operations.
Translate 50 remaining Kashmiri values covering Accessibility content, brute-force lockout settings, Admin Panel user status, attachment storage paths, and scheduled board operations. Preserve technical terminology and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:27:45 +03:00
Lauri Ojansivu 948f9ae6bc Translate Kashmiri status and support settings.
Translate 50 remaining Kashmiri values covering board status and upload progress, file limits, custom translations, checklist display, Support, and Accessibility pages. Preserve named and technical tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:26:29 +03:00
Lauri Ojansivu b9340fc6e0 Translate Kashmiri attachment storage maintenance.
Translate 50 remaining Kashmiri values covering bulk attachment migration, storage repair and progress, file statistics, storage identifiers, and MongoDB compaction. Preserve technical names and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:25:01 +03:00
Lauri Ojansivu 78292d89c8 Translate Kashmiri diagnostics and storage actions.
Translate 50 remaining Kashmiri values covering team and organization assignment, Node memory diagnostics, legal notices, checklist actions, and attachment storage moves. Preserve technical names and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:23:32 +03:00
Lauri Ojansivu 97b0bd02a8 Translate Kashmiri recovery and ticket details.
Translate 50 remaining Kashmiri values covering Office and API report details, data recovery status, swimlane copying, wait indicators, ticket states, and card detail dialogs. Preserve named, format, markup, and configuration tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:22:11 +03:00
Lauri Ojansivu 257fa572a9 Translate Kashmiri maps and reports.
Translate 50 remaining Kashmiri values covering map coordinates and detection, server troubleshooting, string templates, board activity, Problems reports, and office login reporting. Preserve named, format, HTML, and command tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:20:55 +03:00
Lauri Ojansivu 4e75d754d2 Translate Kashmiri dependencies and locations.
Translate 50 remaining Kashmiri values covering board and card sorting, stickers, card dependencies, dependency imports, board backgrounds, and card locations. Preserve named and format tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:19:34 +03:00
Lauri Ojansivu c4f07a30e7 Translate Kashmiri advanced-search guidance.
Translate 50 remaining Kashmiri values covering search predicates, validation errors, pagination, and the complete advanced-search operator guide. Preserve named, format, HTML, and Markdown tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:18:32 +03:00
Lauri Ojansivu 00c5c86220 Translate Kashmiri advanced-search vocabulary.
Translate 50 remaining Kashmiri values covering search-result counts and single-token advanced-search operators and predicates for boards, cards, dates, and content fields. Preserve named and format tokens and add a negative regression guard against spaces in query operator names. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:16:28 +03:00
Lauri Ojansivu cbb9ce2ddb Translate Kashmiri shared templates and card views.
Translate 50 remaining Kashmiri values covering linked-card safeguards, tasks, domain sharing, shared templates, My Cards and Due Cards views, global search, and missing-item errors. Preserve named, format, and markup tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:15:12 +03:00
Lauri Ojansivu bbf680be13 Translate Kashmiri roles and editor controls.
Translate 50 remaining Kashmiri values covering multi-window cards, inline editor shortcuts, organization and user dialogs, notifications, board roles, and weekday names. Preserve named, format, and markup tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:13:35 +03:00
Lauri Ojansivu 7ea2ee0052 Translate Kashmiri authentication and reminders.
Translate 50 remaining Kashmiri values covering member lists, custom HTML, authentication errors, board duplication, due reminders, deletion confirmations, and resize controls. Preserve named, format, and HTML tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:12:00 +03:00
Lauri Ojansivu 5fefb6212a Translate Kashmiri rule actions and branding.
Translate 50 remaining Kashmiri values covering rule action descriptions, checklist and swimlane creation, date-field actions, authentication settings, custom manifests, and branding. Preserve named, format, and markup tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:10:23 +03:00
Lauri Ojansivu f219a74391 Translate Kashmiri automation conditions and actions.
Translate 50 remaining Kashmiri values covering movement and archive conditions, label, member and checklist triggers, card positioning, and email actions. Preserve named and format tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:09:03 +03:00
Lauri Ojansivu 99ea9aa307 Translate Kashmiri workflow schedules and actions.
Translate 50 remaining Kashmiri values covering visual-workflow imports, scheduled and button triggers, due-date conditions, time units, and list actions. Preserve named and format tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:07:57 +03:00
Lauri Ojansivu e96c29e891 Translate Kashmiri activity and automation rules.
Translate 50 remaining Kashmiri values covering activity labels, custom fields, automation-rule editing, workflow triggers, and rule import and export. Preserve named and format tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 03:06:33 +03:00
Lauri Ojansivu 489c59735f Document the 32-bit AppImage fixes.
Add the i686 heap and armhf capability-probe corrections to the Upcoming WeKan release notes, including the failure modes and regression coverage.\n\nThanks to xet7 !
2026-08-27 03:02:46 +03:00
Lauri Ojansivu e8b6003e71 Fix 32-bit AppImage startup checks.
Keep the automatically selected V8 heap within the address space of a 32-bit bundled Node binary, so i686 can deserialize its startup snapshot. Probe the inner bundled Node before smoke-testing an AppImage, so an armhf runner without the hard-float loader is reported as unchecked instead of as a broken application. Add plain-Node regression coverage for the heap limit, override, capability probe and HTTP test ordering.\n\nThanks to xet7 !
2026-08-27 03:02:04 +03:00
Lauri Ojansivu ba11977dbd Translate Kashmiri card and subtask settings.
Translate 50 remaining Kashmiri values covering card dates, destructive cleanup, subtask routing, minicard presentation and parent-card paths. Preserve named and format tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:59:18 +03:00
Lauri Ojansivu fd08000401 Translate Kashmiri diagnostics and tenancy.
Translate 50 remaining Kashmiri values covering database and operating-system diagnostics, custom-field display and organization and team tenancy. Preserve environment names and technical literals and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:57:58 +03:00
Lauri Ojansivu 25f7003658 Translate Kashmiri limits and webhooks.
Translate 50 remaining Kashmiri values covering attachment and API limits, registration, SMTP invitations, webhooks and runtime version labels. Preserve email tokens and technical names and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:56:45 +03:00
Lauri Ojansivu 2b3aefcbb2 Translate Kashmiri tracking and branding.
Translate 50 remaining Kashmiri values covering starred boards, time tracking, uploads, custom branding, welcome templates and WIP validation. Preserve locale terminology and extend token, markup and progress regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:55:09 +03:00
Lauri Ojansivu 16a8f050e0 Translate Kashmiri access and shortcuts.
Translate 50 remaining Kashmiri values covering watch notifications, public and private access, member removal, search, WIP limits and keyboard shortcuts. Preserve named, format and HTML tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:53:53 +03:00
Lauri Ojansivu 930c453261 Translate Kashmiri membership and selection.
Translate 50 remaining Kashmiri values covering board membership, list archives, swimlane actions, bulk selection, role descriptions and notifications. Preserve named tokens and locale terminology and extend regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:52:21 +03:00
Lauri Ojansivu 05dc535816 Translate Kashmiri Trello imports.
Translate 50 remaining Kashmiri values covering Trello API and ZIP imports, workspace placement, member mapping, date validation and labels. Preserve URLs, format tokens and structured terms and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:50:56 +03:00
Lauri Ojansivu 6c99557bfe Translate Kashmiri filters and board imports.
Translate 50 remaining Kashmiri values covering advanced filters, activity display, imported members and board imports from external formats. Preserve operators, named tokens and structured examples and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:49:14 +03:00
Lauri Ojansivu b6a248bb25 Translate Kashmiri exports and filters.
Translate 50 remaining Kashmiri values covering user validation, card exports, disk-space messages, list sorting and date, label and member filters. Preserve token and markup inventories and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:46:57 +03:00
Lauri Ojansivu 0e16a95e4b Translate Kashmiri accounts and validation.
Translate 50 remaining Kashmiri values covering permanent deletion, profiles, dates, account emails, import validation and access errors. Preserve every email and format token and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:45:49 +03:00
Lauri Ojansivu a1595e761c Translate Kashmiri roles and custom fields.
Translate 50 remaining Kashmiri values covering board roles, deletion confirmations, clipboard actions, template copying and custom fields. Preserve token and markup inventories, keep the translated example valid JSON and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:44:16 +03:00
Lauri Ojansivu 8d9cd676f6 Translate Kashmiri navigation and colors.
Translate 50 remaining Kashmiri values covering starring, automatic list widths, card aging, keyboard movement, dialogs and board colors. Preserve token and markup inventories and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:42:38 +03:00
Lauri Ojansivu 50e4856038 Translate Kashmiri members and appearance.
Translate 50 remaining Kashmiri values covering member mapping, linked items, themes, fonts, avatars and permissions. Preserve token and markup inventories and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:41:32 +03:00
Lauri Ojansivu 7f3f907c16 Translate Kashmiri voting and imports.
Translate 50 remaining Kashmiri values covering card membership, voting, planning poker, dependencies, organization and team controls and imports. Preserve token and markup inventories and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:40:25 +03:00
Lauri Ojansivu b9e6a98569 Translate Kashmiri board views and archives.
Translate 50 remaining Kashmiri values covering board views, display modes, calendars, archive guidance and card editing. Preserve named, format and HTML tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:39:27 +03:00
Lauri Ojansivu 7aff9a486b Translate Kashmiri archives and administration.
Translate 50 remaining Kashmiri values covering administration, announcements, archives, board appearance, membership views and privacy guidance. Preserve named, format and HTML tokens and extend locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:37:52 +03:00
Lauri Ojansivu 611f0e336d Translate Kashmiri board layout controls.
Translate 50 remaining Kashmiri values covering board selection, home-board actions, list widths, swimlane heights and checklist creation. Preserve all format tokens and extend locale progress regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:36:41 +03:00
Lauri Ojansivu fb732a0b97 Translate Kashmiri workspaces and activity.
Translate 50 remaining Kashmiri values covering cross-board moves, activity history, checklists and workspace navigation. Preserve all format and named tokens and extend the locale regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:35:07 +03:00
Lauri Ojansivu 2a3e6874e4 Translate Kashmiri board activity.
Translate the first 50 remaining Kashmiri values covering board, card, checklist and comment activity. Preserve every code token and add whole-locale token, markup and progress regression coverage. These direct translations are low-confidence and welcome review by a Kashmiri speaker.\n\nThanks to xet7 !
2026-08-27 02:33:49 +03:00
github-actions[bot] 7acaed1c25 Bump versions for v11.15 2026-08-26 22:34:35 +00:00
Lauri Ojansivu c4c372272d Prepare v11.15 release 2026-08-27 01:33:30 +03:00
Lauri Ojansivu 71944526de Document reactive cross-board destinations
Add the #2494 UI and publication fix to the upcoming release summary with its three-browser regression evidence.\n\nThanks to rlach and xet7 !
2026-08-27 01:30:12 +03:00
Lauri Ojansivu 04bd60dd3f Keep cross-board move destinations reactive
Give move and copy dialogs an authorization-filtered destination subscription and query its Minimongo results reactively, so boards created through WeKan remain selectable. Replace the REST-only #2494 regression with a browser-driven board, list, card and Move Card workflow across all three engines.\n\nThanks to rlach and xet7 !\n\nFixes #2494,
2026-08-27 01:29:55 +03:00
Lauri Ojansivu 1b56821118 Verify cross-board card positioning through WeKan API 2026-08-27 01:01:21 +03:00
Lauri Ojansivu b9d3b6bd81 Keep private linked-card snapshots visible in tests 2026-08-27 00:36:05 +03:00
Lauri Ojansivu d5ecede570 Update completed translation coverage 2026-08-27 00:36:05 +03:00
Lauri Ojansivu b6b0381110 Document VSCodium sandbox and test auth boundaries. 2026-08-26 22:50:18 +03:00
Lauri Ojansivu 922b6275c6 Document authentication security fixes. 2026-08-26 22:43:57 +03:00
Lauri Ojansivu 12f2fddc2c Fix OIDC and account metadata security boundaries.
Thanks to crypto-nidh and xet7 !
2026-08-26 22:41:44 +03:00
Lauri Ojansivu 5c02b0d28e Update upcoming release translation summary. 2026-08-26 22:31:59 +03:00
Lauri Ojansivu 4d65e27736 Complete Konkani translations. 2026-08-26 22:30:04 +03:00
Lauri Ojansivu cf347af2da Translate Konkani migration status and accounts. 2026-08-26 22:26:54 +03:00
Lauri Ojansivu f3ffccebe6 Translate Konkani migration monitoring and tuning. 2026-08-26 22:25:32 +03:00
Lauri Ojansivu 498a9bd49a Translate Konkani migration steps and repair. 2026-08-26 22:24:19 +03:00
Lauri Ojansivu 03f63c1d1d Translate Konkani board integrity migrations. 2026-08-26 22:22:30 +03:00
Lauri Ojansivu 75a12c647d Translate Konkani migration controls and monitoring. 2026-08-26 22:21:10 +03:00
Lauri Ojansivu a10f82c4b5 Translate Konkani cloud storage setup. 2026-08-26 22:19:41 +03:00
Lauri Ojansivu 60932fef46 Translate Konkani security and backups. 2026-08-26 22:18:35 +03:00
Lauri Ojansivu e8e1d7a704 Translate Konkani loading and security controls. 2026-08-26 22:16:18 +03:00
Lauri Ojansivu dee5d3dc39 Translate Konkani scheduled and database migrations. 2026-08-26 22:14:42 +03:00
Lauri Ojansivu 4818d72a8e Translate Konkani scheduled board operations. 2026-08-26 22:13:00 +03:00
Lauri Ojansivu 374038f4f2 Translate Konkani accessibility and lockout settings. 2026-08-26 22:12:02 +03:00
Lauri Ojansivu 1b59718780 Translate Konkani board status and uploads. 2026-08-26 22:10:35 +03:00
Lauri Ojansivu 35f2ad63b8 Translate Konkani storage repair and compaction. 2026-08-26 22:09:37 +03:00
Lauri Ojansivu 1bbd0dc24c Translate Konkani organizations and attachment moves. 2026-08-26 22:08:09 +03:00
Lauri Ojansivu b4ac9d85b7 Translate Konkani tickets and runtime metrics. 2026-08-26 22:07:15 +03:00
Lauri Ojansivu 43ab14f8d7 Translate Konkani API and recovery reports. 2026-08-26 22:05:44 +03:00
Lauri Ojansivu 69186a1c1b Translate Konkani maps and reports. 2026-08-26 22:04:36 +03:00
Lauri Ojansivu 097866debd Translate Konkani dependencies and locations. 2026-08-26 22:03:10 +03:00
Lauri Ojansivu 04dee15381 Translate Konkani search predicates and dependencies. 2026-08-26 22:02:13 +03:00
Lauri Ojansivu efa1cff336 Translate Konkani global search guidance. 2026-08-26 22:00:30 +03:00
Lauri Ojansivu 8c04e8525e Translate Konkani global search operators. 2026-08-26 21:59:25 +03:00
Lauri Ojansivu 8fbeee0feb Translate Konkani card views and search. 2026-08-26 21:58:17 +03:00
Lauri Ojansivu ca5d7d233b Translate Konkani roles and shared templates. 2026-08-26 21:57:13 +03:00
Lauri Ojansivu db3e454ad8 Translate Konkani editing and role settings. 2026-08-26 21:55:47 +03:00
Lauri Ojansivu 23842e6e3e Translate Konkani board display and reminders. 2026-08-26 21:54:44 +03:00
Lauri Ojansivu 9a5f9b2aa7 Translate Konkani date automation and metadata. 2026-08-26 21:53:08 +03:00
Lauri Ojansivu 0db99b91ea Translate Konkani automation actions. 2026-08-26 21:52:15 +03:00
Lauri Ojansivu 29e5ef2d79 Translate Konkani automation conditions. 2026-08-26 21:51:00 +03:00
Lauri Ojansivu 6dd4f27faa Translate Konkani automation schedules and triggers. 2026-08-26 21:50:10 +03:00
Lauri Ojansivu b747216639 Translate Konkani automation workflows. 2026-08-26 21:48:52 +03:00
Lauri Ojansivu 9a41565dbf Translate Konkani subtasks and automation rules. 2026-08-26 21:47:51 +03:00
Lauri Ojansivu b3329c5fde Translate Konkani card dates and deletion. 2026-08-26 21:46:36 +03:00
Lauri Ojansivu 74f84080ba Translate Konkani system and organization settings. 2026-08-26 21:45:36 +03:00
Lauri Ojansivu e9f34369e9 Translate Konkani invitations and webhooks. 2026-08-26 21:44:15 +03:00
Lauri Ojansivu aa6e62e219 Translate Konkani branding and attachment limits. 2026-08-26 21:43:21 +03:00
Lauri Ojansivu dcec3a1c57 Translate Konkani tracking and uploads. 2026-08-26 21:41:53 +03:00
Lauri Ojansivu e7c35d2dc5 Translate Konkani notifications and shortcuts. 2026-08-26 21:41:02 +03:00
Lauri Ojansivu 70265d08b8 Translate Konkani selection and board roles. 2026-08-26 21:39:17 +03:00
Lauri Ojansivu c85996a203 Translate Konkani import mapping and list actions. 2026-08-26 21:38:19 +03:00
Lauri Ojansivu 590436d4f4 Translate Konkani Trello API imports. 2026-08-26 21:36:44 +03:00
Lauri Ojansivu 1859bd8efc Translate Konkani filters and board imports. 2026-08-26 21:35:40 +03:00
Lauri Ojansivu 371b9676e1 Translate Konkani sorting and filters. 2026-08-26 21:33:23 +03:00
Lauri Ojansivu f76d2551ab Translate Konkani email and card exports. 2026-08-26 21:32:33 +03:00
Lauri Ojansivu 093d0769b0 Translate Konkani profiles and enrollment. 2026-08-26 21:30:49 +03:00
Lauri Ojansivu 45c4af2763 Translate Konkani roles and custom fields. 2026-08-26 21:29:59 +03:00
Lauri Ojansivu 075dc54c23 Translate Konkani board recovery and colors. 2026-08-26 21:28:34 +03:00
Lauri Ojansivu c36cdd570a Translate Konkani themes and navigation. 2026-08-26 21:27:51 +03:00
Lauri Ojansivu 9d0ff7af09 Translate Konkani recovery and member mapping. 2026-08-26 21:26:35 +03:00
Lauri Ojansivu 1e3f26c773 Translate Konkani voting and import dialogs. 2026-08-26 21:25:28 +03:00
Lauri Ojansivu 731fdfd0ac Translate Konkani calendar and archive guidance. 2026-08-26 21:24:13 +03:00
Lauri Ojansivu 0ada50a44b Translate Konkani board views and backgrounds. 2026-08-26 21:23:13 +03:00
Lauri Ojansivu 839ed447af Translate Konkani archives and administration. 2026-08-26 21:21:49 +03:00
Lauri Ojansivu ec28906135 Translate Konkani board layout controls. 2026-08-26 21:20:55 +03:00
Lauri Ojansivu 47c3b0a96d Translate Konkani workspace activity. 2026-08-26 21:19:01 +03:00
Lauri Ojansivu 0b2710c3de Translate Konkani board activity. 2026-08-26 21:17:54 +03:00
Lauri Ojansivu 9f2bcb319f Complete Kannada translations. 2026-08-26 21:15:13 +03:00
Lauri Ojansivu fb65d3d9a7 Translate Kannada accounts and problem repair. 2026-08-26 21:14:09 +03:00
Lauri Ojansivu 1c0145ad96 Translate Kannada migration monitoring and tuning. 2026-08-26 21:12:37 +03:00
Lauri Ojansivu 0bdee1193b Translate Kannada migration steps and monitoring. 2026-08-26 21:11:09 +03:00
Lauri Ojansivu 4007f6c2fc Translate Kannada board integrity migrations. 2026-08-26 21:09:38 +03:00
Lauri Ojansivu afd9654ba0 Translate Kannada migrations and S3 controls. 2026-08-26 21:07:26 +03:00
Lauri Ojansivu fa91e62a0c Translate Kannada cloud storage and migrations. 2026-08-26 21:06:07 +03:00
Lauri Ojansivu 9a5b3e1dff Translate Kannada backup scheduling and restore modes. 2026-08-26 21:04:18 +03:00
Lauri Ojansivu 571aa47439 Translate Kannada privacy and notification controls. 2026-08-26 21:03:11 +03:00
Lauri Ojansivu d71e1eb441 Translate Kannada loading and security controls. 2026-08-26 21:01:12 +03:00
Lauri Ojansivu 0d2e842147 Translate Kannada storage and database migrations. 2026-08-26 20:59:06 +03:00
Lauri Ojansivu 8fcd63db11 Translate Kannada users and scheduled operations. 2026-08-26 20:57:15 +03:00
Lauri Ojansivu aafbb756dd Translate Kannada accessibility and lockout settings. 2026-08-26 20:56:11 +03:00
Lauri Ojansivu 2d1b8b98c3 Translate Kannada uploads and custom translations. 2026-08-26 20:54:32 +03:00
Lauri Ojansivu 81586e28ad Translate Kannada attachment storage maintenance. 2026-08-26 20:53:29 +03:00
Lauri Ojansivu 4307df7bb6 Translate Kannada checklist and attachment actions. 2026-08-26 20:51:50 +03:00
Lauri Ojansivu 6c067a4c4a Translate Kannada tickets and memory diagnostics. 2026-08-26 20:50:49 +03:00
Lauri Ojansivu 9529b782b2 Translate Kannada recovery reports and spinners. 2026-08-26 20:49:20 +03:00
Lauri Ojansivu 1600b85c1c Translate Kannada maps and admin reports. 2026-08-26 20:48:18 +03:00
Lauri Ojansivu fc8d851c09 Translate Kannada dependencies and locations. 2026-08-26 20:46:51 +03:00
Lauri Ojansivu 77465b53ad Translate Kannada search notes and dependencies. 2026-08-26 20:45:59 +03:00
Lauri Ojansivu 1f4338a429 Translate Kannada search combination guidance. 2026-08-26 20:44:53 +03:00
Lauri Ojansivu 47ed65164f Translate Kannada search status guidance. 2026-08-26 20:44:04 +03:00
Lauri Ojansivu d54139723a Translate Kannada search operator guidance. 2026-08-26 20:43:08 +03:00
Lauri Ojansivu bc336c5096 Translate Kannada search help navigation. 2026-08-26 20:42:18 +03:00
Lauri Ojansivu 6e3d296deb Translate Kannada search predicates and errors. 2026-08-26 20:41:34 +03:00
Lauri Ojansivu e62f153cad Translate Kannada search and due card views. 2026-08-26 20:40:51 +03:00
Lauri Ojansivu c7600d04c1 Translate Kannada domains and card views. 2026-08-26 20:39:34 +03:00
Lauri Ojansivu 98f2a8697a Translate Kannada users and board roles. 2026-08-26 20:38:30 +03:00
Lauri Ojansivu 6ec58f09a4 Translate Kannada reminders and editor settings. 2026-08-26 20:37:07 +03:00
Lauri Ojansivu 6f783bf84a Translate Kannada authentication and due activity. 2026-08-26 20:35:59 +03:00
Lauri Ojansivu 95a14a6f03 Translate Kannada checklist and date automation. 2026-08-26 20:34:30 +03:00
Lauri Ojansivu 5c77f38ce3 Translate Kannada automation actions. 2026-08-26 20:33:39 +03:00
Lauri Ojansivu be47905a1a Translate Kannada automation conditions. 2026-08-26 20:32:30 +03:00
Lauri Ojansivu 03110650cd Translate Kannada automation schedules and imports. 2026-08-26 20:31:51 +03:00
Lauri Ojansivu 6f7a70db02 Translate Kannada automation workflows. 2026-08-26 20:30:34 +03:00
Lauri Ojansivu 22c848a1e0 Translate Kannada card hierarchy and activity. 2026-08-26 20:29:42 +03:00
Lauri Ojansivu 1735151f72 Translate Kannada organization and card settings. 2026-08-26 20:28:12 +03:00
Lauri Ojansivu 890a2f6448 Translate Kannada webhooks and system settings. 2026-08-26 20:26:59 +03:00
Lauri Ojansivu 9dec6313bf Translate Kannada registration and SMTP settings. 2026-08-26 20:25:44 +03:00
Lauri Ojansivu 6891a83444 Translate Kannada tracking and attachment limits. 2026-08-26 20:24:49 +03:00
Lauri Ojansivu 7d94dcdb02 Translate Kannada navigation and time tracking. 2026-08-26 20:23:08 +03:00
Lauri Ojansivu 3bb8efa502 Translate Kannada visibility and member actions. 2026-08-26 20:22:15 +03:00
Lauri Ojansivu d4e8dae5b5 Translate Kannada settings and selection controls. 2026-08-26 20:20:39 +03:00
Lauri Ojansivu a29d5cd5bd Translate Kannada Trello jobs and list actions. 2026-08-26 20:19:48 +03:00
Lauri Ojansivu b27cb01e4c Translate Kannada Trello import guidance. 2026-08-26 20:18:16 +03:00
Lauri Ojansivu b64d6893ca Translate Kannada filter and import guidance. 2026-08-26 20:17:07 +03:00
Lauri Ojansivu 2f9fbe3961 Translate Kannada advanced filter controls. 2026-08-26 20:15:20 +03:00
Lauri Ojansivu 296ffaada0 Translate Kannada exports and filters. 2026-08-26 20:14:44 +03:00
Lauri Ojansivu c315f4e4b5 Translate Kannada email and import errors. 2026-08-26 20:13:27 +03:00
Lauri Ojansivu 11bd63cf3e Translate Kannada custom fields and email. 2026-08-26 20:12:16 +03:00
Lauri Ojansivu 743f2a3149 Translate Kannada board roles and confirmations. 2026-08-26 20:10:49 +03:00
Lauri Ojansivu 01f73c122d Translate Kannada navigation and colors. 2026-08-26 20:09:56 +03:00
Lauri Ojansivu d51de1217d Translate Kannada appearance settings. 2026-08-26 20:08:42 +03:00
Lauri Ojansivu f150cee208 Translate Kannada imports and member mapping. 2026-08-26 20:07:56 +03:00
Lauri Ojansivu c0c4dd354d Translate Kannada voting controls. 2026-08-26 20:06:33 +03:00
Lauri Ojansivu 5a6fdac961 Translate Kannada board views and archives. 2026-08-26 20:05:48 +03:00
Lauri Ojansivu 1767fe07df Translate Kannada board display settings. 2026-08-26 20:04:19 +03:00
Lauri Ojansivu 0703a3bf15 Translate Kannada board layout and archives. 2026-08-26 20:03:29 +03:00
Lauri Ojansivu 422b846883 Translate Kannada board selection controls. 2026-08-26 20:01:52 +03:00
Lauri Ojansivu 46e944178c Translate Kannada workspace activity. 2026-08-26 20:00:55 +03:00
Lauri Ojansivu 7dd067f153 Translate Kannada board activity. 2026-08-26 19:59:37 +03:00
Lauri Ojansivu 8edba09497 Complete Greenlandic translations. 2026-08-26 19:56:43 +03:00
Lauri Ojansivu c26e27e7a8 Translate Greenlandic accounts and system events. 2026-08-26 19:55:59 +03:00
Lauri Ojansivu d8c5b51721 Translate Greenlandic migration resources. 2026-08-26 19:54:38 +03:00
Lauri Ojansivu 8ef8900fab Translate Greenlandic migration monitoring. 2026-08-26 19:53:38 +03:00
Lauri Ojansivu f9b3823c13 Translate Greenlandic board repair migrations. 2026-08-26 19:52:30 +03:00
Lauri Ojansivu 0a56e3bb03 Translate Greenlandic migration and S3 settings. 2026-08-26 19:51:11 +03:00
Lauri Ojansivu 456b6a97b9 Translate Greenlandic backup and cloud settings. 2026-08-26 19:50:14 +03:00
Lauri Ojansivu f53a9271a3 Translate Greenlandic security and backups. 2026-08-26 19:48:44 +03:00
Lauri Ojansivu 29f05f69ad Translate Greenlandic migration and storage. 2026-08-26 19:46:41 +03:00
Lauri Ojansivu 6c4f2619f6 Translate Greenlandic lockout and scheduled jobs. 2026-08-26 19:45:11 +03:00
Lauri Ojansivu 1a5086310e Translate Greenlandic support and accessibility. 2026-08-26 19:44:03 +03:00
Lauri Ojansivu 6739d255aa Translate Greenlandic storage repair. 2026-08-26 19:43:08 +03:00
Lauri Ojansivu 2a1cc780ec Translate Greenlandic teams and attachment storage. 2026-08-26 19:41:44 +03:00
Lauri Ojansivu aa016b1dbb Translate Greenlandic recovery and support reports. 2026-08-26 19:40:12 +03:00
Lauri Ojansivu 691af0fd96 Translate Greenlandic maps and reports. 2026-08-26 19:39:09 +03:00
Lauri Ojansivu 567a689e39 Translate Greenlandic dependencies and locations. 2026-08-26 19:37:54 +03:00
Lauri Ojansivu a8b0afc9bc Translate Greenlandic search guidance. 2026-08-26 19:37:00 +03:00
Lauri Ojansivu 9c32a08011 Translate Greenlandic search operators. 2026-08-26 19:35:15 +03:00
Lauri Ojansivu 5df3fab75a Translate Greenlandic shared views and search. 2026-08-26 19:34:22 +03:00
Lauri Ojansivu 8c02334151 Translate Greenlandic roles and notifications. 2026-08-26 19:33:20 +03:00
Lauri Ojansivu 13e794d59e Translate Greenlandic reminders and layout. 2026-08-26 19:31:58 +03:00
Lauri Ojansivu 7f4d35ae53 Translate Greenlandic rule and web settings. 2026-08-26 19:30:38 +03:00
Lauri Ojansivu 59368c128c Translate Greenlandic rule conditions and actions. 2026-08-26 19:29:30 +03:00
Lauri Ojansivu 9136707ce5 Translate Greenlandic scheduled rules. 2026-08-26 19:28:35 +03:00
Lauri Ojansivu ae66331a83 Translate Greenlandic activity and rules. 2026-08-26 19:27:38 +03:00
Lauri Ojansivu 234c9b4151 Translate Greenlandic card dates and subtasks. 2026-08-26 19:26:24 +03:00
Lauri Ojansivu ecefb4e59c Translate Greenlandic runtime and tenancy. 2026-08-26 19:25:24 +03:00
Lauri Ojansivu e2a9d635b7 Translate Greenlandic limits and webhooks. 2026-08-26 19:24:26 +03:00
Lauri Ojansivu 33c2a9a596 Translate Greenlandic tracking and branding. 2026-08-26 19:23:25 +03:00
Lauri Ojansivu 3219858f59 Translate Greenlandic search and shortcuts. 2026-08-26 19:22:16 +03:00
Lauri Ojansivu 55f32d2995 Translate Greenlandic roles and selection. 2026-08-26 19:21:15 +03:00
Lauri Ojansivu c21fd09c09 Translate Greenlandic Trello API imports. 2026-08-26 19:20:12 +03:00
Lauri Ojansivu 3825a8771b Translate Greenlandic filters and board imports. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 410adb1d9f Translate Greenlandic exports and filters. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 74e2ed579c Translate Greenlandic email and import errors. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu bf57e7030a Translate Greenlandic roles and custom fields. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 0d7e2e0e0b Translate Greenlandic navigation and colors. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 45a52886a9 Translate Greenlandic members and themes. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 18c22e0f8c Translate Greenlandic voting and imports. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 30413d8b25 Translate Greenlandic board views and archives. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu b971f626f4 Translate Greenlandic administration and archives. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 2f5998c24f Translate Greenlandic workspace layouts. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 8ac54c1df2 Translate Greenlandic activity and workspaces. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 495e16e888 Translate Greenlandic board activity. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 93f9ad02c8 Complete Kazakh translations. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu cd79113f90 Translate Kazakh accounts and system events. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu e668d393f3 Translate Kazakh migration tuning and resources. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu db46bdd159 Translate Kazakh migration monitoring controls. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 906a363fc2 Translate Kazakh board repair migrations. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu a9b493f9c1 Translate Kazakh migration and S3 settings. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 4346c134ee Translate Kazakh backup and cloud settings. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 64729214c9 Translate Kazakh security and backup controls. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 36a41dafe8 Translate Kazakh migration and storage controls. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 7727604244 Translate Kazakh lockout and scheduled jobs. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 9932a72829 Translate Kazakh support and accessibility. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 21b882ee05 Translate Kazakh storage repair controls. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu a22d9b7673 Translate Kazakh teams and attachment storage. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 1f059cf1e5 Translate Kazakh recovery and support reports. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 0fa7a2e3a7 Translate Kazakh maps and reports. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu ccfdbc520f Translate Kazakh dependencies and locations. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu ab7a850e94 Translate Kazakh global search help. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu c931487d39 Translate Kazakh search syntax. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 21d78d0b41 Translate Kazakh shared views and search. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 9d69e89e70 Translate Kazakh roles and notifications. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu f86be205cc Translate Kazakh reminders and layout controls. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu b1db043575 Translate Kazakh rule and web settings. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu d1a1d91be8 Translate Kazakh rule conditions and actions. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu c0c515e385 Translate Kazakh scheduled rules. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 47e226b4d6 Translate Kazakh activity and rules. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu efe1bab531 Translate Kazakh subtasks and card settings. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu afa7713a21 Translate Kazakh runtime and tenancy settings. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 35166749bd Translate Kazakh limits and webhooks. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu ad3216cebc Translate Kazakh tracking and branding. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu d9b7b36211 Translate Kazakh search and shortcuts. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 6dbb344d19 Translate Kazakh roles and selection controls. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 061ffd93a6 Translate Kazakh Trello API imports. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 3c1039bf6c Translate Kazakh filters and board imports. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 92b46b5f42 Translate Kazakh exports and filters. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 0fb4e3d276 Translate Kazakh email and import errors. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 7ffb99a541 Translate Kazakh roles and custom fields. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 52067573c2 Translate Kazakh navigation and colors. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 0961141ff4 Translate Kazakh members and themes. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu e653d8c747 Translate Kazakh voting and board imports. 2026-08-26 19:19:40 +03:00
Lauri Ojansivu 14e8a23be4 Translate Kazakh board views and archives. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 87ae1f9eec Translate Kazakh board administration. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 71a2bd6ffa Translate Kazakh board layout controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 05289a37d8 Translate Kazakh workspace activity. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 48eaf02373 Translate Kazakh board activity history. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu d2e027588a Complete Javanese translations. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 9246e74dc5 Translate Javanese accounts and system events. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 539d7c67ba Translate Javanese migration tuning and resources. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu ae56d12392 Translate Javanese migration monitoring controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 41c4cd0833 Translate Javanese board repair migrations. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 098cf75cfa Translate Javanese migration and S3 settings. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu d4f914c53b Translate Javanese backup and cloud settings. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu e359a72cb5 Translate Javanese security and backup controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu cf8e207f4f Translate Javanese migration and storage controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 907f912a51 Translate Javanese lockout and scheduled jobs. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 5f49ced29a Translate Javanese support and accessibility. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 0ca90556a3 Translate Javanese storage repair and compaction. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu d37003ee85 Translate Javanese diagnostics and attachment actions. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 07a7547f76 Translate Javanese recovery and support reports. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 20efea1249 Translate Javanese maps and reports. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 479bda6e6f Translate Javanese dependencies and locations. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 86c3e2f599 Translate Javanese global search help. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu dacba12351 Translate Javanese global search syntax. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 7dce39ca81 Translate Javanese shared views and search. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 24be95df10 Translate Javanese roles and notifications. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 137ebca0a6 Translate Javanese reminders and layout controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 0aef196db7 Translate Javanese rule and web settings. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 6863366319 Translate Javanese rule conditions and actions. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu b96c02b441 Translate Javanese scheduled rule actions. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 271c9f4223 Translate Javanese rule workflows. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 58f81ae364 Translate Javanese subtask and card settings. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 43c11fa8ea Translate Javanese runtime and tenancy settings. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu ca443c699c Translate Javanese transfer and webhook settings. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 72df0beb89 Translate Javanese tracking and branding. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu eaacdd81e8 Translate Javanese search and shortcuts. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu c6f8f8b6b4 Translate Javanese board roles and selection. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 3eb521b7a8 Translate Javanese Trello import controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 6a86d8c76c Translate Javanese filters and board imports. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 2e82910984 Translate Javanese exports and filters. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu e8b45e715a Translate Javanese email and import errors. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 3507d302e4 Translate Javanese roles and custom fields. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu abb8ed1e5b Translate Javanese navigation and colors. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 4991ccc73d Translate Javanese member and theme settings. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 6610b58557 Translate Javanese voting and imports. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 7a76b57661 Translate Javanese board views and archives. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 6d10cef01e Translate Javanese board administration. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu ca996696e1 Translate Javanese board layout controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu f6e533dcd2 Translate Javanese workspace activity. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 30fd596009 Translate Javanese activity history. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu e84873cea0 Complete Inuktitut translations. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 121b6a50bd Translate Inuktitut account diagnostics. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 2caafb194f Translate Inuktitut migration tuning. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 071cfede6f Translate Inuktitut migration monitoring. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 6cfd7154b7 Translate Inuktitut board repair migrations. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 18474bb18b Translate Inuktitut migration controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 71a110da8b Translate Inuktitut cloud backup settings. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 8101480373 Translate Inuktitut security and backup controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 7eeff55b22 Translate Inuktitut database migration controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 59bf39f63a Translate Inuktitut lockouts and scheduled jobs. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 1b5d441401 Translate Inuktitut support and accessibility settings. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 056c8da02c Translate Inuktitut storage repair and compaction. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 63639a62ca Translate Inuktitut diagnostics and attachment actions. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 611ee73f2a Translate Inuktitut recovery and support reports. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 84d184e989 Translate Inuktitut maps and reports. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 12c733ea5e Translate Inuktitut dependencies and locations. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 0be628e03c Translate Inuktitut global search instructions. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 2a504d434b Translate Inuktitut global search terms. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 32f3baf9fa Translate Inuktitut domains and card views. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 59e3d0d811 Translate Inuktitut roles and weekdays. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 37a50339f2 Translate Inuktitut reminders and display settings. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu b37e66dd97 Translate Inuktitut rule actions and web metadata. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 5521ffab77 Translate Inuktitut automation actions. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 3e21b57f26 Translate Inuktitut scheduled automation rules. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 015d25c55b Translate Inuktitut automation workflows. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 5742233abf Translate Inuktitut card dates and subtasks. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu eda9afc900 Translate Inuktitut diagnostics and organizations. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu aa3ad209ba Translate Inuktitut limits and webhooks. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 2be04a0a58 Translate Inuktitut tracking and branding controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 949a084f31 Translate Inuktitut search and keyboard controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 268a73eb42 Translate Inuktitut list and selection controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu d375cdcef8 Translate Inuktitut Trello import controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu c7f10c97f3 Translate Inuktitut filters and import guidance. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 00df302d6a Translate Inuktitut exports and filters. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 41e50dfd53 Translate Inuktitut email and validation messages. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 4f744183ac Translate Inuktitut roles and custom fields. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 13e4dd5f5b Translate Inuktitut board display controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu a345384260 Translate Inuktitut member and appearance settings. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 8db107ed74 Translate Inuktitut voting and import dialogs. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu e8bda15cf5 Translate Inuktitut board and card views. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu a9a8b89a9a Translate Inuktitut archives and board summaries. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu ce75d9bfb5 Translate Inuktitut workspace controls. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu a2888fb4ec Translate Inuktitut movement and workspaces. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 4551136914 Translate Inuktitut activity history. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 0caf1e74bf Complete the Icelandic translation. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 5def671e1d Translate Icelandic repository and problem reports. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu d2ce24dd3d Translate Icelandic migration resource monitoring. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu 6cd6c63218 Translate Icelandic migration monitoring. 2026-08-26 19:19:39 +03:00
Lauri Ojansivu ad73f06851 Translate Icelandic board repair migrations. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu a41fce9fa6 Translate Icelandic migration controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu dc5d60003e Translate Icelandic scheduled backups and cloud storage. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu dfe94a07b7 Translate Icelandic security and backup controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu a9085a6a10 Translate Icelandic migration and storage settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu de5291bad5 Translate Icelandic account lockout and scheduled jobs. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu f26a88e900 Translate Icelandic upload and support settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 3fb48bbc5f Translate Icelandic attachment storage maintenance. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 5c8d8768ba Translate Icelandic organization and storage controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 3ae21f4653 Translate Icelandic API and recovery reports. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu ca3307bdde Translate Icelandic maps and reports. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu a6adbaa2c9 Translate Icelandic dependencies and backgrounds. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu eddb2c10b7 Translate Icelandic global search guidance. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 8ee8d95bdd Translate Icelandic search operators. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 25f791cce9 Translate Icelandic tasks and shared views. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu f8c732eefc Translate Icelandic roles and editor preferences. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 6a3c21014e Translate Icelandic board activity and layout controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 2dc027780f Translate Icelandic rule and layout settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 1cff169fc2 Translate Icelandic automation actions. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu a5c2b680fb Translate Icelandic workflow scheduling. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 1cde45bdb4 Translate Icelandic automation rules. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 6cd1ea5b74 Translate Icelandic card lifecycle settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 0039c85f20 Translate Icelandic diagnostics and tenancy settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu e2940573e1 Translate Icelandic attachment and webhook settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu b319489e54 Translate Icelandic tracking and branding controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 0cc0846c89 Translate Icelandic privacy and shortcut controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu e3d425b2b8 Translate Icelandic board and list controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu bd67a5dae0 Translate Icelandic Trello import controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 2a67e8279d Translate Icelandic filters and board imports. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu d7f77ac228 Translate Icelandic exports and filters. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu d2810f1bae Translate Icelandic email and validation messages. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu a40eac1126 Translate Icelandic roles and custom fields. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu b170c6826f Translate Icelandic board display controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 245b8e9360 Translate Icelandic member and appearance settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 61e07cea50 Translate Icelandic voting and import dialogs. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 57e90834df Translate Icelandic board and card views. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu ded2ab01e4 Translate Icelandic archives and board summaries. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 9a913333fe Translate Icelandic workspace controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 9f5bd7f4dc Translate Icelandic movement and workspaces. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 038f0418fe Translate Icelandic activity history. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu eec23e7704 Complete the Haitian Creole translation. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu b116b7c476 Translate Haitian Creole repository and problem reporting. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 67280e8ee0 Translate Haitian Creole migration resource controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu a0dc583465 Translate Haitian Creole migration monitoring. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 634599f308 Translate Haitian Creole board repair migrations. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu c6a4a8838e Translate Haitian Creole migration administration. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 62ab31af5b Translate Haitian Creole cloud backup settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 300d797da9 Translate Haitian Creole security and backup settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu f62d46c8c5 Translate Haitian Creole migration and storage controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu e184d53fee Translate Haitian Creole account and scheduled operations. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 8fb6be0479 Translate Haitian Creole board and support settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 4442117d14 Translate Haitian Creole attachment storage maintenance. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu d37802cc54 Translate Haitian Creole teams and attachment actions. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 3ed357d9e9 Translate Haitian Creole recovery and support reports. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 514f5e230b Translate Haitian Creole locations and reports. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu afe9d94d5a Translate Haitian Creole dependencies and locations. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 14f9b3e02b Translate Haitian Creole search documentation. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 71dc3604a6 Translate Haitian Creole search operators. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 0a9d5ae8b2 Translate Haitian Creole shared templates and card views. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu adcc14c895 Translate Haitian Creole roles and notification settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 7a1e2ce767 Translate Haitian Creole account and reminder controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 4b6c7a8837 Translate Haitian Creole rule and branding settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 35a8043f4e Translate Haitian Creole automation actions. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 0d72bb324d Translate Haitian Creole scheduled automation controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu fbd19cab17 Translate Haitian Creole rule workflow controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 5955cc9eba Translate Haitian Creole card and subtask settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 8feb212413 Translate Haitian Creole diagnostics and tenancy settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 1cda44e10e Translate Haitian Creole transfer and server settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 4afbb4ef09 Translate Haitian Creole tracking and upload controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 496d595b99 Translate Haitian Creole member and navigation controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu eaf071b314 Translate Haitian Creole list and selection controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 2a43c540ed Translate Haitian Creole Trello import controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu a2869928a8 Translate Haitian Creole filters and board imports. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 0c59e1072c Translate Haitian Creole exports and filters. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu ecc9d1c5de Translate Haitian Creole email and validation messages. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu ecc355a6b8 Translate Haitian Creole roles and custom fields. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu 68a8c70f4b Translate Haitian Creole board display controls. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu f8c698f81c Translate Haitian Creole member and appearance settings. 2026-08-26 19:19:38 +03:00
Lauri Ojansivu d05782ac52 Translate Haitian Creole voting and import dialogs.
Translate 50 Haitian Creole card-member, voting, Planning Poker, dependency, organization, team, background, account, domain, mapping, import, and export strings. Preserve product terminology, update the regression to 1,867 remaining placeholders, and move the TODO Later checkpoint to userPopup-title.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu 43fe1df7a1 Translate Haitian Creole board and card views.
Translate 50 Haitian Creole public-board, display-mode, zoom, calendar, archive-safeguard, due-date, attachment, custom-field, label, and member strings. Preserve workspace, format, and HTML tokens, update the regression to 1,917 remaining placeholders, and move the TODO Later checkpoint to card-members-title.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu f622e3cb69 Translate Haitian Creole archives and board summaries.
Translate 50 Haitian Creole administration, public-board, archive, template, attachment, background, All Boards, member-summary, and privacy strings. Preserve count, size, format, and HTML tokens, update the regression to 1,967 remaining placeholders, and move the TODO Later checkpoint to board-public-info.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu f968fe6a0f Translate Haitian Creole workspace controls.
Translate 50 Haitian Creole workspace selection, Home-board, date activity, template, list sizing, keyboard shortcut, swimlane, checklist, cover, label, and member strings. Update the regression to 2,017 remaining placeholders and move the TODO Later checkpoint to added.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu 87a51bee46 Translate Haitian Creole movement and workspaces.
Translate 50 Haitian Creole card movement, activity summary, checklist activity, date, and workspace-management strings. Preserve all format and entity tokens, update the regression to 2,067 remaining placeholders, and move the TODO Later checkpoint to workspaceActionsPopup-title.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu 7f7ed7479b Translate Haitian Creole activity history.
Translate 50 Haitian Creole activity, organization restriction, comment, checklist, label, and archive strings. Add locale-wide token and HTML-tag regression coverage, record 2,117 remaining placeholders, and move the TODO Later checkpoint to act-moveCard.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu 4f0c641918 Complete the Hausa translation.
Translate the final 17 Hausa network-event, filesystem-integrity, scoped import and export, and numeric-search strings. Preserve IP, format, and search tokens, prove Hausa has zero English placeholders, and advance the TODO Later checkpoint to Haitian Creole.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu 0fc199314e Translate Hausa repository and problem reports.
Translate 50 Hausa repository, authentication, Admin Problems, card repair, CPU status, and event-report strings. Preserve OTP and repair counters, update the regression to 17 remaining Hausa placeholders, and move the TODO Later checkpoint to event-detail.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu e7687068ed Translate Hausa migration resource monitoring.
Translate 50 Hausa migration batching, CPU throttling, monitoring, pagination, schedule, storage-distribution, and attachment-statistic strings. Preserve units and storage terms, update the regression to 67 remaining Hausa placeholders, and move the TODO Later checkpoint to current-step.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu 74cff40b02 Translate Hausa migration monitoring.
Translate 50 Hausa repair-step, board-conversion, resource-monitoring, schedule, attachment-storage metric, and job-queue strings. Preserve CPU, ID, URL, and GridFS terms, update the regression to 117 remaining Hausa placeholders, and move the TODO Later checkpoint to memory-usage.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu 610eac4d68 Translate Hausa board repair migrations.
Translate 50 Hausa board integrity, duplicate-list, lost-card, archive restoration, URL repair, migration confirmation, progress, and step strings. Preserve schema field and URL terms, update the regression to 167 remaining Hausa placeholders, and move the TODO Later checkpoint to step-fix-attachment-urls.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu d84f7b91d1 Translate Hausa migration and S3 controls.
Translate 50 Hausa GridFS, S3, migration, scheduled board operation, attachment monitoring, and writable-path strings. Preserve storage and protocol terms, update the regression to 217 remaining Hausa placeholders, and move the TODO Later checkpoint to card-show-lists-on-minicard.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu 5037a8f8fd Translate Hausa backup and cloud settings.
Translate 50 Hausa backup scheduling, restoration, GCS, S3, Azure, cloud credential, provider path, and connection-test strings. Preserve console paths, JSON fields, and provider names, update the regression to 267 remaining Hausa placeholders, and move the TODO Later checkpoint to attachment-move-storage-gcs.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu bda6715750 Translate Hausa security and backup controls.
Translate 50 Hausa Sandstorm cleanup, card loading, secure rendering, import and export privacy, activity, notification, watch, and backup strings. Preserve HTML, environment, path, and product tokens, update the regression to 317 remaining Hausa placeholders, and move the TODO Later checkpoint to backup-schedule.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu f8f136a1d6 Translate Hausa migration and storage settings.
Translate 50 Hausa scheduled-job, migration-diagnostic, filesystem, S3, Azure, database-migration, and Sandstorm strings. Preserve database URLs, environment variables, and __db__, update the regression to 367 remaining Hausa placeholders, and move the TODO Later checkpoint to sandstorm-storage-item.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu 148ca717b5 Translate Hausa lockout and scheduled operations.
Translate 50 Hausa accessibility, brute-force lockout, user status, attachment path, and scheduled board operation strings. Update the regression to 417 remaining Hausa placeholders and move the TODO Later checkpoint to cron-job-delete-confirm.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu c2913a762e Translate Hausa board and accessibility settings.
Translate 50 Hausa board status, upload, workspace, custom translation, checklist display, support, and accessibility strings. Preserve workspace and format tokens, update the regression to 467 remaining Hausa placeholders, and move the TODO Later checkpoint to accessibility-info-not-added-yet.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu f11e8e2f44 Translate Hausa attachment storage maintenance.
Translate 50 Hausa bulk attachment movement, storage repair, file statistics, and MongoDB compaction strings. Preserve storage and database terminology, update the regression to 517 remaining Hausa placeholders, and move the TODO Later checkpoint to board-status.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu 18da86dccc Translate Hausa organization and storage controls.
Translate 50 Hausa team, organization, Node memory, legal notice, checklist action, and attachment-storage strings. Preserve Node, GridFS, and S3 terminology, update the regression to 567 remaining Hausa placeholders, and move the TODO Later checkpoint to move-all-attachments-to-gridfs.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu 2ceb1d0d00 Translate Hausa recovery and request reports.
Translate 50 Hausa office and API reporting, recovery, wait indicator, deletion safeguard, ticket, request, and card-detail strings. Preserve API configuration and product names, update the regression to 617 remaining Hausa placeholders, and move the TODO Later checkpoint to add-teams.\n\nThanks to xet7 !
2026-08-26 19:19:38 +03:00
Lauri Ojansivu 0e8b877be2 Translate Hausa reports and map controls.
Translate 50 Hausa map detection, server troubleshooting, sorting, custom-field template, reporting, impersonation, recovery, and office-login strings. Preserve command and format tokens, update the regression to 667 remaining Hausa placeholders, and move the TODO Later checkpoint to office-last-seen.\n\nThanks to xet7 !
2026-08-26 19:19:37 +03:00
Lauri Ojansivu 6a5292c7de Translate Hausa card dependency controls.
Translate 50 Hausa sorting, completion, sticker, card dependency, board background, and location strings. Preserve import and size placeholders, update the regression to 717 remaining Hausa placeholders, and move the TODO Later checkpoint to location-latitude.\n\nThanks to xet7 !
2026-08-26 19:19:37 +03:00
Lauri Ojansivu 182c527f81 Translate Hausa search instructions.
Translate 50 Hausa search validation, paging, operator reference, status, sorting, limit, and query-composition strings. Preserve all format, search, and metavariable tokens, update the regression to 767 remaining Hausa placeholders, and move the TODO Later checkpoint to number.\n\nThanks to xet7 !
2026-08-26 19:19:37 +03:00
Lauri Ojansivu 37ec889609 Translate Hausa search grammar.
Translate 50 Hausa missing-result messages and global-search operators and predicates. Preserve percent and range placeholders, update the regression to 817 remaining Hausa placeholders, and move the TODO Later checkpoint to predicate-member.\n\nThanks to xet7 !
2026-08-26 19:19:37 +03:00
Lauri Ojansivu 117a6e15ba Translate Hausa shared views and searches.
Translate 50 Hausa linked-card safeguards, shared-template and domain scopes, personal and due-card views, global search, and missing-item messages. Preserve percent tokens, update the regression to 867 remaining Hausa placeholders, and move the TODO Later checkpoint to user-username-not-found.\n\nThanks to xet7 !
2026-08-26 19:19:37 +03:00
Lauri Ojansivu 0d9412c81e Translate Hausa editor and role controls.
Translate 50 Hausa multi-card editing, organization and user dialogs, notification filters, board-role permissions, weekday, status, and linked-card strings. Update the regression to 917 remaining Hausa placeholders and move the TODO Later checkpoint to delete-linked-cards-before-this-list.\n\nThanks to xet7 !
2026-08-26 19:19:37 +03:00
Lauri Ojansivu 6b0143ed28 Translate Hausa date and account controls.
Translate 50 Hausa member, custom HTML, authentication, duplication, destructive-action, date reminder, selection placement, resizing, and editor strings. Preserve HTML, percent, and underscore tokens, update the regression to 967 remaining Hausa placeholders, and move the TODO Later checkpoint to open-many-cards-at-once.\n\nThanks to xet7 !
2026-08-26 19:19:37 +03:00
Lauri Ojansivu c3bffa1b49 Translate Hausa rule and branding settings.
Translate 50 Hausa concrete rule actions, date fields, authentication labels, and custom HTML, manifest, asset-link, and layout settings. Update the regression to 1,017 remaining Hausa placeholders and move the TODO Later checkpoint to board-member-list.\n\nThanks to xet7 !
2026-08-26 19:19:37 +03:00
Lauri Ojansivu 85d2fb6057 Translate Hausa rule actions.
Translate 50 Hausa rule fragments and concrete actions for moving and archiving cards, labels, members, checklists, and email. Update the regression to 1,067 remaining Hausa placeholders and move the TODO Later checkpoint to r-d-send-email-subject.\n\nThanks to xet7 !
2026-08-26 19:19:37 +03:00
Lauri Ojansivu 3a182cfbe2 Translate Hausa scheduled automation controls.
Translate 50 Hausa visual-workflow import, scheduled and button trigger, due-date timing, list sorting, relative-date, and rule grammar strings. Preserve __count__ and product names, update the regression to 1,117 remaining Hausa placeholders, and move the TODO Later checkpoint to r-the-board.\n\nThanks to xet7 !
2026-08-26 19:19:37 +03:00
Lauri Ojansivu 0d5fbe4600 Translate Hausa activity and automation rules.
Translate 50 Hausa label and custom-field activity strings, visual rule controls, card event triggers, and JSON, CSV, and Trello Butler transfer guidance. Preserve every percent and underscore placeholder, update the regression to 1,167 remaining Hausa placeholders, and move the TODO Later checkpoint to r-import-workflow.\n\nThanks to xet7 !
2026-08-26 19:19:37 +03:00
Lauri Ojansivu b4657140a1 Translate Hausa card and subtask settings.
Translate 50 Hausa card date and color labels, destructive-action confirmations, subtask and card settings, minicard display choices, and parent-card paths. Preserve the __board__ and percent-format tokens, update the regression to 1,217 remaining Hausa placeholders, and move the TODO Later checkpoint to activity-removed-label.\n\nThanks to xet7 !
2026-08-26 19:19:37 +03:00
Lauri Ojansivu aed4e49f77 Translate Hausa system and organization labels.
Translate 50 Hausa database and operating-system diagnostics, time units, custom-field display settings, visibility labels, and organization administration strings. Preserve product names and environment-variable tokens exactly, update the progress regression to 1,267 remaining Hausa placeholders, and move the TODO Later checkpoint to card-received.\n\nThanks to xet7 !
2026-08-26 19:19:37 +03:00
github-actions[bot] 831735a902 Bump versions for v11.14 2026-08-26 08:55:57 +00:00
Lauri Ojansivu 686d41367d Prepare v11.14 release 2026-08-26 11:54:53 +03:00
Lauri Ojansivu 2b219a024e Document the browser-verified date editor centering.
Point the Upcoming entry at the completed geometry fix and record that positioning uses the date editor's rendered shell width.\n\nThanks to Alishara and xet7 !
2026-08-26 11:52:11 +03:00
Lauri Ojansivu 548add4fee Center date editors by their rendered width.
Use the 400 px shell declared by popup.css when calculating the date editor offset, and pin that shared geometry in the unit regression. The focused Chromium test now measures an exact viewport center instead of the former 10 px drift.\n\nThanks to Alishara and xet7 !\n\nFixes #6636,
2026-08-26 11:51:12 +03:00
Lauri Ojansivu 47ac125688 Document the card layout fix.
Thanks to Alishara and xet7 !
2026-08-26 11:43:36 +03:00
Lauri Ojansivu 7f9f3c66e4 Fix card gutters and date editor layout.
Thanks to Alishara and xet7 !

Fixes #6636,
2026-08-26 11:42:04 +03:00
Lauri Ojansivu ec42c37bde Translate Hausa transfer and webhook strings.
Thanks to xet7 !
2026-08-26 11:37:43 +03:00
Lauri Ojansivu 76645fc5e1 Translate Hausa tracking and branding strings.
Thanks to xet7 !
2026-08-26 11:36:34 +03:00
Lauri Ojansivu fe84a88735 Translate Hausa notification and shortcut strings.
Thanks to xet7 !
2026-08-26 11:35:45 +03:00
Lauri Ojansivu 39c39651ce Translate Hausa membership and selection strings.
Thanks to xet7 !
2026-08-26 11:34:44 +03:00
Lauri Ojansivu 0853f5a1f2 Translate Hausa Trello import strings.
Thanks to xet7 !
2026-08-26 11:33:46 +03:00
Lauri Ojansivu 8586c9e7e7 Translate Hausa filter and import strings.
Thanks to xet7 !
2026-08-26 11:32:47 +03:00
Lauri Ojansivu 010613feff Translate Hausa export and filter strings.
Thanks to xet7 !
2026-08-26 11:31:21 +03:00
Lauri Ojansivu f0ae2c0bd1 Translate Hausa email and error strings.
Thanks to xet7 !
2026-08-26 11:30:21 +03:00
Lauri Ojansivu 44d7410b1d Translate Hausa roles and custom-field strings.
Thanks to xet7 !
2026-08-26 11:29:17 +03:00
Lauri Ojansivu e5d9f45d43 Translate Hausa navigation and color strings.
Thanks to xet7 !
2026-08-26 11:28:18 +03:00
Lauri Ojansivu 69315a9eb3 Translate Hausa member and theme strings.
Thanks to xet7 !
2026-08-26 11:27:18 +03:00
Lauri Ojansivu 04e2e17b01 Translate Hausa voting and import strings.
Thanks to xet7 !
2026-08-26 11:26:27 +03:00
Lauri Ojansivu 12b7ddafe0 Translate Hausa board views and card strings.
Thanks to xet7 !
2026-08-26 11:25:37 +03:00
Lauri Ojansivu 7362e9e9cf Translate Hausa administration and archive strings.
Thanks to xet7 !
2026-08-26 11:24:34 +03:00
Lauri Ojansivu a559cc13ce Translate Hausa board layout strings.
Thanks to xet7 !
2026-08-26 11:23:34 +03:00
Lauri Ojansivu da84ddcc4f Translate Hausa activity and workspace strings.
Thanks to xet7 !
2026-08-26 11:22:33 +03:00
Lauri Ojansivu 2fb069cb60 Start the Hausa translation.
Thanks to xet7 !
2026-08-26 11:21:40 +03:00
Lauri Ojansivu 1fc59614a7 Complete the Guarani translation.
Thanks to xet7 !
2026-08-26 11:20:04 +03:00
Lauri Ojansivu d9e2eba0fc Complete four warning-only translations.
Thanks to xet7 !
2026-08-26 11:19:12 +03:00
Lauri Ojansivu 89a0e3a3f6 Complete the Manx translation.
Thanks to xet7 !
2026-08-26 11:18:02 +03:00
Lauri Ojansivu 265c1cc5e1 Translate Manx operations and repository strings.
Thanks to xet7 !
2026-08-26 11:16:35 +03:00
Lauri Ojansivu 3743fb013a Translate Manx monitoring strings.
Thanks to xet7 !
2026-08-26 11:15:43 +03:00
Lauri Ojansivu a38824d953 Translate Manx board migration strings.
Thanks to xet7 !
2026-08-26 11:14:51 +03:00
Lauri Ojansivu 39eb38dd29 Translate Manx migration repair strings.
Thanks to xet7 !
2026-08-26 11:13:47 +03:00
Lauri Ojansivu 1a0574af0a Translate Manx cloud storage strings.
Thanks to xet7 !
2026-08-26 11:12:47 +03:00
Lauri Ojansivu 00db401454 Translate Manx backup scheduling strings.
Thanks to xet7 !
2026-08-26 11:11:42 +03:00
Lauri Ojansivu e49f4c2783 Translate Manx privacy and backup strings.
Thanks to xet7 !
2026-08-26 11:11:00 +03:00
Lauri Ojansivu 14704cb220 Translate Manx migration and storage strings.
Thanks to xet7 !
2026-08-26 11:08:56 +03:00
Lauri Ojansivu ed57f4827f Translate Manx access and scheduling strings.
Thanks to xet7 !
2026-08-26 11:07:45 +03:00
Lauri Ojansivu 571665dc36 Translate Manx board and support strings.
Thanks to xet7 !
2026-08-26 11:06:48 +03:00
Lauri Ojansivu e135ff5c7c Translate Manx attachment maintenance strings.
Thanks to xet7 !
2026-08-26 11:04:55 +03:00
Lauri Ojansivu dddb9833ac Translate Manx organization and storage strings.
Thanks to xet7 !
2026-08-26 11:03:50 +03:00
Lauri Ojansivu 8a057a4dc6 Translate Manx recovery and ticket strings.
Thanks to xet7 !
2026-08-26 11:02:53 +03:00
Lauri Ojansivu 019ac833b5 Translate Manx map and report strings.
Thanks to xet7 !
2026-08-26 11:01:44 +03:00
Lauri Ojansivu 9f1a75e5de Translate Manx dependency and location strings.
Thanks to xet7 !
2026-08-26 11:00:51 +03:00
Lauri Ojansivu 2d26ce22da Translate Manx search instruction strings.
Thanks to xet7 !
2026-08-26 10:59:47 +03:00
Lauri Ojansivu f95194c22e Translate Manx search operator strings.
Thanks to xet7 !
2026-08-26 10:58:23 +03:00
Lauri Ojansivu eca638dc49 Translate Manx templates and card views.
Thanks to xet7 !
2026-08-26 10:57:14 +03:00
Lauri Ojansivu b509fb6cc8 Translate Manx roles and editor strings.
Thanks to xet7 !
2026-08-26 10:56:00 +03:00
Lauri Ojansivu ed3f2722ae Translate Manx due-time and layout strings.
Thanks to xet7 !
2026-08-26 10:55:02 +03:00
Lauri Ojansivu cc28b36ff7 Translate Manx rule action and manifest strings.
Thanks to xet7 !
2026-08-26 10:54:02 +03:00
Lauri Ojansivu 2269a09f83 Translate Manx rule grammar strings.
Thanks to xet7 !
2026-08-26 10:53:11 +03:00
Lauri Ojansivu cc86b936f2 Translate Manx scheduled rule strings.
Thanks to xet7 !
2026-08-26 10:52:20 +03:00
Lauri Ojansivu 284e872aaa Translate Manx visual-rule workflow strings.
Thanks to xet7 !
2026-08-26 10:51:34 +03:00
Lauri Ojansivu 7a4913bde9 Translate Manx card date and subtask strings.
Thanks to xet7 !
2026-08-26 10:50:29 +03:00
Lauri Ojansivu fb7288f818 Translate Manx metrics and multitenancy strings.
Thanks to xet7 !
2026-08-26 10:49:37 +03:00
Lauri Ojansivu 589bec6675 Translate Manx transfer and webhook strings.
Thanks to xet7 !
2026-08-26 10:48:37 +03:00
Lauri Ojansivu 1efecbc23b Translate Manx tracking and upload strings.
Thanks to xet7 !
2026-08-26 10:47:34 +03:00
Lauri Ojansivu 5c5e281655 Translate Manx privacy and shortcut strings.
Thanks to xet7 !
2026-08-26 10:46:40 +03:00
Lauri Ojansivu 759728855d Translate Manx archive and board-role strings.
Thanks to xet7 !
2026-08-26 10:45:39 +03:00
Lauri Ojansivu 7f8d60a9f9 Translate Manx Trello import and member mapping strings.
Thanks to xet7 !
2026-08-26 10:44:44 +03:00
Lauri Ojansivu a61703ad45 Translate Manx advanced filter and import strings.
Thanks to xet7 !
2026-08-26 10:43:43 +03:00
Lauri Ojansivu 4a6c7bd1a5 Translate Manx export and filter strings.
Thanks to xet7 !
2026-08-26 10:41:51 +03:00
Lauri Ojansivu da640037e7 Translate Manx email and import-error strings.
Thanks to xet7 !
2026-08-26 10:40:50 +03:00
Lauri Ojansivu 8986795898 Translate Manx role and custom-field strings.
Thanks to xet7 !
2026-08-26 10:39:40 +03:00
Lauri Ojansivu 4afdbce1ec Translate Manx navigation and color strings.
Thanks to xet7 !
2026-08-26 10:38:46 +03:00
Lauri Ojansivu b443ef6a94 Translate Manx member and appearance strings.
Thanks to xet7 !
2026-08-26 10:37:44 +03:00
Lauri Ojansivu e3ab6e8de5 Translate Manx voting and import strings.
Thanks to xet7 !
2026-08-26 10:36:48 +03:00
Lauri Ojansivu f81d4f8473 Translate Manx board view and archive strings.
Thanks to xet7 !
2026-08-26 10:36:00 +03:00
Lauri Ojansivu 1dd8f9ec8f Translate Manx administration and archive strings.
Thanks to xet7 !
2026-08-26 10:35:06 +03:00
Lauri Ojansivu dc3bea39b5 Translate Manx board selection and sizing strings.
Thanks to xet7 !
2026-08-26 10:34:12 +03:00
Lauri Ojansivu 0a9338575d Translate Manx activity and workspace strings.
Thanks to xet7 !
2026-08-26 10:33:18 +03:00
Lauri Ojansivu debfcedb0f Start completing Manx translations.
Thanks to xet7 !
2026-08-26 10:32:10 +03:00
Lauri Ojansivu 61c617ebc6 Complete actionable Guarani translations.
Thanks to xet7 !
2026-08-26 10:30:21 +03:00
Lauri Ojansivu b558071cc3 Translate Guarani repository and problem-report strings.
Thanks to xet7 !
2026-08-26 10:29:11 +03:00
Lauri Ojansivu fa6afabbb3 Translate Guarani attachment migration strings.
Thanks to xet7 !
2026-08-26 10:28:00 +03:00
Lauri Ojansivu 7a1ea46e59 Translate Guarani migration monitoring strings.
Thanks to xet7 !
2026-08-26 10:27:01 +03:00
Lauri Ojansivu 1ab5651997 Translate Guarani board repair strings.
Thanks to xet7 !
2026-08-26 10:25:39 +03:00
Lauri Ojansivu fd54c7e1a1 Translate Guarani migration control strings.
Thanks to xet7 !
2026-08-26 10:23:48 +03:00
Lauri Ojansivu e64dc9da4e Translate Guarani backup and cloud-storage strings.
Thanks to xet7 !
2026-08-26 10:22:33 +03:00
Lauri Ojansivu 50efb9c3a2 Translate Guarani privacy and backup strings.
Thanks to xet7 !
2026-08-26 10:20:45 +03:00
Lauri Ojansivu 5cbfa33a58 Translate Guarani migration and storage strings.
Thanks to xet7 !
2026-08-26 10:17:52 +03:00
Lauri Ojansivu f60a8745f3 Translate Guarani lockout and scheduled-operation strings.
Thanks to xet7 !
2026-08-26 10:16:50 +03:00
Lauri Ojansivu 3246622461 Translate Guarani board and support strings.
Thanks to xet7 !
2026-08-26 10:15:12 +03:00
Lauri Ojansivu 4b7e1fba73 Translate Guarani attachment maintenance strings.
Thanks to xet7 !
2026-08-26 10:13:49 +03:00
Lauri Ojansivu 3361f69c2f Translate Guarani organization and storage strings.
Thanks to xet7 !
2026-08-26 10:12:05 +03:00
Lauri Ojansivu 49881a61b5 Translate Guarani recovery and ticket strings.
Thanks to xet7 !
2026-08-26 10:10:39 +03:00
Lauri Ojansivu 69f60bfbad Translate Guarani map and report strings.
Thanks to xet7 !
2026-08-26 10:09:10 +03:00
Lauri Ojansivu 08256d20e6 Translate Guarani dependency and location strings.
Thanks to xet7 !
2026-08-26 10:08:03 +03:00
Lauri Ojansivu 53e5ec7d83 Translate Guarani search instruction strings.
Thanks to xet7 !
2026-08-26 10:06:50 +03:00
Lauri Ojansivu d9cd495342 Translate Guarani search operator strings.
Thanks to xet7 !
2026-08-26 10:05:30 +03:00
Lauri Ojansivu ff25dbe767 Translate Guarani templates and card views.
Thanks to xet7 !
2026-08-26 10:04:21 +03:00
Lauri Ojansivu 802da7874b Translate Guarani roles and editor strings.
Thanks to xet7 !
2026-08-26 10:02:30 +03:00
Lauri Ojansivu 5ff745be3d Translate Guarani layout and due-time strings.
Thanks to xet7 !
2026-08-26 10:00:48 +03:00
Lauri Ojansivu f9632646f7 Translate Guarani rule actions and manifest strings.
Thanks to xet7 !
2026-08-26 09:57:47 +03:00
Lauri Ojansivu fda2bdc5cd Translate Guarani rule grammar strings.
Thanks to xet7 !
2026-08-26 09:56:32 +03:00
Lauri Ojansivu b9c24de24f Translate Guarani scheduled rule strings.
Thanks to xet7 !
2026-08-26 09:55:03 +03:00
Lauri Ojansivu 7c2d64f19c Translate Guarani rule automation strings.
Thanks to xet7 !
2026-08-26 09:52:56 +03:00
Lauri Ojansivu 6e7ec8cdd4 Translate Guarani card settings and deletion strings.
Thanks to xet7 !
2026-08-26 09:50:28 +03:00
Lauri Ojansivu 10d481cd24 Translate Guarani database and organization strings.
Thanks to xet7 !
2026-08-26 09:48:03 +03:00
Lauri Ojansivu fe8d5cfc51 Translate Guarani transfer and webhook strings.
Thanks to xet7 !
2026-08-26 09:45:08 +03:00
Lauri Ojansivu f7fb308728 Translate Guarani tracking and upload strings.
Thanks to xet7 !
2026-08-26 09:42:46 +03:00
Lauri Ojansivu 68736db6d1 Translate Guarani privacy and shortcut strings.
Thanks to xet7 !
2026-08-26 09:41:16 +03:00
Lauri Ojansivu 7f8e459a82 Translate Guarani archive and role strings.
Thanks to xet7 !
2026-08-26 09:40:00 +03:00
Lauri Ojansivu 9d7f5a7260 Translate Guarani Trello import and member strings.
Thanks to xet7 !
2026-08-26 09:38:54 +03:00
Lauri Ojansivu 78029204fc Translate Guarani filtering and import strings.
Thanks to xet7 !
2026-08-26 09:37:37 +03:00
Lauri Ojansivu 820de93f56 Translate Guarani export and filtering strings.
Thanks to xet7 !
2026-08-26 09:36:17 +03:00
Lauri Ojansivu 8eb68d7107 Translate Guarani email and error strings.
Thanks to xet7 !
2026-08-26 09:35:18 +03:00
Lauri Ojansivu 84e3537087 Translate Guarani permission and custom field strings.
Thanks to xet7 !
2026-08-26 09:34:03 +03:00
Lauri Ojansivu 755312afb1 Translate Guarani card aging and color strings.
Thanks to xet7 !
2026-08-26 09:33:03 +03:00
Lauri Ojansivu 1de22033dc Translate Guarani member and theme strings.
Thanks to xet7 !
2026-08-26 09:32:09 +03:00
Lauri Ojansivu 6142cc5468 Translate Guarani voting and import strings.
Thanks to xet7 !
2026-08-26 09:31:12 +03:00
Lauri Ojansivu e784be2bdb Translate Guarani board view and card strings.
Thanks to xet7 !
2026-08-26 09:30:07 +03:00
Lauri Ojansivu 0c761bf7f2 Translate Guarani administration and archive strings.
Thanks to xet7 !
2026-08-26 09:29:07 +03:00
Lauri Ojansivu 5721bb69f8 Translate Guarani board layout controls.
Thanks to xet7 !
2026-08-26 09:27:59 +03:00
Lauri Ojansivu ec46f04c84 Translate Guarani movement and workspace strings.
Thanks to xet7 !
2026-08-26 09:26:26 +03:00
Lauri Ojansivu 9788cd4f1e Translate Guarani activity strings.
Thanks to xet7 !
2026-08-26 09:25:11 +03:00
Lauri Ojansivu b1a9d0cc17 Complete Zulu Sandstorm warning translation.
Thanks to xet7 !
2026-08-26 09:23:21 +03:00
Lauri Ojansivu dc0063837a Translate Tamazight Chinese and Zulu Sandstorm warnings.
Thanks to xet7 !
2026-08-26 09:22:30 +03:00
Lauri Ojansivu c959404610 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-26 09:21:36 +03:00
Lauri Ojansivu 0db1a2a3e0 Translate Vietnamese through Cantonese Sandstorm warnings.
Thanks to xet7 !
2026-08-26 09:21:36 +03:00
Lauri Ojansivu a1668d2fbd Translate Ukrainian through Vietnamese Sandstorm warnings.
Thanks to xet7 !
2026-08-26 09:21:36 +03:00
Lauri Ojansivu 61759ba9f7 Translate Serbian through Uyghur Sandstorm warnings.
Thanks to xet7 !
2026-08-26 09:21:36 +03:00
Lauri Ojansivu 55b5c7cb22 Translate Portuguese through Slovenian Sandstorm warnings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 1b696a0349 Translate Dutch through Portuguese Sandstorm warnings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 480d9d7eb6 Translate Khmer through Norwegian Sandstorm warnings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu e16bb58a2f Record canonical Cambodian Khmer warning.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 9adf1335ca Translate Hungarian through Khmer Sandstorm warnings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu f0b4812ba2 Translate French through Croatian Sandstorm warnings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu a858251007 Translate Spanish through French Sandstorm warnings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu f4f82d3be5 Translate Greek Esperanto and Spanish Sandstorm warnings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 165e97f0ea Translate ten additional Sandstorm member warnings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 82c845aa5f Translate another ten Sandstorm member warnings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu ac929262d0 Translate ten more Sandstorm member warnings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 8bdd1a10c6 Translate ten Sandstorm member warnings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 3bc619b858 Translate Galician and Xhosa Sandstorm warning.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 316f924bd6 Complete Scottish Gaelic translations.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 5d2ec21e4b Translate Scottish Gaelic account and problem strings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu e1dc0bf5f2 Translate Scottish Gaelic migration status strings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu b26b88dae9 Translate Scottish Gaelic migration monitoring strings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 2644ee13f6 Translate Scottish Gaelic board repair strings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu e67b14c818 Translate Scottish Gaelic migration control strings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu e1136d0f6a Translate Scottish Gaelic cloud backup strings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 8b282d2b2e Translate Scottish Gaelic security and backup strings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 2c3151e1c5 Translate Scottish Gaelic migration and storage strings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 0da7dd5045 Translate Scottish Gaelic account security strings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 572aed60f6 Translate Scottish Gaelic board and support strings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 7ddca08079 Translate Scottish Gaelic attachment storage strings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 169cabff41 Translate Scottish Gaelic team and attachment strings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 3d2e18f07b Translate Scottish Gaelic recovery and ticket strings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu e69d577595 Translate Scottish Gaelic map and report strings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 049992f5d3 Translate Scottish Gaelic dependencies and location strings.
Thanks to xet7 !
2026-08-26 09:21:35 +03:00
Lauri Ojansivu 67031f9b2a Translate Scottish Gaelic global-search instructions.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu ffc57fda6c Translate Scottish Gaelic global-search vocabulary.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 298832651c Translate Scottish Gaelic shared-template and search strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu fff2cf9c6f Translate Scottish Gaelic board roles and weekday strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu b2dff9624e Translate Scottish Gaelic due-date and layout strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 6ef37ee204 Translate Scottish Gaelic rule details and custom branding.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu bad1b9e73c Translate Scottish Gaelic rule actions and conditions.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu b2383260d6 Translate Scottish Gaelic scheduled rule triggers.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 4a645ecd07 Translate Scottish Gaelic rule workflow strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 2ab8f57e16 Translate Scottish Gaelic card date and subtask settings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 7022bf4bbd Translate Scottish Gaelic runtime and organization settings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 188bfee10d Translate Scottish Gaelic transfer and webhook settings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu ddd053ab9f Translate Scottish Gaelic tracking and branding strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu c8ce629114 Translate Scottish Gaelic notifications and shortcuts.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu e27895417e Translate Scottish Gaelic list and selection actions.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 9769c61936 Translate Scottish Gaelic Trello import and member mapping.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu f1247d6b72 Translate Scottish Gaelic filtering and import guidance.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 9c84adfc71 Translate Scottish Gaelic export and filter strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 40ed66b6eb Translate Scottish Gaelic email and error strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 8c4910a8c1 Translate Scottish Gaelic permissions and custom fields.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu ed31bc76db Translate Scottish Gaelic navigation and color strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 45bdb525b4 Translate Scottish Gaelic member and appearance dialogs.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu fbb82697ac Translate Scottish Gaelic voting and import dialogs.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu b8f717ac43 Translate Scottish Gaelic board views and archive guidance.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 26c41a141d Translate Scottish Gaelic administration and archive strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu eeade808c4 Translate Scottish Gaelic board layout controls.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 06446ec52d Translate Scottish Gaelic activity and workspace strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu d6b7b86f3d Begin the Scottish Gaelic translation.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 7f75b0e9c4 Complete the Irish translation.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 1d0ee4ef3c Translate Irish repository and repair status strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu a85e0ed91b Translate Irish migration resource and monitoring strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 33b5d352ae Translate Irish migration progress and monitoring strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 1e5a1befa8 Translate Irish board integrity migration strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 0162d9b432 Translate Irish storage migration controls.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu fb31137d4c Translate Irish backup scheduling and cloud storage strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu eaa08768cb Translate Irish security and backup settings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 9907e16645 Translate Irish migration and storage settings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu ab66b77be8 Translate Irish account protection and scheduled-job strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu d52512a271 Translate Irish board status and support strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 1813f5d4ad Translate Irish attachment storage administration strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 6da427f222 Translate Irish organization and attachment strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 9416f587d3 Translate Irish recovery and ticket strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu c947a850c9 Translate Irish map and report strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 644e647208 Translate Irish card dependency and background strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 4034b37724 Translate Irish global-search instructions.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 726fa68767 Translate Irish global-search vocabulary.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 048b3af25e Translate next shared-template and search strings into Irish.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu cc9cff0cfe Translate another 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 035042eb8d Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 350058abae Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 25aa6d29f1 Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 8ee47cfad0 Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 2a27bbf6ae Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 3f63e9ed38 Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 9b8a4b23ba Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 501102190f Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu a42455b380 Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu a45c6146f5 Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 9842ced982 Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 8b7a9570e2 Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 6a327c2e59 Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu b2964d913d Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 410ebf2ee0 Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu c589876135 Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 823c505bb9 Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 430342b692 Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 93c0a72f8f Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 8463cc8fbf Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 2242c8eee6 Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu a9b465e180 Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 71c5c2cbeb Translate next 50 Irish strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu e6eb60cbfc Begin Irish whole-file translation.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 879ae21b74 Complete Faroese whole-file translation.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 22b2d6936e Translate next 48 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 7a44438e55 Translate next 48 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu f562700d78 Translate next 48 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 51ae1495fe Translate next 48 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 95d279b22d Translate next 48 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu bcd56f26e4 Translate next 48 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 49d601be51 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 3736685c6a Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu a9c14f2ec5 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu e7e3bed710 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 49aed8d25d Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 067f77d221 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu a1b4ab6e3d Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 6b64d591d7 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 0addad8e61 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 10aad65974 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu ceed080c65 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu a14b092796 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu ecc0b2585f Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 71e039603e Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu f5724b9259 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 8888063679 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 7e90dcfbb6 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 4531586965 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:34 +03:00
Lauri Ojansivu 00d60ac6ae Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 0a201a6917 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 574e8c0ad9 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 5515eac442 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu f6eed7aeee Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 03beac4463 Translate next 49 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 69383ccec3 Translate next 50 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 2fcce1dd2c Translate next 50 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu fc56ad374a Translate next 50 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 918b1e645d Translate next 50 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 71f4f057da Translate next 50 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 7a9e8095c6 Translate next 50 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 147b1f24ff Translate next 50 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu b0b0a8925c Translate next 50 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 02eccf5c53 Translate next 50 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 7e40d9a0c3 Translate next 50 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu b8951c7649 Translate next 50 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 967c1faa94 Translate next 50 Faroese strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu c84eaa7a94 Begin Faroese whole-file translation.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu a55ff3c4d6 Complete Fijian whole-file translation.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 664348bd9f Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 14d7f6b753 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 1a0ad7c2a7 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 1a60ed4e00 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 38ff86aa79 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 75efb13910 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 9bba089a6d Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 89baf94521 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu e8e39f0e75 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu ba32373e47 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 9ff7747553 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 7c2b7b24ec Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 3b841f080c Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 19855adc05 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu d3cf1669b9 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 9620908e93 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu ebdd89d617 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 57c811f2e7 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu fd5d37a0af Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 88f3ec68a1 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 991891ef20 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu e6cc5e9df1 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 46120b3d33 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 55c66709ae Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 2b2ce6446c Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 22b9bbc88e Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 5e27a567c2 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 365a7ca810 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 75ef984950 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 7eefc88b40 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 3e634451f1 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 4637045e81 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 5b54fc268c Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu d266383217 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu f83e798d42 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu b1ee8971af Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 1c39a9724f Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu e72c9cccce Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 0b4cb81129 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu e587fa408e Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 6510a87f61 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 4101378055 Translate next 50 Fijian strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu a603636b2c Begin Fijian whole-file translation.
Translate the first 50 actionable values and add regression coverage for remaining-count, placeholder, format-token, and tag integrity.

Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu de1feef876 Complete Fulah whole-file translation.
Translate the final 17 actionable values. Bucket and Log remain unchanged because they are invariant product terms, so Fulah has no genuinely untranslated strings.

Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 501689d5a7 Translate next 49 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu ee858e69c4 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 04a3eed3b6 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu b2d34b900b Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 02d5d54c93 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 891a24f4a5 Translate next 49 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 422d32e76a Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu ca319b8524 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu fb4852dd83 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 84183e36f5 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 6ebc6ad186 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 319dc70bbc Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu ef961fe76d Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 975bdf7a9d Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 5ee5638447 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 7bdee08d85 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 7bc07ad143 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 49750469d2 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 19f8c18a08 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu cfe4aebd59 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu c4c89dea02 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu e9bae31966 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 0128d0eac9 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 382c189de9 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 36469e16ca Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 20bf975f72 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 47503009a0 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu ff3b1a0ae9 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 98c80f4e8a Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 8ff0eda788 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu fdba844a1f Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 3615b21490 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu be7bbddfba Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 31f1c632e5 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 523f5313da Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 4154475df8 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu c6faacb4c7 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu 911df0d8d5 Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:33 +03:00
Lauri Ojansivu fa6738aa5e Translate next 50 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:32 +03:00
Lauri Ojansivu 967aad8564 Translate next 100 Fulah strings.
Thanks to xet7 !
2026-08-26 09:21:32 +03:00
github-actions[bot] ff29558bf2 Bump versions for v11.13 2026-08-26 01:00:58 +00:00
Lauri Ojansivu 747dca9afe Prepare v11.13 release 2026-08-26 03:59:51 +03:00
Lauri Ojansivu 55e28de600 Repair full-suite consistency checks
Register generic Chinese with the Simplified Chinese accounts bundle, synchronize the WeKan service documentation across every FerretDB backend compose file, update the measured translation coverage count, and make source guards recognize equivalent formatting and board validation without weakening their assertions.

Thanks to xet7 !
2026-08-25 23:16:16 +03:00
Lauri Ojansivu ee55ab566c Remove fixed reports from TODO Later 2026-08-25 20:27:17 +03:00
Lauri Ojansivu ff1dd77113 Document localized notification emails 2026-08-25 20:26:27 +03:00
Lauri Ojansivu 46609d3e87 Close localized notification email report
The recipient-language bundle loading fix from commit 22f996903 already prevents server notifications from falling back to English. Add exact French translation, placeholder, load-order, send-language, fallback, and negative default-language regressions.

Thanks to Sophalso and xet7 !

Fixes #5438,
2026-08-25 20:26:01 +03:00
Lauri Ojansivu 6667876758 Document speech-scrollable board regions 2026-08-25 20:23:08 +03:00
Lauri Ojansivu fb10b6160c Make independent board regions speech-scrollable
Expose every list body and opened card as a named, keyboard-focusable region so speech-recognition and keyboard users can target the correct scrollbar. Cover the actual overflow elements and verify Page Down scrolls both regions in the live browser.

Thanks to ProdLead and xet7 !

Fixes #2499,
2026-08-25 20:22:42 +03:00
Lauri Ojansivu 4320661e97 Document Snap external MongoDB URL closure 2026-08-25 20:20:04 +03:00
Lauri Ojansivu f8c7815b26 Close Snap external MongoDB URL report
The startup fix from commit fc02a1575 already preserves a configured mongo-url instead of rebuilding the internal 27019 address. Add executable regressions for the real config reader, exact external URL and oplog preservation, and the negative empty-setting fallback.

Thanks to cc4in and xet7 !

Fixes #6543,
2026-08-25 20:19:50 +03:00
Lauri Ojansivu 854defe729 Document safe Snap database restore 2026-08-25 20:18:42 +03:00
Lauri Ojansivu d7a225eb05 Make Snap database restore safely replace data
Retain the existing --drop restore behavior that prevents duplicate-key collisions, validate that exactly one real archive was supplied, and preserve paths containing spaces as one mongorestore argument. Add command-level positive and negative regressions with a mocked Snap environment.

Thanks to ram19890 and xet7 !

Fixes #6547,
2026-08-25 20:17:49 +03:00
Lauri Ojansivu 2cade9a6ba Document Japanese label wrapping closure 2026-08-25 20:16:22 +03:00
Lauri Ojansivu e10fc10c56 Close Japanese add-card label wrapping report
The scoped CJK wrapping fix from commit 9ddbc777d already keeps Japanese submit and footer labels intact without changing global Latin wrapping. Add a live Chromium regression that selects Japanese, opens the real add-card composer, and verifies the translated Add and Link controls remain single-line.

Thanks to arifer612, skye0402 and xet7 !

Fixes #4023,
2026-08-25 20:16:05 +03:00
Lauri Ojansivu 39bcd66b20 Document legacy minicard creator default 2026-08-25 20:14:00 +03:00
Lauri Ojansivu 6af6db72db Keep card creators hidden on legacy minicards
Treat the dedicated minicard creator setting as opt-in when an older board does not contain it, matching both the schema default and the actual card rendering. Keep the separate opened-card creator choice independent and cover the legacy default plus explicit UI opt-in.

Thanks to tamasberesoebb and xet7 !

Fixes #3823,
2026-08-25 20:13:40 +03:00
Lauri Ojansivu 09c6c341e4 Document Spring OIDC endpoint resolution 2026-08-25 20:08:27 +03:00
Lauri Ojansivu aee94c2637 Resolve absolute and Spring OIDC endpoints
Use one endpoint resolver for authorization, token, Oracle token, and user-info requests so complete HTTP and HTTPS URLs remain intact and relative paths are joined without duplicate slashes. Document Spring Authorization Server's default protocol endpoints and callback configuration, with regression coverage for both valid and malformed combinations.

Thanks to Esther125 and xet7 !

Fixes #5150,
2026-08-25 20:07:43 +03:00
Lauri Ojansivu abed585bb1 Document LDAP group import prevention
Record group-object validation, compatible user schemas, focused regression coverage, documentation, and live Meteor compilation in the upcoming release notes.
2026-08-25 20:02:53 +03:00
Lauri Ojansivu 1485251958 Exclude LDAP groups from user synchronization
Recognize known Active Directory, OpenLDAP, and POSIX group object classes case-insensitively. Skip them during bulk import, reject them at account creation, preserve custom user schemas, and document user search filters.

Thanks to MiteNy and xet7 !

Fixes #4875,
2026-08-25 20:02:32 +03:00
Lauri Ojansivu 2fc7f34420 Document safe LDAP background synchronization
Record repeat profile updates, confirmed removal handling, fail-safe lookup boundaries, regression coverage, and live Meteor compilation in the upcoming release notes.
2026-08-25 19:59:37 +03:00
Lauri Ojansivu 433455e0e8 Fail safely during LDAP background presence sync
Keep repeat profile updates and confirmed removed-user disabling, but abort on missing identifier configuration or ambiguous directory matches. Share and test the exact disable and re-enable decisions and document their authority boundary.

Thanks to RowhamD, q16marvin and xet7 !

Fixes #4654,
2026-08-25 19:59:16 +03:00
Lauri Ojansivu 79e1d70af2 Document prefixed-path color popup closure
Record the Firefox navigation cause, focused coverage, production-mode subpath startup, live Chromium mutation result, and unavailable local Firefox binary.
2026-08-25 19:55:07 +03:00
Lauri Ojansivu 4c53324193 Close prefixed-path list and swimlane color report
Pin the existing Firefox submit-navigation fix for both color popups with positive and negative source coverage. Add a live browser regression that saves both colors and keeps the board under its configured URL prefix.

Thanks to RowhamD, cautiouscoyote and xet7 !

Fixes #4652,
2026-08-25 19:54:37 +03:00
Lauri Ojansivu 013896bd2a Document LDAP to OIDC profile preservation
Record the verified account-linking behavior, expanded data-preservation coverage, and live Meteor compilation in the upcoming release notes.
2026-08-25 19:48:36 +03:00
Lauri Ojansivu 7145d70474 Close LDAP to OIDC profile preservation report
Confirm the existing profile-merge fix preserves avatar and template preferences, and extend its behavioral regression to retain login sessions and organization membership across account linking.

Thanks to funakura and xet7 !

Fixes #4560,
2026-08-25 19:48:15 +03:00
Lauri Ojansivu 0d987d20e8 Document LDAP stale-password REST closure
Record the REST email bypass, shared guard enforcement, focused authentication coverage, and live positive and negative verification in the upcoming release notes.
2026-08-25 19:46:13 +03:00
Lauri Ojansivu 88d37ae98a Reject stale LDAP passwords in REST email login
Apply the existing LDAP migration password guard before the REST route can check a local hash. Preserve uniform failure timing and cover the browser hook, REST ordering, and negative response wiring.

Thanks to akitzing and xet7 !

Fixes #4419,
2026-08-25 19:45:38 +03:00
Lauri Ojansivu 1dda8f461e Document LDAP encryption configuration closure
Record the preferred transport values, retained legacy compatibility, focused regression coverage, and live Meteor startup verification in the upcoming release notes.
2026-08-25 19:40:26 +03:00
Lauri Ojansivu 7fb9ad1d75 Close LDAP encryption configuration ambiguity
Document the preferred LDAPS, STARTTLS, and unencrypted settings, replace the legacy FreeIPA example, and pin the existing normalized runtime wiring and warnings with regression coverage.

Thanks to robertdahlem and xet7 !

Fixes #4158,
2026-08-25 19:39:57 +03:00
Lauri Ojansivu b50043cbaa Document hardened LDAP admin-group login
Record the verified #4036 behavior, exact group-list hardening, focused and full LDAP coverage, Meteor compilation, and external-directory test limitation.
2026-08-25 19:36:52 +03:00
Lauri Ojansivu 139bf43418 Close LDAP admin-group login filtering gaps
Preserve the existing admin-group and multi-group login support while parsing names once, removing blank and duplicate entries, escaping every configured assertion value, and refusing blank-only group lists before an LDAP search.

Thanks to Hellnino18 and xet7 !

Fixes #4036,
2026-08-25 19:36:31 +03:00
Lauri Ojansivu 320be9c9a1 Document safe Sandstorm member cleanup
Move #1192 out of the infrastructure backlog and record the local-removal warning, card-role cleanup coverage, client compilation, and live member permission regressions.
2026-08-25 19:34:32 +03:00
Lauri Ojansivu b9cfc1d8d4 Clarify local member removal on Sandstorm
Keep the board-admin removal action available for clearing stale WeKan members, but warn that it does not revoke grain access and that access must also be removed through Sandstorm. Protect the platform gate, permissions, card-role cleanup, and board removal wiring.

Thanks to soerenk, ocdtrekkie, rautamiekka, kentonv, ertanalytics, sjoki, lslamp and xet7 !

Fixes #1192,
2026-08-25 19:34:06 +03:00
Lauri Ojansivu 9531ebc98c Document LDAP REST authentication
Move #3707 out of the infrastructure backlog and record the Meteor login-handler integration, focused authentication coverage, full compilation, and live REST API regression results.
2026-08-25 19:29:57 +03:00
Lauri Ojansivu b294ecfb01 Authenticate LDAP users through the REST login route
Route existing and first-time LDAP username credentials through Meteor server login handlers instead of requiring a local bcrypt password. Preserve uniform failures, throttling, two-factor checks, REST token creation, and local-password behavior.

Thanks to mariogalan, ViViDboarder, mgiacomoli, HaKePlan and xet7 !

Fixes #3707,
2026-08-25 19:29:41 +03:00
Lauri Ojansivu 771e1e0097 Document fail-closed LDAP group filters
Move #3700 out of the infrastructure backlog and record the validated missing-setting diagnostics, fail-closed membership behavior, regression coverage, and live Meteor compilation.
2026-08-25 19:24:49 +03:00
Lauri Ojansivu 433348c667 Fail closed on incomplete LDAP group filters
Name every missing membership setting before a group lookup. Refuse filtered logins and return no synchronization groups instead of silently omitting the member clause and searching every group in the directory.

Thanks to fgoe and xet7 !

Fixes #3700,
2026-08-25 19:24:25 +03:00
Lauri Ojansivu 64c2bfc8e1 Document multi-valued LDAP email normalization
Add the #3630 account-creation fix and its focused, compiled Meteor, and live Chromium verification to the upcoming release notes.
2026-08-25 19:21:46 +03:00
Lauri Ojansivu 7f12b0a59c Normalize multi-valued LDAP email attributes
Decode scalar, multi-valued, and serialized LDAP Buffer attributes before passing email addresses to Meteor accounts. Preserve each alias as a string, select the first address for account creation, discard empty values, and remove duplicate aliases case-insensitively.

Thanks to mlang38 and xet7 !

Fixes #3630,
2026-08-25 19:21:16 +03:00
Lauri Ojansivu 3525585400 Document non-blocking outgoing webhooks
Move the completed hanging-receiver report out of TODO Later and add its explicit regressions to the Upcoming WeKan release notes.

Thanks to n8ores, RyanHecht and xet7 !
2026-08-25 19:15:02 +03:00
Lauri Ojansivu 0c08ea8a1f Close the blocking outgoing webhook report
Current activity delivery already launches the outgoing webhook without awaiting it and isolates both synchronous and asynchronous failures. Add explicit regressions showing a receiver that holds its response yields control immediately, a rejected delivery resolves safely, and the activity hook retains its non-awaited callback wiring.

Thanks to n8ores, RyanHecht and xet7 !

Fixes #3575,
2026-08-25 19:14:45 +03:00
Lauri Ojansivu 4d1a8b9b5b Refresh the investigated issue backlog
Carry the Sandstorm outbound-webhook Powerbox dependency forward and remove the environment and live-UI reports completed during the current issue pass.

Thanks to braindevops, ocdtrekkie and xet7 !
2026-08-25 19:12:59 +03:00
Lauri Ojansivu 4d22b7c1dd Document first-login CAS account creation
Add the missing-services guard and its positive and account-conflict regressions to the Upcoming WeKan release notes.

Thanks to faelys, Meeques, jgribonvald and xet7 !
2026-08-25 19:11:49 +03:00
Lauri Ojansivu ca59a68fc7 Accept first CAS users without OAuth services
Guard the OIDC-only account normalization path when a custom login handler supplies no services object. The CAS handler already provides a validated username, verified email, profile and authentication method at the top level, so first login now reaches normal account insertion without fabricating OAuth data. Add positive wiring and negative account-conflict regressions.

Thanks to faelys, Meeques, jgribonvald and xet7 !

Fixes #3204,
2026-08-25 19:11:26 +03:00
Lauri Ojansivu faad48fd69 Document restored top-bar board templates
Add the current-template picker repair and its focused and live Chromium coverage to the Upcoming WeKan release notes.

Thanks to Jimmi61 and xet7 !
2026-08-25 19:09:38 +03:00
Lauri Ojansivu 3cdc64d14d Restore board templates in the top bar
Restore the top-bar Create Board control and classify it as a board-template search just like Add Board. Keep the nested picker open while its template-container subscription becomes ready instead of returning to the parent popup with stale or empty data. Existing copy logic retains custom-field definitions. Add focused and live Chromium coverage for archived-template filtering, card exclusion, and custom-field copying.

Thanks to Jimmi61 and xet7 !

Fixes #3070,
2026-08-25 19:09:17 +03:00
Lauri Ojansivu d9c5544054 Document closed Apache forward proxying
Add the hardened reverse-proxy examples and their configuration regressions to the Upcoming WeKan release notes.

Thanks to bastos77 and xet7 !
2026-08-25 19:01:00 +03:00
Lauri Ojansivu 577ccb9f83 Keep Apache reverse-proxy examples closed
Set ProxyRequests Off explicitly in both documented WeKan virtual hosts so an unsafe inherited global setting cannot expose an open forward proxy. Remove the obsolete wildcard proxy authorization and explain why ProxyMaxForwards does not address the vulnerability. Add positive and negative configuration regressions.

Thanks to bastos77 and xet7 !

Fixes #3001,
2026-08-25 19:00:43 +03:00
Lauri Ojansivu a14dbe144e Document first profile email saving
Add the missing-email profile repair and its positive and negative live coverage to the Upcoming WeKan release notes.

Thanks to jpfleury and xet7 !
2026-08-25 18:59:15 +03:00
Lauri Ojansivu ac0d9a3cd1 Save the first email on accounts without one
Treat a missing emails array as an empty profile address and let an authenticated user save their own email when Allow Email Change is enabled. Keep global administrators authorized, reject cross-user and disabled-setting writes, and store the new address unverified. Add focused and live Chromium regressions for the Sandstorm-shaped account.

Thanks to jpfleury and xet7 !

Fixes #2445,
2026-08-25 18:58:51 +03:00
Lauri Ojansivu a372816bc2 Document multi-board Sandstorm membership
Add the restored second-board member workflow and its focused and live Chromium coverage to the Upcoming WeKan release notes.

Thanks to Mini-pen, drseussofporn, jpretori, ocdtrekkie, wellgoodthen, stoltenhoff and xet7 !
2026-08-25 18:55:13 +03:00
Lauri Ojansivu 3c57cb876e Add Sandstorm users to any board
Replace the broken Powerbox-only member control with WeKan's maintained permission-checked member picker in multi-board grains. Existing grain identities can now be searched and attached to the selected board, while local email-account creation remains hidden because those accounts cannot use Sandstorm header authentication. Preserve configure-derived global admin rights and add focused plus live Chromium regressions for second-board membership.

Thanks to Mini-pen, drseussofporn, jpretori, ocdtrekkie, wellgoodthen, stoltenhoff and xet7 !

Fixes #2405,
2026-08-25 18:54:50 +03:00
Lauri Ojansivu f2f1c9b137 Document verified webhook activity placeholders
Add the closed duplicate and its focused placeholder regression coverage to the Upcoming WeKan release notes.

Thanks to sfahrenholz, chotaire and xet7 !
2026-08-25 18:49:59 +03:00
Lauri Ojansivu 6c2b7c4e8f Close the stale webhook placeholder duplicate
The canonical report #2226 is closed, and the current activity builder resolves every entity before outgoing webhook text reaches TAPi18n. Add focused regression coverage for the resolved parameter object and translated placeholder inventories.

Thanks to sfahrenholz, chotaire and xet7 !

Fixes #1969,
2026-08-25 18:49:42 +03:00
Lauri Ojansivu 3d7508fa81 Document restored email verification links
Add the issue 1426 token-route fix and its live positive and negative browser verification to the Upcoming WeKan release.

Thanks to Sunildevb, jgrocha and xet7 !
2026-08-25 18:46:21 +03:00
Lauri Ojansivu bb267fddf4 Restore anonymous email verification links
Configure the maintained accounts package's verification route so generated links reach /verify-email/:token and consume the token instead of falling through to WeKan's signed-in guard. Also expose the companion resend route. Add positive and negative wiring coverage plus live Chromium cases proving valid links verify, consume, and sign in while invalid tokens do none of those.

Thanks to Sunildevb, jgrocha, nickian, bbyszio, teutat3s, MichaelW-SD, alessiozanutta, mbalmer, Ranzo, DarthJahus, AndreLCRodrigues and xet7 !

Fixes #1426,
2026-08-25 18:46:03 +03:00
Lauri Ojansivu 6e58d692e1 Document removal of the broken Winston startup hook
Add the issue 1094 closure and its legacy-logger regression coverage to the Upcoming WeKan release.

Thanks to zagoa, huneau and xet7 !
2026-08-25 18:42:18 +03:00
Lauri Ojansivu cae55dae4d Close the legacy Winston startup report
The Winston/Zulip database observer that replaced Meteor's default console transport was removed in 2017, and current live Meteor runs start logging and MongoDB together. Remove stale Docker guidance that still advertised the obsolete feature and add regressions protecting the removed startup files, dependencies, and active database URL.

Thanks to zagoa, huneau and xet7 !

Fixes #1094,
2026-08-25 18:42:05 +03:00
Lauri Ojansivu c765a77c6d Document verified Sandstorm username claims
Add the issue 574 closure and its existing case-insensitive and concurrent-claim coverage to the Upcoming WeKan release.

Thanks to mitar and xet7 !
2026-08-25 18:41:01 +03:00
Lauri Ojansivu eca1095f28 Close the Sandstorm username race report
Commit 25d33cec3 already made Sandstorm username probing case-insensitive, anchored, and regex-safe, then retries with the next suffix when a concurrent account wins the unique-index race. All 11 focused positive, negative, race, and integration-wiring regressions pass against the current source.

Thanks to mitar and xet7 !

Fixes #574,
2026-08-25 18:40:48 +03:00
Lauri Ojansivu db14bf6671 Document resumed Sandstorm grain layouts
Add the issue 697 iframe lifecycle fix and its live browser verification to the Upcoming WeKan release.

Thanks to neynah, dwrensha and xet7 !
2026-08-25 18:40:16 +03:00
Lauri Ojansivu 9bc1896ff7 Refresh hidden grain layouts when their iframe resumes
A Sandstorm grain can finish rendering while its iframe is hidden, leaving responsive helpers with the hidden frame dimensions because returning does not reliably emit resize. Coalesce visibility, page-show, and focus resumes, wait until the next rendered frame, and dispatch the ordinary resize path so the sidebar, header, popups, and mobile layout measure the restored viewport. Keep registration safe across Meteor hot reloads and add positive, negative, wiring, and live Chromium regressions.

Thanks to neynah, dwrensha and xet7 !

Fixes #697,
2026-08-25 18:39:57 +03:00
Lauri Ojansivu ea704bb966 Document the verified Chinese font fallback
Add the issue 914 closure and its live Chinese-locale browser coverage to the Upcoming WeKan release.

Thanks to ZhouMoHan-work and xet7 !
2026-08-25 18:32:43 +03:00
Lauri Ojansivu 6aa455fb61 Close the Chinese font fallback report
The Japanese-specific Yu Gothic and Meiryo fallbacks were removed in 2017, and current WeKan also keeps the root language synchronized with the selected locale while offering a validated per-user font override. Add positive and negative source coverage plus a live Chromium regression that selects Chinese, verifies html lang, and rejects Japanese fonts from the computed default stack.

Thanks to ZhouMoHan-work and xet7 !

Fixes #914,
2026-08-25 18:32:14 +03:00
Lauri Ojansivu d410f40152 Link the live-tested long-list regression
Point the Upcoming Card movement entry at the final exact-card browser regression that passed against the isolated Meteor development stack.

Thanks to lenxeon and xet7 !
2026-08-25 18:29:00 +03:00
Lauri Ojansivu 5f16d1cbbf Select the exact card in the long-list regression
Use the seeded card identifier instead of substring title matching so Long Card 1 cannot also select Long Card 10. The focused Chromium test now passes against an isolated live Meteor development server and MongoDB.

Thanks to lenxeon and xet7 !

Fixes #761,
2026-08-25 18:28:24 +03:00
Lauri Ojansivu 7cc278f941 Document the long-list bottom drop regression
Add the verified issue 761 coverage to the Upcoming WeKan release under Card movement.

Thanks to lenxeon and xet7 !
2026-08-25 18:24:30 +03:00
Lauri Ojansivu a68ad67977 Close the long-list bottom drop report
The current card drag handler scrolls the list body under the pointer, refreshes sortable geometry after scrolling, and keeps the board still until the list reaches its end. Correct the focused source regression for the formatted conditional and add a Chromium regression that drags through a long list, observes vertical scrolling, and verifies the persisted card order changes.

Thanks to lenxeon and xet7 !

Fixes #761,
2026-08-25 18:24:08 +03:00
Lauri Ojansivu 1b630d3adf Document distinct board archive destinations
Add the verified Sandstorm-safe Archived Items navigation and explicit closing commit for issue 1280 to Upcoming.

Thanks to ocdtrekkie and xet7 !
2026-08-25 18:21:00 +03:00
Lauri Ojansivu 97e7b3158e Close the Sandstorm archive navigation report
Current board menus route Archived Items to the board's card, list, and swimlane archive, while archived boards remain a distinct All Boards section. Add an exact regression that prevents those destinations from being conflated again; the archived-card browser restore flow remains registered.

Thanks to ocdtrekkie and xet7 !

Fixes #1280,
2026-08-25 18:20:25 +03:00
Lauri Ojansivu 6f69e89976 Document visible card activity history
Add the verified full-history section behavior and its new persisted-activity browser regression for issue 1658 to Upcoming.

Thanks to franksiler and xet7 !
2026-08-25 18:19:04 +03:00
Lauri Ojansivu 1ad5d2ba3c Close the missing card activity report
Card details already exposes Activities through its collapsed section and requests full history when opened, independent of obsolete showActivities flags. Add a browser regression that seeds a persisted card activity, opens the section, and requires that exact history entry to render.

Thanks to franksiler and xet7 !

Fixes #1658,
2026-08-25 18:18:47 +03:00
Lauri Ojansivu 18b618aa0f Document persistent linked-card member avatars
Add the verified visibility-filtered avatar publication and refreshed browser regression for issue 1946 to Upcoming.

Thanks to theonafor and xet7 !
2026-08-25 18:17:53 +03:00
Lauri Ojansivu 6f6fe229a3 Close the linked-card avatar refresh report
Authorized linked-source publications already include the minimal member avatar profile after source-board visibility filtering. Extend the cross-board browser regression with a second member's custom avatar and verify that it remains rendered after leaving and re-entering the linking board.

Thanks to theonafor and xet7 !

Fixes #1946,
2026-08-25 18:17:40 +03:00
Lauri Ojansivu fd28eec990 Document private legacy board imports
Add the fail-closed Sandstorm permission fix and its focused and browser regressions for issue 1991 to the Upcoming release notes.

Thanks to xet7 !
2026-08-25 18:15:55 +03:00
Lauri Ojansivu b552af1606 Import legacy boards privately by default
Treat only an explicit supported public permission as public during WeKan board import. Missing Sandstorm-era fields, malformed values, and lookalikes now fail closed to private while explicit private and public exports retain their intended visibility. Add focused and browser regressions.

Thanks to xet7 !

Fixes #1991,
2026-08-25 18:15:26 +03:00
Lauri Ojansivu 3dabbc1135 Document enforceable REST token logout
Add the verified token-revocation behavior and its new end-to-end API regression for issue 1437 to the Upcoming release notes.

Thanks to Gobliins, MonkeyNinja, AbdullahAlAsad and xet7 !
2026-08-25 18:13:52 +03:00
Lauri Ojansivu 6e4e1a5db7 Close the stale REST token report
The logout endpoint already revokes the presented token or all sessions with explicit authorization guards. Add an end-to-end API regression that logs out with a real bearer token, verifies its database removal, and requires the revoked token to receive HTTP 401 afterward.

Thanks to Gobliins, MonkeyNinja, AbdullahAlAsad and xet7 !

Fixes #1437,
2026-08-25 18:13:38 +03:00
Lauri Ojansivu a360eddf00 Document reactive sidebar label drops
Add the verified late-card droppable behavior and its new browser regression for issue 1554 to the Upcoming release notes.

Thanks to marco-aa, ocdtrekkie and xet7 !
2026-08-25 18:12:55 +03:00
Lauri Ojansivu 712ce25d0e Close the sidebar label drag report
The reactive droppable fix already reinitializes cards added after the list first renders. Add a browser regression that inserts a late card, performs a real pointer drag from the sidebar label, and verifies the label assignment in the database.

Thanks to marco-aa, ocdtrekkie and xet7 !

Fixes #1554,
2026-08-25 18:12:41 +03:00
Lauri Ojansivu b4dd6a9d4f Document resilient subtask board navigation
Record the verified unloaded-board fallback and the explicit closing commit for issue 1853 in the Upcoming release notes.

Thanks to xet7 and TNick !
2026-08-25 18:11:37 +03:00
Lauri Ojansivu d6eaaa7377 Close the missing subtask board navigation report
The navigation fix in 7c0ae509a derives the card and board route from the subtask itself, falls back to its stored boardId when the destination board is not loaded, and warns instead of throwing for malformed data. All 11 loaded, unloaded, plain-document, and negative wiring regressions pass.

Thanks to xet7 and TNick !

Fixes #1853,
2026-08-25 18:11:23 +03:00
Lauri Ojansivu d93fe3d5b8 Document complete Sandstorm card-date imports
Record the verified date restoration and safe malformed-data handling and the explicit closing commit for issue 1992 in Upcoming.

Thanks to xet7 !
2026-08-25 18:10:54 +03:00
Lauri Ojansivu bb925445b3 Close the Sandstorm card-date import report
The importer fix in 7c0ae509a restores all five exported card dates, falls back to the card document when old or pruned exports lack creation activity, and degrades corrupt optional dates safely instead of aborting card insertion. All 13 focused regressions pass and both WeKan import browser flows remain registered.

Thanks to xet7 !

Fixes #1992,
2026-08-25 18:10:38 +03:00
Lauri Ojansivu 789bab5cac Document safe swimlane archive round trips
Record the verified card-preserving archive and restore behavior and the explicit closing commit for issue 2292 in Upcoming.

Thanks to Maevabus, chirrut2 and xet7 !
2026-08-25 18:10:09 +03:00
Lauri Ojansivu d6cf4016d0 Close the swimlane archive data-loss report
The fix in 7c0ae509a archives a swimlane's active cards with the lane and restores exactly that cascade, while preserving cards archived earlier and cards in other lanes. All 11 positive, negative, legacy-data, and repeated-round-trip regressions pass.

Thanks to Maevabus, chirrut2 and xet7 !

Fixes #2292,
2026-08-25 18:09:46 +03:00
Lauri Ojansivu 5f78e7a63c Document stable cross-board card ordering
Add the verified finite-sort fix and its new cross-board browser regression for issue 2494 to the Upcoming release notes.

Thanks to rlach and xet7 !
2026-08-25 18:09:05 +03:00
Lauri Ojansivu 500452f786 Close the cross-board card ordering report
The synchronous client no-cache fix already prevents cross-board moves from deriving NaN sort values. Add a browser regression that moves a card into a populated list on another board, requires three distinct finite positions, and confirms every destination card remains visible.

Thanks to rlach and xet7 !

Fixes #2494,
2026-08-25 18:08:43 +03:00
Lauri Ojansivu 30e9d8231f Document reliable board membership lifecycle
Record the verified invitation, removal, and team-access behavior and the explicit closing commit for issue 4730 in Upcoming.

Thanks to UCS, rainbowjaw, DVNBLMHC and xet7 !
2026-08-25 18:07:24 +03:00
Lauri Ojansivu f861c92de9 Close the board membership lifecycle report
The invitation fix in 9ddbc777d keeps decline and removal inactive, clears pending invitations, rejects self-reactivation without an invitation, and preserves the valid accept flow. Its 11 focused cases pass; team-to-board membership propagation passes 16 more cases, and browser coverage registers both administrator removal and its non-admin negative path.

Thanks to UCS, rainbowjaw, DVNBLMHC and xet7 !

Fixes #4730,
2026-08-25 18:07:07 +03:00
Lauri Ojansivu 0c104560e8 Document grouped archive card actions
Record the verified Restore and Delete grouping and the explicit closing commit for issue 3199 in the Upcoming release notes.

Thanks to fxkr and xet7 !
2026-08-25 18:06:25 +03:00
Lauri Ojansivu 41d6a8a28c Close the grouped archive actions report
The fix in 973c2ad90 already wraps each archived card with its own Restore and Delete links and adds theme-neutral, RTL-safe separation below the group. Its structural, spacing, hover, and narrow-sidebar regressions pass, and the browser restore scenario remains registered.

Thanks to fxkr and xet7 !

Fixes #3199,
2026-08-25 18:06:10 +03:00
Lauri Ojansivu 1a888bfe33 Document restored All Boards list details
Record the verified non-reactive restoration of optional list names and card counts for issue 4959 in the Upcoming release notes.

Thanks to SmartPhoneLover, helioguardabaxo and xet7 !
2026-08-25 18:05:51 +03:00
Lauri Ojansivu 02a3630e23 Close the restored All Boards details report
The non-reactive tile-data implementation already restored per-list card counts without reviving the board-icon movement that caused their removal. Mark the enabled-board positive and ordered-list regressions for the original report; the same suite retains disabled, legacy-board, and cross-board negative cases.

Thanks to SmartPhoneLover, helioguardabaxo and xet7 !

Fixes #4959,
2026-08-25 18:05:17 +03:00
Lauri Ojansivu 1b4d17d00e Document stable Admin People paging
Add the verified server-named page and account-field regressions for issue 4897 to the Upcoming release notes.

Thanks to jas01, dvschuetz and xet7 !
2026-08-25 18:04:48 +03:00
Lauri Ojansivu 68e3702266 Close the inconsistent Admin People report
Current server-side paging names each People page explicitly, renders only those users in stable creation order, and publishes each row's email, creation time, and authentication method. Mark those positive and negative regressions for the original report so cached user documents cannot make rows disappear or change identity data while scrolling.

Thanks to jas01, dvschuetz and xet7 !

Fixes #4897,
2026-08-25 18:04:21 +03:00
Lauri Ojansivu 27c45a8a24 Document the corrected All Boards card counters
Record the verified legacy-board counter and member-visibility fix and its explicit issue-closing commit in the Upcoming release notes.

Thanks to Meeques, helioguardabaxo and xet7 !
2026-08-25 18:03:26 +03:00
Lauri Ojansivu 4f702d5355 Close the All Boards card-counter report
The startup schema and All Boards tile fix in 9ddbc777d already restored per-board counter and member visibility for old and new boards. The current tile-data suite verifies enabled and disabled counters independently, missing legacy flags, member visibility, foreign-list isolation, and the template wiring.

Thanks to Meeques, helioguardabaxo and xet7 !

Fixes #4825,
2026-08-25 18:03:01 +03:00
Lauri Ojansivu ca4fa13e19 Document the board-scoped filter selection fix
Record the verified two-board regression and the explicit closing commit for issue 2306 in the Upcoming release notes.

Thanks to Gobliins and xet7 !
2026-08-25 18:02:33 +03:00
Lauri Ojansivu ec9410a3a4 Close the board-scoped filter selection report
The fix in 7c0ae509a scopes both filter-to-selection queries and every later bulk-action selector to the current board. Its positive, negative, and end-to-end selector regressions reproduce the two-board scenario and confirm that a matching card cached from another board cannot be selected or mutated.

Thanks to Gobliins and xet7 !

Fixes #2306,
2026-08-25 18:02:12 +03:00
Lauri Ojansivu 7b28747037 Document the closed IFTTT trigger report
Add the verified moved-from-list regression to the Rules release notes and record that the other reported automation failures had already been corrected.

Thanks to TiibCD and xet7 !
2026-08-25 18:01:17 +03:00
Lauri Ojansivu baa780331c Close the remaining IFTTT trigger report
The checklist, email, member-removal, and date-action failures have already been corrected. Mark the exact moved-from-list positive and move-into-list negative regressions as coverage for the remaining report so later rule changes cannot silently revive it.

Thanks to TiibCD and xet7 !

Fixes #1972,
2026-08-25 18:00:49 +03:00
Lauri Ojansivu 77b8ed2cb4 Document private linked-card regression coverage 2026-08-25 17:59:20 +03:00
Lauri Ojansivu 9cdbe1a539 Close the private linked-card visibility report
Pin the current secure behavior: a viewer without source-board access can open and close the linked card's stored snapshot, while the source card and its children remain unpublished. The production fallback and authorization fixes already exist in current source.

Thanks to TiibCD, t0mcat1337 and xet7 !

Fixes #1942,
2026-08-25 17:59:06 +03:00
Lauri Ojansivu b27a094b18 Document preserved copied-comment history 2026-08-25 17:57:29 +03:00
Lauri Ojansivu 8dff96d7b3 Preserve copied comment history
Keep each copied comment's original author and timestamps, re-home it without mutating the cached source, and avoid generating false new-comment activity. Add positive, negative, wiring, and REST browser regressions.

Thanks to ocdtrekkie, 2martens, frmwrk123 and xet7 !

Fixes #1213,
2026-08-25 17:57:13 +03:00
Lauri Ojansivu f30a3e7d61 Document invitee collision regression coverage 2026-08-25 17:55:18 +03:00
Lauri Ojansivu d3f2275866 Close the duplicate invitee collision report
Pin the exact two-domain email collision and mixed-case normalization scenarios from the report. The production fix was implemented in 0f2179988 for duplicate issue #619.

Thanks to ghost, rvalle and xet7 !

Fixes #825,
2026-08-25 17:55:03 +03:00
Lauri Ojansivu 2f07028356 Document the mobile Search navigation fix 2026-08-25 17:52:39 +03:00
Lauri Ojansivu 7d67f80df5 Return mobile Search directly to the board
Close the full-screen Search sidebar when its Back control is used on a mini screen, while preserving desktop and other sidebar transitions. Add positive, negative, wiring, and mobile browser regressions.

Thanks to 1ubuntuuser and xet7 !

Fixes #3576,
2026-08-25 17:52:23 +03:00
Lauri Ojansivu 0993a0acd4 Document remote card-details cleanup 2026-08-25 17:46:00 +03:00
Lauri Ojansivu 951b072b58 Close card details after remote removal
Observe the card that opened each details view and clear desktop, mobile route, and popup state when another client removes it or moves it to a different board. Add positive, negative, wiring, and mobile browser regressions.\n\nThanks to rptl and xet7 !\n\nFixes #3114,
2026-08-25 17:45:40 +03:00
Lauri Ojansivu 71a2c47d71 Document Board Table view improvements 2026-08-25 17:41:03 +03:00
Lauri Ojansivu 0847af8ec6 Add optional swimlane grouping to Table view
Keep the flat table as the default and let each user persist an optional grouped mode. Group cards in board swimlane order, preserve the selected column sort inside each lane, and insert section rows after pagination so headers never consume card slots.

Add user-isolation, empty-state, section-boundary and browser persistence coverage.

Thanks to rmb82 and xet7 !

Fixes #6634,
2026-08-25 17:39:23 +03:00
Lauri Ojansivu 7c8674449c Document follow-up FerretDB security fixes 2026-08-25 17:37:35 +03:00
Lauri Ojansivu 828cb5e534 Restore sorting to board Table view
Make text, member, label and date headers toggle a shared client-side ascending or descending comparator. Keep empty date values last and use card title as a deterministic tie-breaker.

Add comparator boundary coverage and a browser header-toggle regression.

Thanks to rmb82 and xet7 !

Fixes #6633,
2026-08-25 17:32:57 +03:00
Lauri Ojansivu ad6a7b7c8c Make board Table view responsive
Use the available browser width and default card titles to a compact single line with an escaped native tooltip. Add a per-user wrap preference that persists locally.

Cover default, toggled and user-isolated preference behavior plus the rendered browser layout.

Thanks to rmb82 and xet7 !

Fixes #6632,
2026-08-25 17:31:29 +03:00
Lauri Ojansivu f7255cac65 Document the fast touch drag fix 2026-08-25 17:27:57 +03:00
Lauri Ojansivu 6c9ba47456 Prevent fast touch drags from opening cards
Suppress only the synchronous click that touch-punch emits after a jQuery UI sortable drag. Ordinary taps and later clicks retain their normal behavior.

Add positive and negative unit coverage plus a real fast-touch drag browser regression.

Thanks to xator91 and xet7 !

Fixes #5421,
2026-08-25 17:27:33 +03:00
Lauri Ojansivu 2eb7401e5d Document the latest FerretDB security fixes 2026-08-25 17:14:21 +03:00
Lauri Ojansivu 93a8ac15bb Document the card drag flicker fix 2026-08-25 17:08:18 +03:00
Lauri Ojansivu b687f37272 Keep dragged cards visible during reactive moves
Preserve a presentation-only copy in the target list while sortable restores the source DOM and Blaze reconciles the database move. Remove it as soon as the real card arrives, on rejection, or at a safety timeout.

Add a browser regression with an intentionally delayed move so the visual gap is deterministic.

Thanks to mimZD and xet7 !

Fixes #6430,
2026-08-25 17:07:19 +03:00
Lauri Ojansivu 38b60fa193 Document the Rules schema fix
Add issue 6630 to the Upcoming bug-fix section, keep the release summary current, and record the positive, negative and browser regression coverage.

Thanks to rmb82 and xet7 !
2026-08-25 17:00:44 +03:00
Lauri Ojansivu ea90023287 Keep action descriptions out of rule documents
Date-setting, date-clearing and add-member actions now store their generated description only on the Action document, matching every other rule action and the Rules schema.

Add positive and negative source coverage plus a browser flow that verifies the saved Rule omits desc while its Action retains it.

Thanks to rmb82 and xet7 !

Fixes #6630,
2026-08-25 17:00:15 +03:00
Lauri Ojansivu 5b3aaf8f36 Document FerretDB CodeQL security fixes
Lead the Upcoming WeKan release with the companion FerretDB v1.59 security work, preserve the provenance table until new bundles are built, and retain the Fulah translation progress below it.

Thanks to xet7 !
2026-08-25 16:58:44 +03:00
Lauri Ojansivu b91eed5710 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-25 16:56:49 +03:00
Lauri Ojansivu 82a6004784 Record initial Fulah translation batches
Add the first two Fulah batches to the Upcoming release and refresh the TODO Later handoff to 2,066 remaining Fulah values and 171,067 untranslated values overall.

Thanks to xet7 !
2026-08-25 16:56:48 +03:00
Lauri Ojansivu 0efb942afb Continue Fulah activity and workspace translation
Translate 50 more Fulah values covering card movement, activity summaries, checklist events and workspace controls.

Preserve all named and percent placeholders and extend whole-locale regression coverage.

Thanks to xet7 !
2026-08-25 16:56:48 +03:00
Lauri Ojansivu 3d02d1d87f Begin Fulah activity translation
Translate the first 50 Fulah values covering activity history, board membership, comments, dates, attachments, checklists, custom fields and imports.

Keep all named and percent placeholders exact and add whole-locale token and markup regression coverage.

Thanks to xet7 !
2026-08-25 16:56:48 +03:00
github-actions[bot] 1c54d89899 Bump versions for v11.12 2026-08-25 13:30:47 +00:00
Lauri Ojansivu a283110bae Prepare v11.12 release 2026-08-25 16:29:40 +03:00
Lauri Ojansivu 352d0fea2c Document bounded database queries
Thanks to xet7 !
2026-08-25 15:49:38 +03:00
Lauri Ojansivu c69bf503c3 Bound high-volume database queries
Scope attachment and checklist discovery to authorized boards, project only fields consumed by filename search, retain only the requested text-ranking window in memory, page Due Cards without truncating the result set, and add compound indexes for lazy and due-card query shapes.\n\nThanks to xet7 !
2026-08-25 15:40:39 +03:00
Lauri Ojansivu 70729adb33 Document FerretDB board polling improvements 2026-08-25 15:05:19 +03:00
Lauri Ojansivu 00026ff231 Reduce FerretDB board publication polling
Default FerretDB launchers to adaptive card loading, keep unrelated public boards out of All Boards, index the narrowed query shapes, and share linked-card discovery within one composite parent evaluation without TTL authorization caching.

Thanks to xet7 !
2026-08-25 15:04:22 +03:00
Lauri Ojansivu 957c3ec7b4 Document LDAP display name fix 2026-08-25 14:55:22 +03:00
Lauri Ojansivu 59f9b31168 Keep LDAP display names in restricted searches
Merge the separately configured identity attributes into LDAP user attribute allowlists so displayName is available for full-name synchronization.

Thanks to Nissulya and xet7 !

Fixes #6617,
2026-08-25 14:54:27 +03:00
Lauri Ojansivu a6478b5a4a Record Ewe translation completion 2026-08-25 14:50:03 +03:00
Lauri Ojansivu 09279e95d7 Complete Ewe translation 2026-08-25 14:49:25 +03:00
Lauri Ojansivu b79fd5d993 Record forty-first and forty-second Ewe translation batches 2026-08-25 14:46:33 +03:00
Lauri Ojansivu 9c154db443 Continue Ewe migration tuning translation 2026-08-25 14:46:11 +03:00
Lauri Ojansivu 0967f16b20 Continue Ewe migration monitoring translation 2026-08-25 14:45:16 +03:00
Lauri Ojansivu aab18ec9a5 Record thirty-ninth and fortieth Ewe translation batches 2026-08-25 14:44:06 +03:00
Lauri Ojansivu 8808b2928b Continue Ewe board repair translation 2026-08-25 14:43:41 +03:00
Lauri Ojansivu 17a1c5f2f3 Continue Ewe storage backend and migration translation 2026-08-25 14:42:29 +03:00
Lauri Ojansivu 79555e56d4 Record thirty-eighth Ewe translation batch 2026-08-25 14:41:25 +03:00
Lauri Ojansivu d51c30ef3e Continue Ewe cloud backup translation 2026-08-25 14:41:06 +03:00
Lauri Ojansivu 0eb1d6984d Record thirty-sixth and thirty-seventh Ewe translation batches 2026-08-25 14:39:34 +03:00
Lauri Ojansivu 61ec79340b Continue Ewe security and backup translation 2026-08-25 14:39:13 +03:00
Lauri Ojansivu 61ba6f3f98 Continue Ewe migration and storage translation 2026-08-25 14:37:26 +03:00
Lauri Ojansivu 072a8256c6 Record thirty-fourth and thirty-fifth Ewe translation batches 2026-08-25 14:36:06 +03:00
Lauri Ojansivu 285ad0dae7 Continue Ewe account protection and scheduling translation 2026-08-25 14:35:47 +03:00
Lauri Ojansivu 0b6bb374b8 Continue Ewe settings and accessibility translation 2026-08-25 14:34:50 +03:00
Lauri Ojansivu 6d480a334a Record thirty-second and thirty-third Ewe translation batches 2026-08-25 14:33:44 +03:00
Lauri Ojansivu d35e7dcf8b Continue Ewe storage repair and compaction translation 2026-08-25 14:33:24 +03:00
Lauri Ojansivu 73cf5b0166 Continue Ewe diagnostics and attachment translation 2026-08-25 14:32:30 +03:00
Lauri Ojansivu 5f34393f32 Record thirtieth and thirty-first Ewe translation batches 2026-08-25 14:31:08 +03:00
Lauri Ojansivu 297673768e Continue Ewe recovery and ticket translation 2026-08-25 14:30:48 +03:00
Lauri Ojansivu d77da6cb46 Continue Ewe reports and location translation 2026-08-25 14:29:59 +03:00
Lauri Ojansivu 6c335591d2 Record twenty-eighth and twenty-ninth Ewe translation batches 2026-08-25 14:28:56 +03:00
Lauri Ojansivu f752f4ccaf Continue Ewe dependencies and location translation 2026-08-25 14:28:38 +03:00
Lauri Ojansivu e753a9c1c9 Continue Ewe global search guidance translation 2026-08-25 14:27:53 +03:00
Lauri Ojansivu 74e3137d8d Record twenty-sixth and twenty-seventh Ewe translation batches 2026-08-25 14:26:28 +03:00
Lauri Ojansivu e6ad5fc694 Continue Ewe search operator translation 2026-08-25 14:26:11 +03:00
Lauri Ojansivu 9ab13a1326 Continue Ewe domains and search translation 2026-08-25 14:25:32 +03:00
Lauri Ojansivu 8bae86934e Record twenty-fourth and twenty-fifth Ewe translation batches 2026-08-25 14:24:31 +03:00
Lauri Ojansivu 23bba05783 Continue Ewe roles and calendar translation 2026-08-25 14:24:12 +03:00
Lauri Ojansivu 462bb64cda Continue Ewe authentication and activity translation 2026-08-25 14:23:23 +03:00
Lauri Ojansivu 7e73013a18 Record twenty-second and twenty-third Ewe translation batches 2026-08-25 14:22:11 +03:00
Lauri Ojansivu ea996b8d23 Continue Ewe automation and manifest translation 2026-08-25 14:21:54 +03:00
Lauri Ojansivu 08ad8fa825 Continue Ewe automation action translation 2026-08-25 14:21:09 +03:00
Lauri Ojansivu 1b004682a0 Record twentieth and twenty-first Ewe translation batches 2026-08-25 14:20:19 +03:00
Lauri Ojansivu bd54f339fd Continue Ewe automation scheduling translation 2026-08-25 14:20:00 +03:00
Lauri Ojansivu 54c02b7e60 Continue Ewe automation workflow translation 2026-08-25 14:19:14 +03:00
Lauri Ojansivu 269e7c16b8 Record eighteenth and nineteenth Ewe translation batches 2026-08-25 14:18:15 +03:00
Lauri Ojansivu ab595a8f82 Continue Ewe card settings and deletion translation 2026-08-25 14:17:58 +03:00
Lauri Ojansivu 24f2c2ee60 Continue Ewe system and multitenancy translation 2026-08-25 14:17:10 +03:00
Lauri Ojansivu 863a06897f Record sixteenth and seventeenth Ewe translation batches 2026-08-25 14:15:58 +03:00
Lauri Ojansivu 4aa0418be6 Continue Ewe transfer and webhook translation 2026-08-25 14:15:38 +03:00
Lauri Ojansivu 5a0e600f4c Continue Ewe tracking and customization translation 2026-08-25 14:14:49 +03:00
Lauri Ojansivu 5037f41340 Record fourteenth and fifteenth Ewe translation batches 2026-08-25 14:13:43 +03:00
Lauri Ojansivu 8a8613a775 Continue Ewe access and shortcut translation 2026-08-25 14:13:25 +03:00
Lauri Ojansivu d63bb85c3f Continue Ewe roles and list actions translation 2026-08-25 14:12:27 +03:00
Lauri Ojansivu 7794dddb4d Record twelfth and thirteenth Ewe translation batches 2026-08-25 14:11:07 +03:00
Lauri Ojansivu cf9255eddb Continue Ewe Trello and member mapping translation 2026-08-25 14:10:48 +03:00
Lauri Ojansivu efaf9537a6 Continue Ewe filtering and board import translation 2026-08-25 14:09:59 +03:00
Lauri Ojansivu cd937f0854 Record tenth and eleventh Ewe translation batches 2026-08-25 14:08:24 +03:00
Lauri Ojansivu 0b709945c6 Continue Ewe export and filter translation 2026-08-25 14:08:05 +03:00
Lauri Ojansivu b30572ced5 Continue Ewe email and validation translation 2026-08-25 14:07:22 +03:00
Lauri Ojansivu 70e752f713 Record eighth and ninth Ewe translation batches 2026-08-25 14:06:16 +03:00
Lauri Ojansivu 9a27513815 Continue Ewe roles and custom fields translation 2026-08-25 14:05:59 +03:00
Lauri Ojansivu efb1bb4b21 Continue Ewe navigation and color translation 2026-08-25 14:05:10 +03:00
Lauri Ojansivu b58ad5774c Record sixth and seventh Ewe translation batches 2026-08-25 14:04:18 +03:00
Lauri Ojansivu 08423a6e8d Continue Ewe membership and appearance translation 2026-08-25 14:04:02 +03:00
Lauri Ojansivu 068c2fdefa Continue Ewe voting and import translation 2026-08-25 14:03:20 +03:00
Lauri Ojansivu c99a80e8f9 Record fourth and fifth Ewe translation batches 2026-08-25 14:02:28 +03:00
Lauri Ojansivu ca486e0197 Continue Ewe board view and archive translation 2026-08-25 14:02:11 +03:00
Lauri Ojansivu 51b6341b32 Continue Ewe board administration translation 2026-08-25 14:01:21 +03:00
Lauri Ojansivu 646b0e94e3 Record second and third Ewe translation batches 2026-08-25 14:00:16 +03:00
Lauri Ojansivu fd0c42f896 Continue Ewe workspace and layout translation 2026-08-25 13:59:58 +03:00
Lauri Ojansivu 997ec2f92e Continue Ewe activity and workspace translation 2026-08-25 13:58:57 +03:00
Lauri Ojansivu 02f8b9bd97 Record first Ewe translation batch 2026-08-25 13:57:56 +03:00
Lauri Ojansivu bf2a68e131 Begin Ewe activity translation 2026-08-25 13:57:40 +03:00
Lauri Ojansivu 3636e820d9 Record Dzongkha translation completion 2026-08-25 13:55:56 +03:00
Lauri Ojansivu 1a9a258aab Complete Dzongkha translation 2026-08-25 13:55:30 +03:00
Lauri Ojansivu 3e406bae63 Continue Dzongkha migration tuning translation 2026-08-25 13:52:52 +03:00
Lauri Ojansivu 3b0e62a220 Record forty-first Dzongkha translation batch 2026-08-25 13:50:11 +03:00
Lauri Ojansivu b690edcf67 Continue Dzongkha monitoring and migration translation 2026-08-25 13:49:55 +03:00
Lauri Ojansivu 0b24e9354d Record fortieth Dzongkha translation batch 2026-08-25 13:47:46 +03:00
Lauri Ojansivu 30553c4918 Continue Dzongkha board repair translation 2026-08-25 13:47:30 +03:00
Lauri Ojansivu 08c4dd2e1e Record thirty-ninth Dzongkha translation batch 2026-08-25 13:43:26 +03:00
Lauri Ojansivu b835cb4bea Continue Dzongkha backend and migration translation 2026-08-25 13:43:10 +03:00
Lauri Ojansivu e5d32e31b9 Record thirty-eighth Dzongkha translation batch 2026-08-25 13:40:57 +03:00
Lauri Ojansivu b7771030ce Continue Dzongkha cloud and restore translation 2026-08-25 13:40:43 +03:00
Lauri Ojansivu 0eae1eb9eb Record thirty-seventh Dzongkha translation batch 2026-08-25 13:37:45 +03:00
Lauri Ojansivu e69482ddd3 Continue Dzongkha security and backup translation 2026-08-25 13:37:31 +03:00
Lauri Ojansivu 563e2aabe0 Record thirty-sixth Dzongkha translation batch 2026-08-25 13:31:04 +03:00
Lauri Ojansivu 9b286414f2 Continue Dzongkha migration and storage translation 2026-08-25 13:30:47 +03:00
Lauri Ojansivu 32e836a306 Record thirty-fifth Dzongkha translation batch 2026-08-25 13:27:43 +03:00
Lauri Ojansivu b3f5c4085e Continue Dzongkha security and scheduling translation 2026-08-25 13:27:27 +03:00
Lauri Ojansivu 7498ccd1bc Record thirty-fourth Dzongkha translation batch 2026-08-25 13:24:48 +03:00
Lauri Ojansivu acb7fb1ce6 Continue Dzongkha settings and support translation 2026-08-25 13:24:33 +03:00
Lauri Ojansivu 7b516198a6 Record thirty-third Dzongkha translation batch 2026-08-25 13:22:08 +03:00
Lauri Ojansivu 8b42afd560 Continue Dzongkha storage and compact translation 2026-08-25 13:21:52 +03:00
Lauri Ojansivu f27262b19c Record thirty-second Dzongkha translation batch 2026-08-25 13:19:18 +03:00
Lauri Ojansivu be69998f4b Continue Dzongkha diagnostics and attachment translation 2026-08-25 13:19:05 +03:00
Lauri Ojansivu c32ce026a8 Record thirty-first Dzongkha translation batch 2026-08-25 13:16:34 +03:00
Lauri Ojansivu 160ac04b66 Continue Dzongkha recovery and ticket translation 2026-08-25 13:16:20 +03:00
Lauri Ojansivu f1e4d50a1f Record thirtieth Dzongkha translation batch 2026-08-25 13:13:54 +03:00
Lauri Ojansivu c6f4b0129f Continue Dzongkha map and report translation 2026-08-25 13:13:39 +03:00
Lauri Ojansivu 7694175d79 Record twenty-ninth Dzongkha translation batch 2026-08-25 13:11:28 +03:00
Lauri Ojansivu a22a0e77cd Continue Dzongkha dependency and location translation 2026-08-25 13:11:14 +03:00
Lauri Ojansivu 7ee45e666a Record twenty-eighth Dzongkha translation batch 2026-08-25 13:09:25 +03:00
Lauri Ojansivu 7160601ef2 Continue Dzongkha search instruction translation 2026-08-25 13:09:10 +03:00
Lauri Ojansivu 003a29b904 Record twenty-seventh Dzongkha translation batch 2026-08-25 13:05:30 +03:00
Lauri Ojansivu cfa2ad9ac3 Continue Dzongkha search operator translation 2026-08-25 13:05:16 +03:00
Lauri Ojansivu 07204720fc Record twenty-sixth Dzongkha translation batch 2026-08-25 13:03:48 +03:00
Lauri Ojansivu 88d31cf17a Continue Dzongkha templates and search translation 2026-08-25 13:03:35 +03:00
Lauri Ojansivu 6e91b6c3e4 Record twenty-fifth Dzongkha translation batch 2026-08-25 13:00:50 +03:00
Lauri Ojansivu e74b5fce6d Continue Dzongkha role and interface translation 2026-08-25 13:00:37 +03:00
Lauri Ojansivu 9cd64cbaaa Record twenty-fourth Dzongkha translation batch 2026-08-25 12:57:56 +03:00
Lauri Ojansivu ec14acf70a Continue Dzongkha HTML and reminder translation 2026-08-25 12:57:43 +03:00
Lauri Ojansivu 22aea05298 Record twenty-third Dzongkha translation batch 2026-08-25 12:54:40 +03:00
Lauri Ojansivu 668c9cd32e Continue Dzongkha rule and branding translation 2026-08-25 12:54:26 +03:00
Lauri Ojansivu 458d746a65 Record twenty-second Dzongkha translation batch 2026-08-25 12:52:03 +03:00
Lauri Ojansivu 60b32029ce Continue Dzongkha automation action translation 2026-08-25 12:51:50 +03:00
Lauri Ojansivu a8a7d0c5de Record twenty-first Dzongkha translation batch 2026-08-25 12:50:10 +03:00
Lauri Ojansivu 02a4b764d5 Continue Dzongkha scheduled automation translation 2026-08-25 12:49:55 +03:00
Lauri Ojansivu 7debc42c4f Record twentieth Dzongkha translation batch 2026-08-25 12:47:54 +03:00
Lauri Ojansivu ee59a1ac88 Continue Dzongkha automation rule translation 2026-08-25 12:47:40 +03:00
Lauri Ojansivu 54fc639dd8 Record nineteenth Dzongkha translation batch 2026-08-25 12:45:15 +03:00
Lauri Ojansivu 03bf26e245 Continue Dzongkha card and subtask translation 2026-08-25 12:45:01 +03:00
Lauri Ojansivu ed50c4ac0f Record eighteenth Dzongkha translation batch 2026-08-25 12:42:40 +03:00
Lauri Ojansivu 2d5e88d77d Continue Dzongkha diagnostics and organization translation 2026-08-25 12:42:27 +03:00
Lauri Ojansivu 402fb55938 Record seventeenth Dzongkha translation batch 2026-08-25 12:39:39 +03:00
Lauri Ojansivu d38d376e00 Continue Dzongkha attachment and email translation 2026-08-25 12:39:25 +03:00
Lauri Ojansivu a1239c2555 Record sixteenth Dzongkha translation batch 2026-08-25 12:36:35 +03:00
Lauri Ojansivu b89e4ed832 Continue Dzongkha tracking and upload translation 2026-08-25 12:36:22 +03:00
Lauri Ojansivu 9ae73baae9 Record fifteenth Dzongkha translation batch 2026-08-25 12:33:34 +03:00
Lauri Ojansivu 2debb0e2da Continue Dzongkha board and shortcut translation 2026-08-25 12:33:21 +03:00
Lauri Ojansivu 4aa7e919c2 Record fourteenth Dzongkha translation batch 2026-08-25 12:29:53 +03:00
Lauri Ojansivu c6a8549b4e Continue Dzongkha list and role translation 2026-08-25 12:29:23 +03:00
Lauri Ojansivu 7d5d2403cb Record thirteenth Dzongkha translation batch 2026-08-25 12:20:53 +03:00
Lauri Ojansivu c501dfcdf5 Continue Dzongkha Trello and member mapping translation 2026-08-25 12:20:35 +03:00
Lauri Ojansivu 5a37482770 Record twelfth Dzongkha translation batch 2026-08-25 12:17:14 +03:00
Lauri Ojansivu 698eef836e Continue Dzongkha filters and board imports translation 2026-08-25 12:16:58 +03:00
Lauri Ojansivu eeaa656d8f Record eleventh Dzongkha translation batch 2026-08-25 12:11:52 +03:00
Lauri Ojansivu d76ccde6c5 Continue Dzongkha exports and filters translation 2026-08-25 12:11:34 +03:00
Lauri Ojansivu 0410b117e7 Record tenth Dzongkha translation batch 2026-08-25 12:08:56 +03:00
Lauri Ojansivu fb40137545 Continue Dzongkha email and error translation 2026-08-25 12:08:38 +03:00
Lauri Ojansivu 0636cd1aa4 Record ninth Dzongkha translation batch 2026-08-25 12:04:46 +03:00
Lauri Ojansivu 67d562029b Continue Dzongkha roles and custom fields translation 2026-08-25 12:04:27 +03:00
Lauri Ojansivu f973f4a9b2 Record eighth Dzongkha translation batch 2026-08-25 12:01:23 +03:00
Lauri Ojansivu f35ecb497e Continue Dzongkha navigation and colors translation 2026-08-25 12:01:02 +03:00
Lauri Ojansivu 469f7a948d Record seventh Dzongkha translation batch 2026-08-25 11:58:48 +03:00
Lauri Ojansivu c4be0d9b86 Continue Dzongkha member and appearance translation 2026-08-25 11:58:28 +03:00
Lauri Ojansivu c479be5b28 Record sixth Dzongkha translation batch 2026-08-25 11:55:45 +03:00
Lauri Ojansivu 2cfe41ec2b Continue Dzongkha voting and import translation 2026-08-25 11:55:28 +03:00
Lauri Ojansivu 31e8bf9147 Record fifth Dzongkha translation batch 2026-08-25 11:53:17 +03:00
Lauri Ojansivu d69443c4cb Continue Dzongkha board views and archives translation 2026-08-25 11:52:55 +03:00
Lauri Ojansivu 632cf8cc49 Record fourth Dzongkha translation batch 2026-08-25 11:50:04 +03:00
Lauri Ojansivu 0930e60b52 Continue Dzongkha board administration translation 2026-08-25 11:49:48 +03:00
Lauri Ojansivu 231c0ae46f Record third Dzongkha translation batch 2026-08-25 11:46:58 +03:00
Lauri Ojansivu 09b6e6d70b Continue Dzongkha workspace and layout translation 2026-08-25 11:46:40 +03:00
Lauri Ojansivu ce6933a667 Record second Dzongkha translation batch 2026-08-25 11:43:11 +03:00
Lauri Ojansivu 98d10bee6f Continue Dzongkha activity and workspace translation 2026-08-25 11:42:48 +03:00
Lauri Ojansivu a85294b116 Record first Dzongkha translation batch 2026-08-25 11:39:57 +03:00
Lauri Ojansivu 8f31871de6 Begin Dzongkha whole-file translation 2026-08-25 11:39:35 +03:00
Lauri Ojansivu 34d6036826 Record completed Chuvash translation 2026-08-25 11:35:16 +03:00
Lauri Ojansivu 42c58ae718 Complete Chuvash whole-file translation 2026-08-25 11:34:49 +03:00
Lauri Ojansivu e740d72037 Record forty-second Chuvash translation batch 2026-08-25 11:33:09 +03:00
Lauri Ojansivu 84f7240d68 Continue Chuvash migration tuning translation 2026-08-25 11:32:51 +03:00
Lauri Ojansivu 10fc6e433f Record forty-first Chuvash translation batch 2026-08-25 11:31:47 +03:00
Lauri Ojansivu cb9006385b Continue Chuvash migration monitoring translation 2026-08-25 11:31:31 +03:00
Lauri Ojansivu 56d09392f8 Record fortieth Chuvash translation batch 2026-08-25 11:30:34 +03:00
Lauri Ojansivu 582ef3cca2 Continue Chuvash board repair translation 2026-08-25 11:29:18 +03:00
Lauri Ojansivu 242e6f662b CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-25 11:29:08 +03:00
Lauri Ojansivu d5b697dfdb Record thirty-ninth Chuvash translation batch 2026-08-25 11:28:34 +03:00
Lauri Ojansivu 3558538116 Continue Chuvash migration controls translation 2026-08-25 11:28:34 +03:00
Lauri Ojansivu 1db3fd8e55 Record thirty-eighth Chuvash translation batch 2026-08-25 11:28:34 +03:00
Lauri Ojansivu 2646a0ae86 Continue Chuvash cloud backup translation 2026-08-25 11:28:34 +03:00
Lauri Ojansivu 8edd8d0c70 Record thirty-seventh Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu dff10fc4c9 Continue Chuvash security and backup translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 19da41ab4f Record thirty-sixth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 2ea587e1b3 Continue Chuvash migrations and storage translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu fbd587bae3 Record thirty-fifth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 6b9844d084 Continue Chuvash account protection translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu b6a0d519e3 Record thirty-fourth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu cd23afbae7 Continue Chuvash uploads and support translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 34ef6cf393 Record thirty-third Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 89f3e38759 Continue Chuvash storage repair translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 0d770889d2 Record thirty-second Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 3609447273 Continue Chuvash organizations and storage translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 7935789bcf Record thirty-first Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 74d6d97015 Continue Chuvash recovery and support translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 5f83c8e60b Record thirtieth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 8d2ffa905b Continue Chuvash locations and reports translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 15221d804a Record twenty-ninth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu b708055c27 Continue Chuvash dependencies and backgrounds translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu e8b4ad0457 Record twenty-eighth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu ce66d24e41 Continue Chuvash global search translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu a419f3429d Record twenty-seventh Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu fcc08752d7 Continue Chuvash search vocabulary translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu dc0f097c9c Record twenty-sixth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 375e51a2cc Continue Chuvash templates and card views translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu bf469f2e41 Record twenty-fifth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 588c2c5ce4 Continue Chuvash roles and notification translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu c0aaa44826 Record twenty-fourth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu c5300e01fc Continue Chuvash reminders and layout translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu a6352c0639 Record twenty-third Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 844b8af713 Continue Chuvash rules and branding translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu f903775ac5 Record twenty-second Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu f7067c50d5 Continue Chuvash rule actions translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 0599ee0187 Record twenty-first Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 1723658256 Continue Chuvash workflow scheduling translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu fa099bef40 Record twentieth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu a2b80b889b Continue Chuvash activity and rules translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 6845ae3556 Record nineteenth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu ef259d8f00 Continue Chuvash card and subtask translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu f160fcf7a9 Record eighteenth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 8be90c9273 Continue Chuvash runtime and tenancy translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 82cc5b853c Record seventeenth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu e6c13ffbc1 Continue Chuvash attachment limits and webhooks translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 7c96c61129 Record sixteenth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 615213404f Continue Chuvash tracking and branding translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu e54f66fcf1 Record fifteenth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu d29a5b185b Continue Chuvash privacy and shortcuts translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 4adc8f02b0 Record fourteenth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu c2ebc314eb Continue Chuvash list actions and board roles translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu f74dd895bf Record thirteenth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 4b1511b57f Continue Chuvash Trello import and member mapping translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 9c4a6dcd9b Record twelfth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu dd5e036450 Continue Chuvash advanced filters and board imports translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 2dff881468 Record eleventh Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu b0799af4bd Continue Chuvash card exports and filters translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 144bbdc777 Record tenth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 310178f2ee Continue Chuvash account emails and import errors translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu d07187193e Record ninth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu f561a0fd93 Continue Chuvash permissions and custom fields translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 87790e3871 Record eighth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu c41801d990 Continue Chuvash navigation and color translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 881b633fca Record seventh Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu f3eb5045de Continue Chuvash member and appearance translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 835d4d041d Record sixth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu cc21db8933 Continue Chuvash voting and imports translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 44888a97b1 Record fifth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 78ddf054d7 Continue Chuvash board views and card controls translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 11fe9348a9 Record fourth Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu da80a33a6f Continue Chuvash board administration translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 401f09957e Record third Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu ee45461200 Continue Chuvash workspace and layout translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu c9692649ae Record second Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu fe2bea3c63 Continue Chuvash activity and workspace translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu d7b64fc90c Record first Chuvash translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 546f5ed189 Begin Chuvash whole-file translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 615773265c Record completed Central Kurdish translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 574627c332 Complete Central Kurdish whole-file translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 9879361f61 Record forty-second Central Kurdish translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu ae1eb8ad42 Continue Central Kurdish migration controls translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 80963e4d3f Record forty-first Central Kurdish translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu b5a958668f Continue Central Kurdish migration monitoring translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 7673248d7d Record fortieth Central Kurdish translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu efc61040a4 Continue Central Kurdish board repair translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 23777ce8a4 Record thirty-ninth Central Kurdish translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 4c820d4dca Continue Central Kurdish storage migration translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu b717cd2a29 Record thirty-eighth Central Kurdish translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu aace170673 Continue Central Kurdish backup and cloud storage translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 517fd84907 Record thirty-seventh Central Kurdish translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 3fd9300593 Continue Central Kurdish security and backup translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 899bf9810c Record thirty-sixth Central Kurdish translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu cb41f10dff Continue Central Kurdish database migration translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu e8e1761ad5 Record thirty-fifth Central Kurdish translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 241af18719 Continue Central Kurdish lockout and scheduling translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 72a40d4932 Record thirty-fourth Central Kurdish translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu fedce366b1 Continue Central Kurdish uploads and support translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu 2fd229faf9 Record thirty-third Central Kurdish translation batch 2026-08-25 11:28:33 +03:00
Lauri Ojansivu bf5f2f0e63 Continue Central Kurdish storage maintenance translation 2026-08-25 11:28:33 +03:00
Lauri Ojansivu e891fae1b6 Record thirty-second Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 2b17697e57 Continue Central Kurdish administration and attachments translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 38dc5b64c4 Record thirty-first Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 089c2eaec5 Continue Central Kurdish recovery and ticket translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu e2fec55974 Record thirtieth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 27150da4cb Continue Central Kurdish locations and reports translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 1cdecb57d2 Record twenty-ninth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 45874bb501 Continue Central Kurdish dependencies and locations translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu d85b1b95ec Record twenty-eighth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu dc14ce1aae Continue Central Kurdish search guidance translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu b8050c5929 Record twenty-seventh Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 6dcd4e3ea2 Continue Central Kurdish search operator translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 5dd81dd6cf Record twenty-sixth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu b6a0285412 Continue Central Kurdish templates and card views translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 87fd013743 Record twenty-fifth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 532ea2565d Continue Central Kurdish roles and notifications translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu a2070197c4 Record twenty-fourth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 511daa2e8c Continue Central Kurdish reminders and layout translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 99bd12f0cf Record twenty-third Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 2f01c67852 Continue Central Kurdish rules and branding translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 3e8a899d12 Record twenty-second Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu f5ffef518d Continue Central Kurdish rule actions translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 8adc3670a5 Record twenty-first Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 862915878e Continue Central Kurdish workflow scheduling translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu c53a73a165 Record twentieth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 387a30bdf4 Continue Central Kurdish activity and rules translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 77e3ec6938 Record nineteenth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu ff176c2f40 Continue Central Kurdish card and subtask translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu b58866a90f Record eighteenth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 021f94d4e7 Continue Central Kurdish runtime and tenancy translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu f47e072eef Record seventeenth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 4991caf568 Continue Central Kurdish SMTP and webhook translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 780cac147e Record sixteenth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 42389b281a Continue Central Kurdish tracking and branding translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 8a928e18c5 Record fifteenth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu ab238b816e Continue Central Kurdish privacy and shortcuts translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 8e2aebfe0c Record fourteenth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 359ef2e37a Continue Central Kurdish list and role translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu bcfb54e774 Record thirteenth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 8dd33301c8 Continue Central Kurdish Trello import translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu c4f5d5a17d Record twelfth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu a1ae1d7d1e Continue Central Kurdish board import translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 7151e63acf Record eleventh Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu b9702a2f53 Continue Central Kurdish export and filter translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu fc25dda15a Record tenth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 2d7364439e Continue Central Kurdish account email translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu e61620e82c Record ninth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 2efd120c84 Continue Central Kurdish roles and fields translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 7009f6f0c7 Record eighth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 183b1dd965 Continue Central Kurdish navigation and colors translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu b8489b3a58 Record seventh Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu eaf27f26b6 Continue Central Kurdish appearance translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 22dc372608 Record sixth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 1381fd30f7 Continue Central Kurdish voting and import translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 21e099e562 Record fifth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 8389975c82 Continue Central Kurdish board views translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 08e8db877f Record fourth Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 6c11c8fe80 Continue Central Kurdish board administration translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 541a862abe Record third Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu a600361382 Continue Central Kurdish workspace translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu e61e94ab64 Record second Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 386a70f284 Continue Central Kurdish activity translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu ea2c4743db Record first Central Kurdish translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 5ffd135b55 Start Central Kurdish whole-file translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 4af48cb511 Record completed Cherokee whole-file translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu a61b14f2e0 Complete Cherokee whole-file translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 3de527c06c Continue Cherokee repository and repair translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 86d655861a Record forty-second Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 0b713688ad Continue Cherokee migration controls translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu f750eebfbd Record forty-first Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 8d97034a3f Continue Cherokee migration monitoring translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 72b03b0c9b Record fortieth Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 6e8d52689d Continue Cherokee board migration translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 066d03bc05 Record thirty-ninth Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 97f2ac4eb3 Continue Cherokee storage migration translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu f5f04c7f5c Record thirty-eighth Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 0abe0ba0b5 Continue Cherokee cloud storage translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 106c25776d Record thirty-seventh Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu b262f9661f Continue Cherokee security and backup translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 79df552826 Record thirty-sixth Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 4680fe36fa Continue Cherokee database migration translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 9853a7fdfc Record thirty-fifth Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 0884ace8bf Continue Cherokee account protection translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 73f5ef72ea Record thirty-fourth Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 15d06c361b Continue Cherokee accessibility translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 54baa5e758 Record thirty-third Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 3e161bb319 Continue Cherokee attachment repair translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 71e6bb8f6f Record thirty-second Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 9ac58afe5e Continue Cherokee organization and storage translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 488b69266a Record thirty-first Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu ef85bcb2c3 Continue Cherokee recovery and support translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 0afd18e199 Record thirtieth Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu f9dc4293d6 Continue Cherokee reports and locations translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu b197b32c69 Record twenty-ninth Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 7227fa01cc Continue Cherokee card metadata translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 5434ad809b Record twenty-eighth Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu d679f46732 Continue Cherokee global search translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 6c377652df Record twenty-seventh Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 2e60bec013 Continue Cherokee search operator translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 481d91639f Record twenty-sixth Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 437a622d73 Continue Cherokee templates and card views translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 62724383e9 Record twenty-fifth Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 65c0eeeb4b Continue Cherokee roles and calendar translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 5dba21cb98 Record twenty-fourth Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 20307961dd Continue Cherokee reminders and layout translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 8b7801f7c6 Record twenty-third Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu a06cc34835 Continue Cherokee automation and manifest translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 4f610bf1a8 Record twenty-second Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu f80737e61d Continue Cherokee automation action translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu fef336f694 Record twenty-first Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu b0f8eead16 Continue Cherokee scheduled automation translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 115c980325 Record twentieth Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 166388340d Continue Cherokee automation rule translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 7de21c695b Record nineteenth Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu 121400d80f Continue Cherokee card settings translation 2026-08-25 11:28:32 +03:00
Lauri Ojansivu aa80d9a518 Record eighteenth Cherokee translation batch 2026-08-25 11:28:32 +03:00
Lauri Ojansivu f6f2b1ebfd Continue Cherokee runtime and tenancy translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 3c495baf98 Record seventeenth Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 66bc7e464d Continue Cherokee attachment and webhook translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 6a840fc691 Record sixteenth Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu c1597fecfa Continue Cherokee tracking and branding translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 4c1159afaa Record fifteenth Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 99bad85a05 Continue Cherokee search and shortcut translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 39d13d24e2 Record fourteenth Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 2e55fc2fad Continue Cherokee list and role translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 8dd1cea634 Record thirteenth Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 504183ee9d Continue Cherokee Trello and member mapping translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 8090e8194f Record twelfth Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu dd25927662 Continue Cherokee filter and import translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu cfb21fc302 Record eleventh Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 761fc439c6 Continue Cherokee export and filter translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 95344b31d9 Record tenth Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 9c43e8a596 Continue Cherokee email and error translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 269a8e7d6d Record ninth Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 8a4e8ea31e Continue Cherokee permissions and custom fields translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu ab3bd2479f Record eighth Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu f61d0fd6bc Continue Cherokee navigation and color translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 696a256558 Record seventh Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 8269f2288e Continue Cherokee member settings translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 502b8d9746 Record sixth Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 6523fc818d Continue Cherokee voting and import translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 97670b04ad Record fifth Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu a144862bca Continue Cherokee board view translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu ace9ab4f59 Record fourth Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 3fa2fa8b22 Continue Cherokee board administration translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 09ee1e31be Record third Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 4095accd5a Continue Cherokee board layout translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 63d994c379 Record second Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 70875a2f8f Continue Cherokee activity translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu ac98845a13 Record first Cherokee translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 41995eecb5 Begin Cherokee whole-file translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 42f94266dd Record completed Buryat whole-file translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 952bbe628a Complete Buryat whole-file translation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 43a3a259a6 Record forty-second Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 4f726ac7d4 Translate Buryat migration tuning and monitoring 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 76b1c92b05 Record forty-first Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 3360041951 Translate Buryat migration steps and monitoring 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 85c0c6a994 Record fortieth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu dbe624f2fa Translate Buryat board integrity migrations 2026-08-25 11:28:31 +03:00
Lauri Ojansivu da2aeafb14 Record thirty-ninth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu ce0a29af65 Translate Buryat storage migration controls 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 6592d9fe17 Record thirty-eighth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 6de634593b Translate Buryat backup and cloud storage guidance 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 93ac389959 Record thirty-seventh Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 63bd048320 Translate Buryat security and backup settings 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 0fd893157f Record thirty-sixth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 34d2cbfe8f Translate Buryat migrations and storage settings 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 449097ee20 Record thirty-fifth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu f8e2decc76 Translate Buryat lockout and scheduled jobs 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 8082e59343 Record thirty-fourth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 796128ad6d Translate Buryat uploads and accessibility 2026-08-25 11:28:31 +03:00
Lauri Ojansivu b340b42b8a Record thirty-third Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 685d6a9a47 Translate Buryat attachment repair and compaction 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 1961822253 Record thirty-second Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 896624d7d5 Translate Buryat teams and attachment storage 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 119a4ccc97 Record thirty-first Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 5246f96494 Translate Buryat recovery and support reports 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 02a456a758 Record thirtieth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 2f8d8cdccc Translate Buryat maps and reports 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 7bae685146 Record twenty-ninth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu ae635e85eb Translate Buryat dependencies and backgrounds 2026-08-25 11:28:31 +03:00
Lauri Ojansivu b0dfc670e0 Record twenty-eighth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 1ace0e1c76 Translate Buryat global search guidance 2026-08-25 11:28:31 +03:00
Lauri Ojansivu f28c633731 Record twenty-seventh Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 53bca43262 Translate Buryat search operators 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 536b580847 Record twenty-sixth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu bd2c7feb49 Translate Buryat shared templates and card views 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 360088a103 Record twenty-fifth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 819bc52722 Translate Buryat roles and calendar settings 2026-08-25 11:28:31 +03:00
Lauri Ojansivu c4b9f45483 Record twenty-fourth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu dde34002f1 Translate Buryat customization and reminders 2026-08-25 11:28:31 +03:00
Lauri Ojansivu c18cd0df13 Record twenty-third Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu d25690510a Translate Buryat rule details and branding 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 7c6fba8b47 Record twenty-second Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu d222329266 Translate Buryat automation actions 2026-08-25 11:28:31 +03:00
Lauri Ojansivu ecb2145124 Record twenty-first Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 8cfc18435d Translate Buryat scheduled automation 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 2d748a2107 Record twentieth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 5c4b958c35 Translate Buryat activity and automation rules 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 7e71736981 Record nineteenth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu a7e41f1264 Translate Buryat card dates and subtask settings 2026-08-25 11:28:31 +03:00
Lauri Ojansivu f6ad72a042 Record eighteenth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu d73d5e6c28 Translate Buryat diagnostics and organizations 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 8ce881a793 Record seventeenth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 414481694e Translate Buryat attachment and webhook settings 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 4d78067376 Record sixteenth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 8c844c13d1 Translate Buryat tracking and branding controls 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 45318160fe Record fifteenth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu b1b1eba461 Translate Buryat visibility and shortcut controls 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 511dcee318 Record fourteenth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 1a9afb116f Translate Buryat membership and selection controls 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 5f326bd2a0 Record thirteenth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 2aadb1d4da Translate Buryat Trello import controls 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 1bc9c88e2f Record twelfth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu cbe2111529 Translate Buryat filters and board imports 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 7e976d1a89 Record eleventh Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu b23fa6babb Translate Buryat exports and filters 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 98ade15feb Record tenth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 3406c732bd Translate Buryat email and error messages 2026-08-25 11:28:31 +03:00
Lauri Ojansivu c9341a54e3 Record ninth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 8cbddb7f0d Translate Buryat roles and custom fields 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 45f9b1ca41 Record eighth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 31491ee042 Translate Buryat layout and color controls 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 2bb9c238f1 Record seventh Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu aa8b6f3fa3 Translate Buryat member and theme controls 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 8e85f31e7d Record sixth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu f13ad0ebcf Translate Buryat voting and import controls 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 4e8c6491a1 Record fifth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 6182e2feaf Translate Buryat board views and archives 2026-08-25 11:28:31 +03:00
Lauri Ojansivu 878e27477b Record fourth Buryat translation batch 2026-08-25 11:28:31 +03:00
Lauri Ojansivu efbcc56afd Translate Buryat board administration 2026-08-25 11:28:31 +03:00
Lauri Ojansivu c0cf9942c4 Record third Buryat translation batch 2026-08-25 11:28:30 +03:00
Lauri Ojansivu 6536c5f628 Translate Buryat workspace controls 2026-08-25 11:28:30 +03:00
Lauri Ojansivu 45f186a795 Record second Buryat translation batch 2026-08-25 11:28:30 +03:00
Lauri Ojansivu 343cddcfcf Continue Buryat activity translation 2026-08-25 11:28:30 +03:00
Lauri Ojansivu f20bd18c11 Record first Buryat whole-file batch 2026-08-25 11:28:30 +03:00
Lauri Ojansivu 07aba2ac26 Begin Buryat whole-file translation 2026-08-25 11:28:30 +03:00
Lauri Ojansivu 9c4592eb7e Record completed Tibetan whole-file translation 2026-08-25 11:28:30 +03:00
Lauri Ojansivu 309abc90a2 Complete Tibetan whole-file translation 2026-08-25 11:28:30 +03:00
Lauri Ojansivu 1cb96a6304 Record Tibetan migration controls progress 2026-08-25 11:28:30 +03:00
Lauri Ojansivu 93535cdc6d Continue Tibetan migration controls translation 2026-08-25 11:28:30 +03:00
Lauri Ojansivu d319561af4 Record Tibetan migration monitoring progress 2026-08-25 11:28:30 +03:00
Lauri Ojansivu 70a65e50de Continue Tibetan migration monitoring translation 2026-08-25 11:28:30 +03:00
Lauri Ojansivu 5f7a1b4aaa Record Tibetan board migration progress 2026-08-25 11:28:30 +03:00
Lauri Ojansivu 957ac00e2b Continue Tibetan board migration translation 2026-08-25 11:28:30 +03:00
Lauri Ojansivu f4ef44ef47 Record Tibetan storage migration progress 2026-08-25 11:28:30 +03:00
Lauri Ojansivu 284ab7ef6a Continue Tibetan storage migration translation 2026-08-25 11:28:30 +03:00
Lauri Ojansivu c2aff94590 Record Tibetan cloud backup progress 2026-08-25 11:28:30 +03:00
Lauri Ojansivu c0b98423d3 Continue Tibetan cloud backup translation 2026-08-25 11:28:30 +03:00
Lauri Ojansivu 372bbbf6f8 Record Tibetan security translation progress 2026-08-25 11:28:30 +03:00
Lauri Ojansivu 0e5932c6d6 Continue Tibetan security translation 2026-08-25 11:28:30 +03:00
Lauri Ojansivu e41d4ebdf9 Record Tibetan migration translation progress 2026-08-25 11:28:30 +03:00
Lauri Ojansivu 8ed8e55fab Continue Tibetan migration translation 2026-08-25 11:28:30 +03:00
github-actions[bot] b67b246018 Bump versions for v11.11 2026-08-25 01:43:11 +00:00
Lauri Ojansivu 5d0b0d84a5 Prepare v11.11 release 2026-08-25 04:41:38 +03:00
Lauri Ojansivu 2d625f68ed Reorganize the upcoming release notes 2026-08-25 04:37:57 +03:00
Lauri Ojansivu 403a0ad7a2 Clarify changelog summary hierarchy 2026-08-25 04:37:29 +03:00
Lauri Ojansivu 0ef4d2787b Add Free Pascal design to the upcoming release notes 2026-08-25 04:17:17 +03:00
Lauri Ojansivu 5d33681e55 Document the Free Pascal rewrite option 2026-08-25 04:16:57 +03:00
Lauri Ojansivu ab04dd2582 Add Go design to the upcoming release notes 2026-08-25 04:12:58 +03:00
Lauri Ojansivu 037eb3144d Document the Go rewrite option 2026-08-25 04:12:22 +03:00
Lauri Ojansivu af93477fc3 Add Haxe design to the upcoming release notes
Link the new Multiverse analysis and summarize its dependency mapping and prototype recommendation.

Thanks to xet7 !
2026-08-25 04:07:52 +03:00
Lauri Ojansivu b649189f46 Document the Haxe rewrite option
Compare incremental and full Haxe migration paths, their benefits and risks, and realistic equivalents for WeKan's platform, UI, integration, testing and packaging dependencies.

Thanks to xet7 !
2026-08-25 04:07:09 +03:00
Lauri Ojansivu b72e0e99fa Isolate Red Strings browser sessions
Give every Red Strings test its own resume token so tearing down an earlier browser connection cannot strand a later Firefox login before its assertion runs.

Thanks to xet7 !
2026-08-25 03:37:20 +03:00
Lauri Ojansivu c31e640593 Run Node E2E with compatible Chromium
Route the Puppeteer regression harness through the existing Playwright container when browsers require Docker, so Linux ARM64 does not fall back to a missing /usr/bin/chromium.

Thanks to xet7 !
2026-08-25 03:03:31 +03:00
Lauri Ojansivu af1924567b Repair all-tests regressions
Use the suite's own Node executable for translation subprocesses, account for the placeholder repair helper and current translation totals, and activate the Table filter after the view-change reload in its browser regression test.

Thanks to xet7 !
2026-08-25 02:26:22 +03:00
Lauri Ojansivu 4bf68e8c3d Sandbox: vscode to vscodium.
Thanks to xet7 !
2026-08-25 01:50:49 +03:00
Lauri Ojansivu 7783497a6f Updated dependencies.
Thanks to developers of dependencies !
2026-08-25 01:42:23 +03:00
Lauri Ojansivu a495d7d890 Keep All Boards tests compatible with Add Board action 2026-08-25 01:26:28 +03:00
Lauri Ojansivu a1dfa88b49 Report blocked security attempts
Add bounded, attributed Problems events for denied MiniProfileBleed, PositionHistoryBleed, and CasBleed attempts. Keep UserSearchBleed and SubtaskExportBleed silent because their safe paths are indistinguishable from ordinary use. Also prevent clients from rewriting validated position history.
2026-08-25 01:26:28 +03:00
Lauri Ojansivu ceb6f3e629 Document All Boards and Table view fixes 2026-08-25 01:14:53 +03:00
Lauri Ojansivu bdec1744f4 Apply board filters in Table view
Build the Table card query from the active board Filter and an immutable board and archive scope, while retaining the view-specific text search.

Thanks to rmb82 and xet7 !

Fixes #6629,
2026-08-25 01:14:39 +03:00
Lauri Ojansivu aae4e81836 Add board creation to All Boards Table view
Use the same creation handler and section rules as the Lists view, including template containers and the Archive and Home exclusions.

Thanks to rmb82 and xet7 !

Fixes #6628,
2026-08-25 01:13:58 +03:00
Lauri Ojansivu a77340b91b Document security advisories 2026-08-25 01:07:52 +03:00
Lauri Ojansivu 4ce5cf6e10 Fix six reported security issues
Authenticate and minimize user publications, escape and throttle user search, enforce board boundaries for position history and exports, and require explicit CAS account linking.

Thanks to Char0n1507, Reload3d, crypto-nidh and xet7 !
2026-08-25 01:04:52 +03:00
Lauri Ojansivu 343340444b Updated dependencies.
Thanks to developers of dependencies !
2026-08-25 00:27:21 +03:00
Lauri Ojansivu 21f50a620d Record Tibetan batch thirty-five checkpoint. 2026-08-24 23:22:54 +03:00
Lauri Ojansivu d8745d70a4 Continue Tibetan lockout translation. 2026-08-24 23:21:29 +03:00
Lauri Ojansivu 463d021957 Record thirty-four Tibetan whole-file batches. 2026-08-24 23:13:21 +03:00
Lauri Ojansivu d521f2918c Continue Tibetan support translation. 2026-08-24 23:12:45 +03:00
Lauri Ojansivu 85415ec40a Continue Tibetan storage translation. 2026-08-24 23:09:44 +03:00
Lauri Ojansivu 2eb4f6bbe2 Record thirty-two Tibetan whole-file batches. 2026-08-24 23:06:49 +03:00
Lauri Ojansivu eafdb9926f Continue Tibetan administration translation. 2026-08-24 23:06:10 +03:00
Lauri Ojansivu f9c4d24b28 Continue Tibetan recovery translation. 2026-08-24 23:03:07 +03:00
Lauri Ojansivu 1e9e3aa68f Record thirty Tibetan whole-file batches. 2026-08-24 23:00:27 +03:00
Lauri Ojansivu 068e47ff45 Continue Tibetan report translation. 2026-08-24 22:59:50 +03:00
Lauri Ojansivu a0e5a40540 Continue Tibetan dependency translation. 2026-08-24 22:57:16 +03:00
Lauri Ojansivu 329d7d9d05 Record twenty-eight Tibetan whole-file batches. 2026-08-24 22:54:54 +03:00
Lauri Ojansivu 49c7d68270 Continue Tibetan search-help translation. 2026-08-24 22:54:21 +03:00
Lauri Ojansivu 4b4836376c Continue Tibetan search translation. 2026-08-24 22:50:10 +03:00
Lauri Ojansivu 7a4766e0c7 Record twenty-six Tibetan whole-file batches. 2026-08-24 22:48:21 +03:00
Lauri Ojansivu 6126599fad Continue Tibetan shared-view translation. 2026-08-24 22:47:47 +03:00
Lauri Ojansivu 099eb4c1b9 Continue Tibetan board-role translation. 2026-08-24 22:44:42 +03:00
Lauri Ojansivu ed632b79e3 Record twenty-four Tibetan whole-file batches. 2026-08-24 22:41:05 +03:00
Lauri Ojansivu 729433cdff Continue Tibetan activity translation. 2026-08-24 22:39:38 +03:00
Lauri Ojansivu eef4af62c2 Continue Tibetan configuration translation. 2026-08-24 22:36:12 +03:00
Lauri Ojansivu 245e5e0adf Record twenty-two Tibetan whole-file batches. 2026-08-24 22:32:44 +03:00
Lauri Ojansivu 3c32da7382 Continue Tibetan rule actions translation. 2026-08-24 22:32:06 +03:00
Lauri Ojansivu 9b28497981 Continue Tibetan scheduled automation translation. 2026-08-24 22:29:42 +03:00
Lauri Ojansivu a1d3c41863 Record twenty Tibetan whole-file batches. 2026-08-24 22:27:24 +03:00
Lauri Ojansivu e2f29fe275 Continue Tibetan automation translation. 2026-08-24 22:26:44 +03:00
Lauri Ojansivu d88ef05094 Continue Tibetan card settings translation. 2026-08-24 22:24:09 +03:00
Lauri Ojansivu fdc1ad4371 Record eighteen Tibetan whole-file batches. 2026-08-24 22:21:31 +03:00
Lauri Ojansivu 5fade33b4e Continue Tibetan diagnostics and tenancy translation. 2026-08-24 22:20:56 +03:00
Lauri Ojansivu 906580d92c Continue Tibetan limits and webhooks translation. 2026-08-24 22:18:24 +03:00
Lauri Ojansivu d683baeb56 Record sixteen Tibetan whole-file batches. 2026-08-24 22:15:44 +03:00
Lauri Ojansivu c4d4607ab8 Continue Tibetan uploads and templates translation. 2026-08-24 22:15:09 +03:00
Lauri Ojansivu 0ed929eb94 Continue Tibetan search and shortcuts translation. 2026-08-24 22:12:16 +03:00
Lauri Ojansivu a9e48c9fe5 Record fourteen Tibetan whole-file batches. 2026-08-24 22:09:03 +03:00
Lauri Ojansivu ebf8522676 Continue Tibetan archives and selection translation. 2026-08-24 22:08:21 +03:00
Lauri Ojansivu 2cba985330 Continue Tibetan Trello import translation. 2026-08-24 22:05:38 +03:00
Lauri Ojansivu 1ac31db01d Record twelve Tibetan whole-file batches. 2026-08-24 22:02:33 +03:00
Lauri Ojansivu d70168bcdb Continue Tibetan import guidance translation. 2026-08-24 22:01:54 +03:00
Lauri Ojansivu 7f3542b17a Continue Tibetan export and filtering translation. 2026-08-24 21:57:34 +03:00
Lauri Ojansivu 469511fe23 Record ten Tibetan whole-file batches. 2026-08-24 21:55:02 +03:00
Lauri Ojansivu 337ebfdacb Continue Tibetan email and error translation. 2026-08-24 21:54:27 +03:00
Lauri Ojansivu 2868da3020 Continue Tibetan roles and custom fields translation. 2026-08-24 21:51:10 +03:00
Lauri Ojansivu 925dd72c4e Record eight Tibetan whole-file batches. 2026-08-24 21:48:28 +03:00
Lauri Ojansivu 69eccdbdeb Continue Tibetan navigation and colors translation. 2026-08-24 21:47:56 +03:00
Lauri Ojansivu 2f46492df4 Continue Tibetan appearance and mapping translation. 2026-08-24 21:45:49 +03:00
Lauri Ojansivu 4d48127716 Record six Tibetan whole-file batches. 2026-08-24 21:42:52 +03:00
Lauri Ojansivu 82254ec7f2 Continue Tibetan voting and import translation. 2026-08-24 21:42:16 +03:00
Lauri Ojansivu b58d68794f Continue Tibetan board views translation. 2026-08-24 21:40:03 +03:00
Lauri Ojansivu c3ab43a39c Record four Tibetan whole-file batches. 2026-08-24 21:37:12 +03:00
Lauri Ojansivu f9de5c5fd5 Continue Tibetan board archive translation. 2026-08-24 21:36:37 +03:00
Lauri Ojansivu 1c977c7b0f Continue Tibetan board layout translation. 2026-08-24 21:33:59 +03:00
Lauri Ojansivu 675401a3e5 Record two Tibetan whole-file batches. 2026-08-24 21:30:51 +03:00
Lauri Ojansivu efd5a918cf Continue Tibetan workspace translation. 2026-08-24 21:30:18 +03:00
Lauri Ojansivu cb16fe49d9 Begin Tibetan whole-file translation. 2026-08-24 21:27:54 +03:00
Lauri Ojansivu bd5f86320f Record completed Bengali whole-file translation. 2026-08-24 21:23:31 +03:00
Lauri Ojansivu f75e20562c Complete Bengali whole-file translation. 2026-08-24 21:22:57 +03:00
Lauri Ojansivu 685eab566f Continue Bengali account and monitoring translation. 2026-08-24 21:21:53 +03:00
Lauri Ojansivu b2b7c0d22e Record forty-two Bengali whole-file batches. 2026-08-24 21:20:15 +03:00
Lauri Ojansivu c8b62e6e63 Continue Bengali migration controls translation. 2026-08-24 21:19:43 +03:00
Lauri Ojansivu 392246c631 Continue Bengali migration monitoring translation. 2026-08-24 21:18:09 +03:00
Lauri Ojansivu 9b89082723 Record forty Bengali whole-file batches. 2026-08-24 21:16:33 +03:00
Lauri Ojansivu 9bf2bc7910 Continue Bengali board repair translation. 2026-08-24 21:16:00 +03:00
Lauri Ojansivu 621729fd49 Continue Bengali storage migration translation. 2026-08-24 21:13:51 +03:00
Lauri Ojansivu e584d03c25 Record thirty-eight Bengali whole-file batches. 2026-08-24 21:12:15 +03:00
Lauri Ojansivu 73a108ed47 Continue Bengali cloud backup translation. 2026-08-24 21:11:39 +03:00
Lauri Ojansivu 8af5acc9db Continue Bengali security and backup translation. 2026-08-24 21:09:50 +03:00
Lauri Ojansivu 816e0214d2 Record thirty-six Bengali whole-file batches. 2026-08-24 21:06:23 +03:00
Lauri Ojansivu 66456dd38e Continue Bengali migration translation. 2026-08-24 21:05:23 +03:00
Lauri Ojansivu 75ca2fd574 Continue Bengali administration translation. 2026-08-24 21:03:32 +03:00
Lauri Ojansivu 0c710d4f57 Record thirty-four Bengali whole-file batches. 2026-08-24 21:00:54 +03:00
Lauri Ojansivu 8657b0f223 Continue Bengali upload and accessibility translation. 2026-08-24 20:59:32 +03:00
Lauri Ojansivu df7ab3c63a Continue Bengali storage repair translation. 2026-08-24 20:57:51 +03:00
Lauri Ojansivu b798bc6d7d Record thirty-two Bengali whole-file batches. 2026-08-24 20:55:43 +03:00
Lauri Ojansivu e2d839b639 Continue Bengali diagnostics and storage translation. 2026-08-24 20:53:48 +03:00
Lauri Ojansivu 7400196ca1 Continue Bengali recovery and ticket translation. 2026-08-24 20:52:09 +03:00
Lauri Ojansivu bc999ef31d Record thirty Bengali whole-file batches. 2026-08-24 20:50:16 +03:00
Lauri Ojansivu 9330bf65d1 Continue Bengali map and report translation. 2026-08-24 20:49:23 +03:00
Lauri Ojansivu 868db26409 Continue Bengali dependency and location translation. 2026-08-24 20:47:39 +03:00
Lauri Ojansivu fbce91d896 Record twenty-eight Bengali whole-file batches. 2026-08-24 20:45:54 +03:00
Lauri Ojansivu 633c3115d0 Continue Bengali global-search help translation. 2026-08-24 20:44:35 +03:00
Lauri Ojansivu 8f3c7ffc77 Continue Bengali global-search translation. 2026-08-24 20:42:22 +03:00
Lauri Ojansivu 2ac3ed9e26 Record twenty-six Bengali whole-file batches. 2026-08-24 20:40:51 +03:00
Lauri Ojansivu 94be3426fd Continue Bengali templates and search translation. 2026-08-24 20:40:01 +03:00
Lauri Ojansivu aac0cc636e Continue Bengali role and editor translation. 2026-08-24 20:38:13 +03:00
Lauri Ojansivu 9ea0e7b1a1 Record twenty-four Bengali whole-file batches. 2026-08-24 20:36:31 +03:00
Lauri Ojansivu a44514ff7c Continue Bengali reminder and layout translation. 2026-08-24 20:35:33 +03:00
Lauri Ojansivu 59b153d0dd Continue Bengali automation and custom-head translation. 2026-08-24 20:33:46 +03:00
Lauri Ojansivu 8800c51e61 Record twenty-two Bengali whole-file batches. 2026-08-24 20:31:50 +03:00
Lauri Ojansivu bafe6ee333 Continue Bengali automation-action translation. 2026-08-24 20:30:58 +03:00
Lauri Ojansivu 4e0b0361c9 Continue Bengali scheduled-automation translation. 2026-08-24 20:29:29 +03:00
Lauri Ojansivu 53c516249d Record twenty Bengali whole-file batches. 2026-08-24 20:27:57 +03:00
Lauri Ojansivu d5bcdc722e Continue Bengali automation workflow translation. 2026-08-24 20:27:11 +03:00
Lauri Ojansivu 7be653a057 Continue Bengali card and subtask translation. 2026-08-24 20:25:35 +03:00
Lauri Ojansivu 09b45273b5 Record eighteen Bengali whole-file batches. 2026-08-24 20:23:45 +03:00
Lauri Ojansivu bb33a9c63a Continue Bengali diagnostics and organization translation. 2026-08-24 20:22:50 +03:00
Lauri Ojansivu 3e5c9fb228 Continue Bengali attachment and webhook translation. 2026-08-24 20:21:12 +03:00
Lauri Ojansivu d7d41f100b Record sixteen Bengali whole-file batches. 2026-08-24 20:19:27 +03:00
Lauri Ojansivu 8d1b061c2e Continue Bengali tracking and branding translation. 2026-08-24 20:18:38 +03:00
Lauri Ojansivu d4cfc2cf1d Continue Bengali visibility and shortcut translation. 2026-08-24 20:16:58 +03:00
Lauri Ojansivu a0e2e22a1d Record fourteen Bengali whole-file batches. 2026-08-24 20:14:57 +03:00
Lauri Ojansivu b60072a2fc Continue Bengali archive and selection translation. 2026-08-24 20:13:46 +03:00
Lauri Ojansivu 52bad0ab2f Continue Bengali Trello import translation. 2026-08-24 20:12:06 +03:00
Lauri Ojansivu 451b2e642c Record twelve Bengali whole-file batches. 2026-08-24 20:10:16 +03:00
Lauri Ojansivu 30a566ec6d Continue Bengali advanced-filter and import translation. 2026-08-24 20:09:26 +03:00
Lauri Ojansivu 89eb712c58 Continue Bengali export and filter translation. 2026-08-24 20:07:03 +03:00
Lauri Ojansivu 107d55041a Record ten Bengali whole-file batches. 2026-08-24 20:04:47 +03:00
Lauri Ojansivu 6014df2bec Continue Bengali email and error translation. 2026-08-24 20:03:59 +03:00
Lauri Ojansivu a5d30b791a Continue Bengali role and custom-field translation. 2026-08-24 20:01:31 +03:00
Lauri Ojansivu e85c27d54c Record eight Bengali whole-file batches. 2026-08-24 19:59:45 +03:00
Lauri Ojansivu 33a240462d Continue Bengali navigation and color translation. 2026-08-24 19:58:54 +03:00
Lauri Ojansivu 4645d6a12f Continue Bengali member and appearance translation. 2026-08-24 19:57:24 +03:00
Lauri Ojansivu f5d69585a7 Record six Bengali whole-file batches. 2026-08-24 19:55:31 +03:00
Lauri Ojansivu 1e57724fe0 Continue Bengali voting and import translation. 2026-08-24 19:54:41 +03:00
Lauri Ojansivu c7d67ee342 Continue Bengali board-view translation. 2026-08-24 19:53:15 +03:00
Lauri Ojansivu 90394da193 Record four Bengali whole-file batches. 2026-08-24 19:51:26 +03:00
Lauri Ojansivu e7f7d95ffb Continue Bengali archive and visibility translation. 2026-08-24 19:50:34 +03:00
Lauri Ojansivu 3792cc9224 Continue Bengali board-control translation. 2026-08-24 19:49:00 +03:00
Lauri Ojansivu f26995f9ee Record two Bengali whole-file batches. 2026-08-24 19:47:07 +03:00
Lauri Ojansivu 88cf43f3a5 Continue Bengali activity and workspace translation. 2026-08-24 19:45:53 +03:00
Lauri Ojansivu 8b4168928a Begin Bengali whole-file translation. 2026-08-24 19:43:55 +03:00
Lauri Ojansivu fcd0195d37 Record completed Bambara whole-file translation. 2026-08-24 19:41:05 +03:00
Lauri Ojansivu 926ff59d29 Complete Bambara whole-file translation. 2026-08-24 19:40:15 +03:00
Lauri Ojansivu bb7d42fca0 Continue Bambara account and event translation. 2026-08-24 19:39:01 +03:00
Lauri Ojansivu fc0d92043f Record forty-two Bambara whole-file batches. 2026-08-24 19:37:21 +03:00
Lauri Ojansivu fb5eab7b5e Continue Bambara migration monitoring translation. 2026-08-24 19:36:34 +03:00
Lauri Ojansivu f7a8bad2f0 Continue Bambara migration diagnostics translation. 2026-08-24 19:35:23 +03:00
Lauri Ojansivu b6c718c463 Record forty Bambara whole-file batches. 2026-08-24 19:33:53 +03:00
Lauri Ojansivu db205e56ba Continue Bambara board-repair translation. 2026-08-24 19:31:51 +03:00
Lauri Ojansivu 6352abab1e Continue Bambara migration-control translation. 2026-08-24 19:28:50 +03:00
Lauri Ojansivu ce46dc153a Record thirty-eight Bambara whole-file batches. 2026-08-24 19:27:07 +03:00
Lauri Ojansivu 8697781435 Continue Bambara cloud-backup translation. 2026-08-24 19:26:28 +03:00
Lauri Ojansivu 6cd50d1e83 Continue Bambara security-control translation. 2026-08-24 19:24:46 +03:00
Lauri Ojansivu bc9a9d0ad0 Record thirty-six Bambara whole-file batches. 2026-08-24 19:21:55 +03:00
Lauri Ojansivu 2552328f43 Continue Bambara database-migration translation. 2026-08-24 19:21:14 +03:00
Lauri Ojansivu 115860bbb5 Continue Bambara account-protection translation. 2026-08-24 19:19:42 +03:00
Lauri Ojansivu 453d1d5aa6 Record thirty-four Bambara whole-file batches. 2026-08-24 19:17:56 +03:00
Lauri Ojansivu 74faf789db Continue Bambara support translation. 2026-08-24 19:17:19 +03:00
Lauri Ojansivu 5f5f4c78b2 Continue Bambara storage-repair translation. 2026-08-24 19:16:01 +03:00
Lauri Ojansivu c7455c9fa0 Record thirty-two Bambara whole-file batches. 2026-08-24 19:14:14 +03:00
Lauri Ojansivu df05278e7c Continue Bambara storage translation. 2026-08-24 19:13:38 +03:00
Lauri Ojansivu ff6eefc3ea Continue Bambara recovery translation. 2026-08-24 19:12:21 +03:00
Lauri Ojansivu 0c3e2de3b7 Record thirty Bambara whole-file batches. 2026-08-24 19:10:42 +03:00
Lauri Ojansivu eaa812a7fc Continue Bambara report translation. 2026-08-24 19:10:04 +03:00
Lauri Ojansivu 185221c6cd Continue Bambara dependency translation. 2026-08-24 19:08:53 +03:00
Lauri Ojansivu 8fe98726d5 Record twenty-eight Bambara whole-file batches. 2026-08-24 19:06:58 +03:00
Lauri Ojansivu 93a39f4170 Continue Bambara search-help translation. 2026-08-24 19:06:20 +03:00
Lauri Ojansivu 9cfd6b435a Continue Bambara search-operator translation. 2026-08-24 19:04:36 +03:00
Lauri Ojansivu b849cb909e Record twenty-six Bambara whole-file batches. 2026-08-24 19:02:40 +03:00
Lauri Ojansivu 411663d0d7 Continue Bambara search-view translation. 2026-08-24 19:02:04 +03:00
Lauri Ojansivu 84e821ac2f Continue Bambara role-status translation. 2026-08-24 19:00:49 +03:00
Lauri Ojansivu c1845fc123 Record twenty-four Bambara whole-file batches. 2026-08-24 18:59:14 +03:00
Lauri Ojansivu 16fb661795 Continue Bambara activity translation. 2026-08-24 18:58:39 +03:00
Lauri Ojansivu d5d40e758c Continue Bambara rule-settings translation. 2026-08-24 18:57:26 +03:00
Lauri Ojansivu 60941b2544 Record twenty-two Bambara whole-file batches. 2026-08-24 18:55:54 +03:00
Lauri Ojansivu 19ca9756b4 Continue Bambara rule-action translation. 2026-08-24 18:55:21 +03:00
Lauri Ojansivu f16b1dac04 Continue Bambara trigger translation. 2026-08-24 18:54:14 +03:00
Lauri Ojansivu c2ac8163b8 Record twenty Bambara whole-file batches. 2026-08-24 18:52:42 +03:00
Lauri Ojansivu 6e649e9401 Continue Bambara automation translation. 2026-08-24 18:52:04 +03:00
Lauri Ojansivu 98aec264bb Continue Bambara card-settings translation. 2026-08-24 18:50:56 +03:00
Lauri Ojansivu 5d565257d5 Record eighteen Bambara whole-file batches. 2026-08-24 18:49:23 +03:00
Lauri Ojansivu 889f15ee59 Continue Bambara diagnostics translation. 2026-08-24 18:48:50 +03:00
Lauri Ojansivu 0035fcd0cb Continue Bambara administration translation. 2026-08-24 18:46:37 +03:00
Lauri Ojansivu d701b70396 Record sixteen Bambara whole-file batches. 2026-08-24 18:44:53 +03:00
Lauri Ojansivu 9a17c8ac92 Continue Bambara tracking translation. 2026-08-24 18:44:20 +03:00
Lauri Ojansivu 2c6be34a6c Continue Bambara shortcut translation. 2026-08-24 18:42:57 +03:00
Lauri Ojansivu 31d8e76d49 Record fourteen Bambara whole-file batches. 2026-08-24 18:41:00 +03:00
Lauri Ojansivu f9a5755614 Continue Bambara selection translation. 2026-08-24 18:40:24 +03:00
Lauri Ojansivu f4cbf2c574 Continue Bambara Trello translation. 2026-08-24 18:39:09 +03:00
Lauri Ojansivu f2c47b8bda Record twelve Bambara whole-file batches. 2026-08-24 18:37:30 +03:00
Lauri Ojansivu 4e2632fc16 Continue Bambara import translation. 2026-08-24 18:36:56 +03:00
Lauri Ojansivu aff3577490 Continue Bambara export translation. 2026-08-24 18:35:08 +03:00
Lauri Ojansivu 994425baa5 Record ten Bambara whole-file batches. 2026-08-24 18:33:37 +03:00
Lauri Ojansivu 1cc76f8856 Continue Bambara email translation. 2026-08-24 18:33:02 +03:00
Lauri Ojansivu d9b50b13b0 Continue Bambara role translation. 2026-08-24 18:31:34 +03:00
Lauri Ojansivu 783df54137 Record eight Bambara whole-file batches. 2026-08-24 18:29:55 +03:00
Lauri Ojansivu 5b0d1b728f Continue Bambara navigation translation. 2026-08-24 18:29:18 +03:00
Lauri Ojansivu 2a0c101c87 Continue Bambara appearance translation. 2026-08-24 18:28:04 +03:00
Lauri Ojansivu 5cf106cd7b Record six Bambara whole-file batches. 2026-08-24 18:26:25 +03:00
Lauri Ojansivu c5de4e7839 Continue Bambara voting translation. 2026-08-24 18:25:50 +03:00
Lauri Ojansivu be89ad924e Continue Bambara board-view translation. 2026-08-24 18:24:41 +03:00
Lauri Ojansivu 410ab132ae Record four Bambara whole-file batches. 2026-08-24 18:22:20 +03:00
Lauri Ojansivu 3e43499da9 Continue Bambara archive translation. 2026-08-24 18:18:40 +03:00
Lauri Ojansivu 4111b6f7d8 Continue Bambara board translation. 2026-08-24 18:17:17 +03:00
Lauri Ojansivu dca987ac44 Record two Bambara whole-file batches. 2026-08-24 18:14:23 +03:00
Lauri Ojansivu 37faf9feac Continue Bambara workspace translation. 2026-08-24 18:13:46 +03:00
Lauri Ojansivu 063ea01600 Begin Bambara whole-file translation. 2026-08-24 18:12:24 +03:00
Lauri Ojansivu 58dec4a963 Record Bhojpuri whole-file completion. 2026-08-24 18:10:05 +03:00
Lauri Ojansivu 16fe17bb48 Complete Bhojpuri whole-file translation. 2026-08-24 18:09:12 +03:00
Lauri Ojansivu faf4515317 Continue Bhojpuri problem monitoring translation. 2026-08-24 18:07:40 +03:00
Lauri Ojansivu c41e40b480 Record forty-two Bhojpuri whole-file batches. 2026-08-24 18:05:44 +03:00
Lauri Ojansivu 813969eaf5 Continue Bhojpuri migration monitoring translation. 2026-08-24 18:05:06 +03:00
Lauri Ojansivu 62398f85e6 Continue Bhojpuri conversion monitoring translation. 2026-08-24 18:03:31 +03:00
Lauri Ojansivu 24601b5d4b Record forty Bhojpuri whole-file batches. 2026-08-24 18:01:49 +03:00
Lauri Ojansivu 1525d01820 Continue Bhojpuri board migration translation. 2026-08-24 18:01:11 +03:00
Lauri Ojansivu a0f1a38692 Continue Bhojpuri migration controls translation. 2026-08-24 17:59:03 +03:00
Lauri Ojansivu fbd3007b67 Record thirty-eight Bhojpuri whole-file batches. 2026-08-24 17:57:07 +03:00
Lauri Ojansivu f3f7a810dc Continue Bhojpuri cloud storage translation. 2026-08-24 17:56:27 +03:00
Lauri Ojansivu 6f5d8d8f39 Continue Bhojpuri security controls translation. 2026-08-24 17:54:07 +03:00
Lauri Ojansivu aa97568684 Record thirty-six Bhojpuri whole-file batches. 2026-08-24 17:50:48 +03:00
Lauri Ojansivu 91260e7d14 Continue Bhojpuri database migration translation. 2026-08-24 17:50:11 +03:00
Lauri Ojansivu 6b60492f9b Continue Bhojpuri account security translation. 2026-08-24 17:48:14 +03:00
Lauri Ojansivu 1b0e3094b6 Record thirty-four Bhojpuri whole-file batches. 2026-08-24 17:46:15 +03:00
Lauri Ojansivu 45553b91c0 Continue Bhojpuri support translation. 2026-08-24 17:45:38 +03:00
Lauri Ojansivu b72cec826c Continue Bhojpuri storage repair translation. 2026-08-24 17:43:57 +03:00
Lauri Ojansivu 091af4f5f7 Record thirty-two Bhojpuri whole-file batches. 2026-08-24 17:42:05 +03:00
Lauri Ojansivu 3834d7804c Continue Bhojpuri diagnostics and storage translation. 2026-08-24 17:41:21 +03:00
Lauri Ojansivu 2ca480f5e2 Continue Bhojpuri recovery translation. 2026-08-24 17:39:45 +03:00
Lauri Ojansivu 1e45d30b6a Record thirty Bhojpuri whole-file batches. 2026-08-24 17:37:53 +03:00
Lauri Ojansivu e2f0cc4cac Continue Bhojpuri reports translation. 2026-08-24 17:37:21 +03:00
Lauri Ojansivu 80b9efc625 Continue Bhojpuri dependencies translation. 2026-08-24 17:35:49 +03:00
Lauri Ojansivu 8ef30522ae Record twenty-eight Bhojpuri whole-file batches. 2026-08-24 17:34:06 +03:00
Lauri Ojansivu ab65b72ecc Continue Bhojpuri search help translation. 2026-08-24 17:33:33 +03:00
Lauri Ojansivu 2c68b10def Continue Bhojpuri global search translation. 2026-08-24 17:31:26 +03:00
Lauri Ojansivu fcad4551fa Record twenty-six Bhojpuri whole-file batches. 2026-08-24 17:30:02 +03:00
Lauri Ojansivu 8c8a2acae4 Continue Bhojpuri shared templates translation. 2026-08-24 17:29:28 +03:00
Lauri Ojansivu 5c32b8e69e Continue Bhojpuri roles translation. 2026-08-24 17:27:46 +03:00
Lauri Ojansivu bd787da9e4 Record twenty-four Bhojpuri whole-file batches. 2026-08-24 17:25:46 +03:00
Lauri Ojansivu b82b2f74c0 Continue Bhojpuri activity translation. 2026-08-24 17:25:00 +03:00
Lauri Ojansivu 922fc392f0 Continue Bhojpuri automation configuration translation. 2026-08-24 17:23:04 +03:00
Lauri Ojansivu b29f8ab2df Record twenty-two Bhojpuri whole-file batches. 2026-08-24 17:20:44 +03:00
Lauri Ojansivu a4dd2ee132 Continue Bhojpuri automation actions translation. 2026-08-24 17:20:08 +03:00
Lauri Ojansivu 7dbac7b00d Continue Bhojpuri scheduled automation translation. 2026-08-24 17:18:46 +03:00
Lauri Ojansivu a6579fb09d Record twenty Bhojpuri whole-file batches. 2026-08-24 17:17:10 +03:00
Lauri Ojansivu eba96421c0 Continue Bhojpuri automation translation. 2026-08-24 17:16:36 +03:00
Lauri Ojansivu 60d228c7ef Continue Bhojpuri card settings translation. 2026-08-24 17:14:33 +03:00
Lauri Ojansivu 45d7eb3374 Record eighteen Bhojpuri whole-file batches. 2026-08-24 17:12:53 +03:00
Lauri Ojansivu 86a4d98976 Continue Bhojpuri diagnostics translation. 2026-08-24 17:12:18 +03:00
Lauri Ojansivu a6381700ae Continue Bhojpuri server settings translation. 2026-08-24 17:10:46 +03:00
Lauri Ojansivu 46cdc19b14 Record sixteen Bhojpuri whole-file batches. 2026-08-24 17:08:50 +03:00
Lauri Ojansivu 562e2e7ae0 Continue Bhojpuri tracking translation. 2026-08-24 17:08:17 +03:00
Lauri Ojansivu c4e81b0de3 Continue Bhojpuri privacy translation. 2026-08-24 17:06:41 +03:00
Lauri Ojansivu 26b681cb64 Record fourteen Bhojpuri whole-file batches. 2026-08-24 17:04:38 +03:00
Lauri Ojansivu f08d8c479d Continue Bhojpuri board actions translation. 2026-08-24 17:03:59 +03:00
Lauri Ojansivu 2d1f0a938a Continue Bhojpuri Trello translation. 2026-08-24 17:02:15 +03:00
Lauri Ojansivu e84dff32b3 Record twelve Bhojpuri whole-file batches. 2026-08-24 17:00:11 +03:00
Lauri Ojansivu a0fefe9793 Continue Bhojpuri import translation. 2026-08-24 16:59:33 +03:00
Lauri Ojansivu b81f241e7c Continue Bhojpuri export translation. 2026-08-24 16:57:05 +03:00
Lauri Ojansivu e3c84f9668 Record ten Bhojpuri whole-file batches. 2026-08-24 16:55:46 +03:00
Lauri Ojansivu d7b8b1acf0 Continue Bhojpuri email translation. 2026-08-24 16:54:35 +03:00
Lauri Ojansivu 4aac0b48a9 Continue Bhojpuri permissions translation. 2026-08-24 16:52:11 +03:00
Lauri Ojansivu bc18a2cbb3 Record eight Bhojpuri whole-file batches. 2026-08-24 16:49:38 +03:00
Lauri Ojansivu 5e394b2944 Continue Bhojpuri navigation translation. 2026-08-24 16:48:03 +03:00
Lauri Ojansivu 51a89024e8 Continue Bhojpuri appearance translation. 2026-08-24 16:46:16 +03:00
Lauri Ojansivu 8a40ffd0de Record six Bhojpuri whole-file batches. 2026-08-24 16:44:11 +03:00
Lauri Ojansivu e2445e186d Continue Bhojpuri card dialogs translation. 2026-08-24 16:43:27 +03:00
Lauri Ojansivu 458d8bc3b6 Continue Bhojpuri board views translation. 2026-08-24 16:41:50 +03:00
Lauri Ojansivu 7bdf932704 Record four Bhojpuri whole-file batches. 2026-08-24 16:39:34 +03:00
Lauri Ojansivu 19625f136f Continue Bhojpuri board settings translation. 2026-08-24 16:38:47 +03:00
Lauri Ojansivu 32ea617a97 Continue Bhojpuri board layout translation. 2026-08-24 16:36:59 +03:00
Lauri Ojansivu ca92c0aeaa Record two Bhojpuri whole-file batches. 2026-08-24 16:34:11 +03:00
Lauri Ojansivu e3e0feb9ff Continue Bhojpuri workspace translation. 2026-08-24 16:32:46 +03:00
Lauri Ojansivu 85d354077e Begin Bhojpuri whole-file translation. 2026-08-24 16:29:57 +03:00
Lauri Ojansivu bed87dd0d2 Record complete Bashkir translation. 2026-08-24 16:27:27 +03:00
Lauri Ojansivu 0cb7c0ec20 Complete Bashkir translation. 2026-08-24 16:26:33 +03:00
Lauri Ojansivu 4a37d3d5f3 Continue Bashkir problem reporting translation. 2026-08-24 16:25:07 +03:00
Lauri Ojansivu 75eededc07 Record forty-two Bashkir whole-file batches. 2026-08-24 16:23:10 +03:00
Lauri Ojansivu 97db47bb16 Continue Bashkir migration monitoring translation. 2026-08-24 16:22:18 +03:00
Lauri Ojansivu 4173a30cc2 Continue Bashkir conversion monitoring translation. 2026-08-24 16:20:32 +03:00
Lauri Ojansivu 7d325e076a Record forty Bashkir whole-file batches. 2026-08-24 16:18:39 +03:00
Lauri Ojansivu b310f52e82 Continue Bashkir board repair translation. 2026-08-24 16:17:49 +03:00
Lauri Ojansivu 5a3aaed211 Continue Bashkir migration controls translation. 2026-08-24 16:15:30 +03:00
Lauri Ojansivu b0b6fe370d Record thirty-eight Bashkir whole-file batches. 2026-08-24 16:13:35 +03:00
Lauri Ojansivu 9225018cc1 Continue Bashkir cloud storage translation. 2026-08-24 16:12:50 +03:00
Lauri Ojansivu d504617978 Continue Bashkir security controls translation. 2026-08-24 16:10:56 +03:00
Lauri Ojansivu 3b386ce72c Record thirty-six Bashkir whole-file batches. 2026-08-24 16:07:39 +03:00
Lauri Ojansivu 59702f662b Continue Bashkir database migration translation. 2026-08-24 16:06:55 +03:00
Lauri Ojansivu cf5b97b9ec Continue Bashkir account protection translation. 2026-08-24 16:05:08 +03:00
Lauri Ojansivu 1d105a1339 Record thirty-four Bashkir whole-file batches. 2026-08-24 16:03:12 +03:00
Lauri Ojansivu b36b7f633c Continue Bashkir support translation. 2026-08-24 16:02:30 +03:00
Lauri Ojansivu 9fa620a28f Continue Bashkir attachment repair translation. 2026-08-24 16:00:53 +03:00
Lauri Ojansivu da52c48019 Record thirty-two Bashkir whole-file batches. 2026-08-24 15:58:24 +03:00
Lauri Ojansivu 7539f6a11a Continue Bashkir storage translation. 2026-08-24 15:57:39 +03:00
Lauri Ojansivu b32595edbf Continue Bashkir recovery translation. 2026-08-24 15:55:52 +03:00
Lauri Ojansivu 8180a93b61 Record thirty Bashkir whole-file batches. 2026-08-24 15:53:34 +03:00
Lauri Ojansivu c31b0eccea Continue Bashkir diagnostics translation. 2026-08-24 15:52:50 +03:00
Lauri Ojansivu 11921ffbf5 Continue Bashkir dependencies translation. 2026-08-24 15:50:45 +03:00
Lauri Ojansivu 0052681aec Record twenty-eight Bashkir whole-file batches. 2026-08-24 15:48:22 +03:00
Lauri Ojansivu f9823596f5 Continue Bashkir search guidance translation. 2026-08-24 15:47:37 +03:00
Lauri Ojansivu b9fee1dc68 Continue Bashkir search operators translation. 2026-08-24 15:45:24 +03:00
Lauri Ojansivu bbcec8e04e Record twenty-six Bashkir whole-file batches. 2026-08-24 15:43:52 +03:00
Lauri Ojansivu f832359b4d Continue Bashkir shared views translation. 2026-08-24 15:43:03 +03:00
Lauri Ojansivu 341305be84 Continue Bashkir board roles translation. 2026-08-24 15:41:05 +03:00
Lauri Ojansivu 3c02a14d04 Record twenty-four Bashkir whole-file batches. 2026-08-24 15:39:15 +03:00
Lauri Ojansivu 7e74d4c12e Continue Bashkir reminders translation. 2026-08-24 15:38:29 +03:00
Lauri Ojansivu a869ab8507 Continue Bashkir customization translation. 2026-08-24 15:36:37 +03:00
Lauri Ojansivu c65a9f5320 Record twenty-two Bashkir whole-file batches. 2026-08-24 15:34:53 +03:00
Lauri Ojansivu c8eeced26e Continue Bashkir rule actions translation. 2026-08-24 15:34:09 +03:00
Lauri Ojansivu fe28d09c5a Continue Bashkir scheduled rules translation. 2026-08-24 15:32:48 +03:00
Lauri Ojansivu 64008a7348 Record twenty Bashkir whole-file batches. 2026-08-24 15:31:00 +03:00
Lauri Ojansivu 3377c35cc7 Continue Bashkir rules translation. 2026-08-24 15:30:19 +03:00
Lauri Ojansivu 7682263751 Continue Bashkir card settings translation. 2026-08-24 15:28:40 +03:00
Lauri Ojansivu f436ad33d8 Record eighteen Bashkir whole-file batches. 2026-08-24 15:26:24 +03:00
Lauri Ojansivu af1db66d19 Continue Bashkir tenancy translation. 2026-08-24 15:25:39 +03:00
Lauri Ojansivu 9323a2c9d6 Continue Bashkir administration translation. 2026-08-24 15:24:01 +03:00
Lauri Ojansivu 62e5daf4ce Record sixteen Bashkir whole-file batches. 2026-08-24 15:22:14 +03:00
Lauri Ojansivu 98e649f409 Continue Bashkir tracking translation. 2026-08-24 15:21:16 +03:00
Lauri Ojansivu 6cfd6593b4 Continue Bashkir shortcuts translation. 2026-08-24 15:18:29 +03:00
Lauri Ojansivu 30df93df94 Record fourteen Bashkir whole-file batches. 2026-08-24 15:16:40 +03:00
Lauri Ojansivu b8bda89b9b Continue Bashkir board roles translation. 2026-08-24 15:15:58 +03:00
Lauri Ojansivu 0c34816ed7 Continue Bashkir Trello import translation. 2026-08-24 15:14:27 +03:00
Lauri Ojansivu 0ae6d32308 Record twelve Bashkir whole-file batches. 2026-08-24 15:12:40 +03:00
Lauri Ojansivu a70fcc6035 Continue Bashkir import translation. 2026-08-24 15:11:40 +03:00
Lauri Ojansivu 8bd69df66c Continue Bashkir export translation. 2026-08-24 15:09:30 +03:00
Lauri Ojansivu b821a679aa Record ten Bashkir whole-file batches. 2026-08-24 15:07:30 +03:00
Lauri Ojansivu d4dfa3b328 Continue Bashkir email translation. 2026-08-24 15:06:46 +03:00
Lauri Ojansivu de74b644db Continue Bashkir roles translation. 2026-08-24 15:04:56 +03:00
Lauri Ojansivu ef0378587f Record eight Bashkir whole-file batches. 2026-08-24 15:02:46 +03:00
Lauri Ojansivu 5c2436823b Continue Bashkir navigation translation. 2026-08-24 15:02:07 +03:00
Lauri Ojansivu 229e4d17ca Continue Bashkir appearance translation. 2026-08-24 15:00:32 +03:00
Lauri Ojansivu a5383269de Record six Bashkir whole-file batches. 2026-08-24 14:58:38 +03:00
Lauri Ojansivu c00ec44927 Continue Bashkir card dialogs translation. 2026-08-24 14:57:58 +03:00
Lauri Ojansivu 4f098a105e Continue Bashkir board views translation. 2026-08-24 14:56:38 +03:00
Lauri Ojansivu f675abb37b Record four Bashkir whole-file batches. 2026-08-24 14:54:56 +03:00
Lauri Ojansivu ad6520f221 Continue Bashkir board settings translation. 2026-08-24 14:54:17 +03:00
Lauri Ojansivu 52c6d17315 Continue Bashkir workspace translation. 2026-08-24 14:52:53 +03:00
Lauri Ojansivu 8a481031a7 Record two Bashkir whole-file batches. 2026-08-24 14:51:04 +03:00
Lauri Ojansivu 727fd0308a Continue Bashkir activity translation. 2026-08-24 14:50:26 +03:00
Lauri Ojansivu b78cf2d986 Begin Bashkir whole-file translation. 2026-08-24 14:49:00 +03:00
Lauri Ojansivu 2832d3f8d4 Record complete Assamese whole-file translation. 2026-08-24 14:46:39 +03:00
Lauri Ojansivu e14b77043a Complete Assamese whole-file translation. 2026-08-24 14:45:47 +03:00
Lauri Ojansivu 07061ad920 Continue Assamese problem reporting translation. 2026-08-24 14:44:41 +03:00
Lauri Ojansivu 42ca0e3147 Record forty-two Assamese whole-file batches. 2026-08-24 14:42:38 +03:00
Lauri Ojansivu 6db145bbdd Continue Assamese migration monitoring translation. 2026-08-24 14:41:55 +03:00
Lauri Ojansivu 48785ba59a Continue Assamese monitoring translation. 2026-08-24 14:40:19 +03:00
Lauri Ojansivu a86c5b7ad4 Record forty Assamese whole-file batches. 2026-08-24 14:38:11 +03:00
Lauri Ojansivu f193c30899 Continue Assamese board migration translation. 2026-08-24 14:37:33 +03:00
Lauri Ojansivu 55793c200d Continue Assamese storage migration translation. 2026-08-24 14:35:11 +03:00
Lauri Ojansivu 0cc9ed807e Record thirty-eight Assamese whole-file batches. 2026-08-24 14:33:21 +03:00
Lauri Ojansivu a24986db53 Continue Assamese cloud backup translation. 2026-08-24 14:32:36 +03:00
Lauri Ojansivu 05f6b9f54f Continue Assamese security controls translation. 2026-08-24 14:30:28 +03:00
Lauri Ojansivu e09f2855e7 Record thirty-six Assamese whole-file batches. 2026-08-24 14:26:33 +03:00
Lauri Ojansivu 7869792ee0 Continue Assamese database migration translation. 2026-08-24 14:25:54 +03:00
Lauri Ojansivu 4abde2c3e3 Continue Assamese account protection translation. 2026-08-24 14:23:46 +03:00
Lauri Ojansivu 2226a6d78d Record thirty-four Assamese whole-file batches. 2026-08-24 14:21:10 +03:00
Lauri Ojansivu 448f7d8404 Continue Assamese support translation. 2026-08-24 14:20:33 +03:00
Lauri Ojansivu fc472e4eb0 Continue Assamese attachment storage translation. 2026-08-24 14:18:45 +03:00
Lauri Ojansivu 26040ecac4 Record thirty-two Assamese whole-file batches. 2026-08-24 14:16:35 +03:00
Lauri Ojansivu 2aa069c88c Continue Assamese administration translation. 2026-08-24 14:15:57 +03:00
Lauri Ojansivu eac3fc68b0 Continue Assamese recovery translation. 2026-08-24 14:14:04 +03:00
Lauri Ojansivu 460ee800ee Record thirty Assamese whole-file batches. 2026-08-24 14:12:02 +03:00
Lauri Ojansivu db40024426 Continue Assamese diagnostics translation. 2026-08-24 14:11:20 +03:00
Lauri Ojansivu b2c749c435 Continue Assamese dependencies translation. 2026-08-24 14:09:38 +03:00
Lauri Ojansivu a4e83bde42 Record twenty-eight Assamese whole-file batches. 2026-08-24 14:07:51 +03:00
Lauri Ojansivu b010bf75ca Continue Assamese search help translation. 2026-08-24 14:07:13 +03:00
Lauri Ojansivu 4b53c5fdcb Continue Assamese search syntax translation. 2026-08-24 14:04:49 +03:00
Lauri Ojansivu dd8065e8ec Record twenty-six Assamese whole-file batches. 2026-08-24 14:03:16 +03:00
Lauri Ojansivu b00d2effa4 Continue Assamese shared views translation. 2026-08-24 14:02:37 +03:00
Lauri Ojansivu a4c1ef2e24 Continue Assamese roles translation. 2026-08-24 14:00:42 +03:00
Lauri Ojansivu cf5f3189ff Record twenty-four Assamese whole-file batches. 2026-08-24 13:58:39 +03:00
Lauri Ojansivu 3928c5675e Continue Assamese activity settings translation. 2026-08-24 13:58:05 +03:00
Lauri Ojansivu 0b44314ae4 Continue Assamese automation settings translation. 2026-08-24 13:56:04 +03:00
Lauri Ojansivu 3e017b7180 Record twenty-two Assamese whole-file batches. 2026-08-24 13:54:31 +03:00
Lauri Ojansivu 94f492fcb7 Continue Assamese automation phrases translation. 2026-08-24 13:51:59 +03:00
Lauri Ojansivu bcdc4775e8 Continue Assamese scheduled automation translation. 2026-08-24 13:50:27 +03:00
Lauri Ojansivu 7bc0ab8ea6 Record twenty Assamese whole-file batches. 2026-08-24 13:48:56 +03:00
Lauri Ojansivu fa5e543a8d Continue Assamese activity and automation translation. 2026-08-24 13:48:16 +03:00
Lauri Ojansivu 5049503218 Continue Assamese card and subtask settings translation. 2026-08-24 13:46:28 +03:00
Lauri Ojansivu ff16efb9e6 Record eighteen Assamese whole-file batches. 2026-08-24 13:44:40 +03:00
Lauri Ojansivu 8a0c79ca25 Continue Assamese diagnostics and tenancy translation. 2026-08-24 13:44:05 +03:00
Lauri Ojansivu 65eabb5b21 Continue Assamese limits and webhook translation. 2026-08-24 13:41:36 +03:00
Lauri Ojansivu 956a72c33d Record sixteen Assamese whole-file batches. 2026-08-24 13:39:42 +03:00
Lauri Ojansivu 454fe1e710 Continue Assamese tracking and branding translation. 2026-08-24 13:39:10 +03:00
Lauri Ojansivu 1febef5551 Continue Assamese navigation and member translation. 2026-08-24 13:37:16 +03:00
Lauri Ojansivu 630435fa79 Record fourteen Assamese whole-file batches. 2026-08-24 13:35:18 +03:00
Lauri Ojansivu 95e763454c Continue Assamese list actions and roles translation. 2026-08-24 13:34:42 +03:00
Lauri Ojansivu 5179eb7647 Continue Assamese Trello and member mapping translation. 2026-08-24 13:32:29 +03:00
Lauri Ojansivu 9c91321a1f Record twelve Assamese whole-file batches. 2026-08-24 13:30:29 +03:00
Lauri Ojansivu b95aa961bc Continue Assamese advanced filters and imports translation. 2026-08-24 13:29:53 +03:00
Lauri Ojansivu 104f3f12ed Continue Assamese exports and filters translation. 2026-08-24 13:27:28 +03:00
Lauri Ojansivu 5e0bc2b215 Record ten Assamese whole-file batches. 2026-08-24 13:25:45 +03:00
Lauri Ojansivu 4e963dab37 Continue Assamese email and error translation. 2026-08-24 13:25:10 +03:00
Lauri Ojansivu d4511ccceb Continue Assamese roles and custom fields translation. 2026-08-24 13:22:35 +03:00
Lauri Ojansivu 33e4a7c1ec Record eight Assamese whole-file batches. 2026-08-24 13:20:35 +03:00
Lauri Ojansivu 6b9a7b6a81 Continue Assamese navigation and color translation. 2026-08-24 13:20:03 +03:00
Lauri Ojansivu 265675c1ce Continue Assamese members and typography translation. 2026-08-24 13:18:30 +03:00
Lauri Ojansivu 28dcfb7688 Record six Assamese whole-file batches. 2026-08-24 13:16:37 +03:00
Lauri Ojansivu d46008c9c4 Continue Assamese voting and dialog translation. 2026-08-24 13:16:05 +03:00
Lauri Ojansivu 5444c182af Continue Assamese board views and archive translation. 2026-08-24 13:14:29 +03:00
Lauri Ojansivu 6e9210d47b Record four Assamese whole-file batches. 2026-08-24 13:12:27 +03:00
Lauri Ojansivu c81798a344 Continue Assamese administration and archive translation. 2026-08-24 13:11:52 +03:00
Lauri Ojansivu 7bdc49228d Continue Assamese board layout translation. 2026-08-24 13:10:11 +03:00
Lauri Ojansivu d8da029dff Record two Assamese whole-file batches. 2026-08-24 13:08:12 +03:00
Lauri Ojansivu 648d426918 Continue Assamese activity and workspace translation. 2026-08-24 13:07:08 +03:00
Lauri Ojansivu 1ee1df2ca4 Begin Assamese whole-file translation. 2026-08-24 13:05:15 +03:00
Lauri Ojansivu 170dea5202 Record completed Amharic whole-file translation. 2026-08-24 13:02:14 +03:00
Lauri Ojansivu d8ef7ad8a4 Complete Amharic whole-file translation. 2026-08-24 13:01:37 +03:00
Lauri Ojansivu eb8d8724f1 Continue Amharic repository and repair translation. 2026-08-24 13:00:31 +03:00
Lauri Ojansivu 71a87dced4 Record forty-two Amharic whole-file batches. 2026-08-24 12:58:53 +03:00
Lauri Ojansivu 1a42efd7be Continue Amharic migration controls translation. 2026-08-24 12:58:18 +03:00
Lauri Ojansivu fc5bcc88e0 Continue Amharic migration and monitoring translation. 2026-08-24 12:56:54 +03:00
Lauri Ojansivu e3c6a7e03b Record forty Amharic whole-file batches. 2026-08-24 12:54:55 +03:00
Lauri Ojansivu 8d08603ac9 Continue Amharic board migration translation. 2026-08-24 12:54:20 +03:00
Lauri Ojansivu c3378401ca Continue Amharic migration and S3 translation. 2026-08-24 12:52:09 +03:00
Lauri Ojansivu c5f4d93350 Record thirty-eight Amharic whole-file batches. 2026-08-24 12:50:33 +03:00
Lauri Ojansivu 88daa17dac Continue Amharic backup and cloud storage translation. 2026-08-24 12:50:00 +03:00
Lauri Ojansivu 925fd424ba Continue Amharic security and backup translation. 2026-08-24 12:48:14 +03:00
Lauri Ojansivu 3649ae491f Record thirty-six Amharic whole-file batches. 2026-08-24 12:45:07 +03:00
Lauri Ojansivu 822f4ba288 Continue Amharic storage and database migration translation. 2026-08-24 12:44:31 +03:00
Lauri Ojansivu 91a82565be Continue Amharic lockout and scheduling translation. 2026-08-24 12:42:47 +03:00
Lauri Ojansivu 40a2ca8c5f Record thirty-four Amharic whole-file batches. 2026-08-24 12:41:03 +03:00
Lauri Ojansivu 635af5a0cc Continue Amharic support and accessibility translation. 2026-08-24 12:40:31 +03:00
Lauri Ojansivu 0939757148 Continue Amharic storage and repair translation. 2026-08-24 12:39:03 +03:00
Lauri Ojansivu c6e83f7001 Record thirty-two Amharic whole-file batches. 2026-08-24 12:36:55 +03:00
Lauri Ojansivu 44be0dc7e0 Continue Amharic administration and attachment translation. 2026-08-24 12:35:58 +03:00
Lauri Ojansivu 508125bf87 Continue Amharic reports and ticket translation. 2026-08-24 12:34:17 +03:00
Lauri Ojansivu 6bed60f9a2 Record thirty Amharic whole-file batches.
Update Upcoming and TODO Later through 1,500 translated Amharic values, with
666 remaining and 197,825 genuinely untranslated values repository-wide.
Record double-brace and percent-brace placeholder coverage.

Thanks to xet7.
2026-08-24 12:31:56 +03:00
Lauri Ojansivu 5f9176edc4 Continue Amharic maps and report translation.
Translate the next 50 map, troubleshooting, string-template, report and office
values. Preserve shell commands, HTML entities, IP terms and percent-brace
tokens, extend token coverage, and update progress to 1,500 translated and 666
remaining.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 12:31:22 +03:00
Lauri Ojansivu 40da7ceeda Continue Amharic dependency and location translation.
Translate the next 50 sorting, sticker, dependency, background and location
values. Preserve JSON/SVG, named tokens and double-brace templates, extend token
coverage accordingly, and update progress to 1,450 translated and 716 remaining.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 12:29:38 +03:00
Lauri Ojansivu d4f8a5f368 Record twenty-eight Amharic whole-file batches.
Update Upcoming and TODO Later through 1,400 translated Amharic values, with
766 remaining and 197,925 genuinely untranslated values repository-wide.
Record range-placeholder ordering and syntax-sensitive search-help coverage.

Thanks to xet7.
2026-08-24 12:27:52 +03:00
Lauri Ojansivu 8ede9a6413 Continue Amharic search-help translation.
Translate the next 50 search predicates, validation messages and full operator
help values. Preserve every backticked expression, metavariable, Markdown mark,
newline and placeholder; update progress to 1,400 translated and 766 remaining.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 12:27:18 +03:00
Lauri Ojansivu 88f4e76f42 Continue Amharic global-search translation.
Translate the next 50 result-count, search-operator and predicate values.
Preserve percent tokens and range-placeholder order, add focused order coverage,
and update progress to 1,350 translated and 816 remaining.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 12:23:00 +03:00
Lauri Ojansivu 2ef0347759 Record twenty-six Amharic whole-file batches.
Update Upcoming and TODO Later through 1,300 translated Amharic values, with
866 remaining and 198,025 genuinely untranslated values repository-wide.

Thanks to xet7.
2026-08-24 12:20:59 +03:00
Lauri Ojansivu 8cf649e740 Continue Amharic domains and card-view translation.
Translate the next 50 checklist, domain, shared-template, My Cards, Due Cards,
global-search and lookup-error values. Preserve domain examples, Markdown and
percent placeholders; update progress to 1,300 translated and 866 remaining.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 12:20:17 +03:00
Lauri Ojansivu de82638120 Continue Amharic roles and editor translation.
Translate the next 50 multi-card, editor, organization, notification, role,
weekday and linked-card values. Preserve keyboard chords and update progress to
1,250 translated and 916 remaining under whole-locale invariant coverage.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 12:18:31 +03:00
Lauri Ojansivu 5cdb175fdb Record twenty-four Amharic whole-file batches.
Update Upcoming and TODO Later through 1,200 translated Amharic values, with
966 remaining and 198,125 genuinely untranslated values repository-wide.

Thanks to xet7.
2026-08-24 12:16:30 +03:00
Lauri Ojansivu 129a95c110 Continue Amharic authentication and reminder translation.
Translate the next 50 custom-body, authentication, duplication, deletion,
positioning, due-reminder, drag and editor values. Preserve HTML tags, newlines
and all activity placeholders; update progress to 1,200 translated and 966
remaining.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 12:15:35 +03:00
Lauri Ojansivu 115e6151b4 Continue Amharic automation and head translation.
Translate the next 50 automation-action, authentication, custom-head and layout
values. Preserve comma-separated examples, HTML/JSON terms and assetlinks.json;
update progress to 1,150 translated and 1,016 remaining.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 12:13:41 +03:00
Lauri Ojansivu e3f0bc055c Record twenty-two Amharic whole-file batches.
Update Upcoming and TODO Later through 1,100 translated Amharic values, with
1,066 remaining and 198,225 genuinely untranslated values repository-wide.

Thanks to xet7.
2026-08-24 12:12:04 +03:00
Lauri Ojansivu 039266234a Continue Amharic automation phrase translation.
Translate the next 50 list, label, member, checklist, card and email automation
fragments with consistent rule-builder terminology. Update progress to 1,100
translated and 1,066 remaining under whole-locale invariant coverage.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 12:11:32 +03:00
Lauri Ojansivu b2e8a27476 Continue Amharic scheduled automation translation.
Translate the next 50 visual-workflow, schedule, due-trigger, button, sorting and
relative-date values. Preserve workflow product names, the count placeholder,
weekday range and symbolic N; update progress to 1,050 translated and 1,116
remaining.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 12:09:59 +03:00
Lauri Ojansivu 7f41bc6e0f Record twenty Amharic whole-file batches.
Update Upcoming and TODO Later through 1,000 translated Amharic values, with
1,166 remaining and 198,325 genuinely untranslated values repository-wide.
Record repeated-percent positional regression coverage.

Thanks to xet7.
2026-08-24 12:08:18 +03:00
Lauri Ojansivu 7e4508f232 Continue Amharic activity and automation translation.
Translate the next 50 activity-message and automation workflow/import values.
Preserve repeated percent argument order, named tokens, JSON/CSV and Butler
terms; update progress to 1,000 translated and 1,166 remaining.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 12:07:40 +03:00
Lauri Ojansivu ef061a85d6 Continue Amharic card and subtask translation.
Translate the next 50 card-date, color, deletion, subtask, minicard and activity
values. Preserve the board placeholder, percent-token order and count examples,
and update progress to 950 translated and 1,216 remaining.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 12:05:36 +03:00
Lauri Ojansivu 81edfa4637 Record eighteen Amharic whole-file batches.
Update Upcoming and TODO Later through 900 translated Amharic values, with
1,266 remaining and 198,425 genuinely untranslated values repository-wide.

Thanks to xet7.
2026-08-24 12:03:48 +03:00
Lauri Ojansivu 0d96fcf16d Continue Amharic diagnostics and tenancy translation.
Translate the next 50 diagnostics, custom-field display, visibility,
organization and team values. Preserve environment variables, protocols,
hostnames and multitenancy syntax; update progress to 900 translated and 1,266
remaining.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 12:03:11 +03:00
Lauri Ojansivu 3f4380a526 Continue Amharic limits and webhook translation.
Translate the next 50 attachment-limit, registration, SMTP, invitation, webhook
and runtime-version values. Preserve invitation placeholders and technical
terms, and update progress to 850 translated and 1,316 remaining.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 12:01:29 +03:00
Lauri Ojansivu f4a22f335c Record sixteen Amharic whole-file batches.
Update Upcoming and TODO Later through 800 translated Amharic values, with
1,366 remaining and 198,525 genuinely untranslated values repository-wide.

Thanks to xet7.
2026-08-24 11:59:47 +03:00
Lauri Ojansivu 14344e047b Continue Amharic tracking and branding translation.
Translate the next 50 time-tracking, upload, custom-branding, welcome-template
and WIP values. Retain URL, API, WIP and numeric terms, and update progress to
800 translated and 1,366 remaining under whole-locale invariant coverage.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:59:14 +03:00
Lauri Ojansivu 1ba438326e Continue Amharic privacy and shortcut translation.
Translate the next 50 notification, privacy, removal, search, WIP and shortcut
values. Preserve login markup and member placeholders, and update progress to
750 translated and 1,416 remaining under whole-locale invariant coverage.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:57:34 +03:00
Lauri Ojansivu 97bc659765 Record fourteen Amharic whole-file batches.
Update Upcoming and TODO Later through 700 translated Amharic values, with
1,466 remaining and 198,625 genuinely untranslated values repository-wide.

Thanks to xet7.
2026-08-24 11:55:35 +03:00
Lauri Ojansivu 3375924769 Continue Amharic board actions and roles translation.
Translate the next 50 board-departure, list/archive, selection, notification and
role values. Preserve the board-title placeholder and update progress to 700
translated and 1,466 remaining under whole-locale invariant coverage.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:55:02 +03:00
Lauri Ojansivu 27cfa82d09 Continue Amharic Trello import and label translation.
Translate the next 50 Trello API import, member-mapping, validation and label
values. Preserve the API URL, year example and percent placeholder, and update
progress to 650 translated and 1,516 remaining.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:52:55 +03:00
Lauri Ojansivu 8d4bdba03b Record twelve Amharic whole-file batches.
Update Upcoming and TODO Later through 600 translated Amharic values, with
1,566 remaining and 198,725 genuinely untranslated values repository-wide.

Thanks to xet7.
2026-08-24 11:50:58 +03:00
Lauri Ojansivu 8e052624b0 Continue Amharic filter and import translation.
Translate the next 50 filter, activity and board-import values while preserving
filter grammar, JSON terms, paths, file formats and import placeholders. Update
progress to 600 translated and 1,566 remaining with focused syntax coverage.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:50:20 +03:00
Lauri Ojansivu 68b20172f3 Continue Amharic export and filter translation.
Translate the next 50 user-error, card-export, sorting and filter values.
Update progress to 550 translated and 1,616 remaining under whole-locale token,
markup and Ethiopic-script coverage.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:47:24 +03:00
Lauri Ojansivu f5d727d411 Record ten Amharic whole-file batches.
Update Upcoming and TODO Later through 500 translated Amharic values, with
1,666 remaining and 198,825 genuinely untranslated values repository-wide.

Thanks to xet7.
2026-08-24 11:45:29 +03:00
Lauri Ojansivu 17f7dc538a Continue Amharic email and error translation.
Translate the next 50 custom-field, profile, email-template, WIP and error
values. Preserve every email placeholder and update progress to 500 translated
and 1,666 remaining under whole-locale token and markup coverage.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:44:51 +03:00
Lauri Ojansivu 4d950bc603 Record nine Amharic whole-file batches.
Update Upcoming and TODO Later through 450 translated Amharic values, with
1,716 remaining and 198,875 genuinely untranslated values repository-wide.
Record the broadened percent-placeholder regression coverage.

Thanks to xet7.
2026-08-24 11:42:49 +03:00
Lauri Ojansivu 2e9aced927 Continue Amharic roles and custom-field translation.
Translate the next 50 color, role, confirmation, copying and custom-field
values. Update progress to 450 translated and 1,716 remaining, and extend
the locale regression to preserve all letter-style percent placeholders.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:41:42 +03:00
Lauri Ojansivu 74b4cf093a Continue Amharic navigation and color translation.
Translate the next 50 navigation, card-aging, movement, dialog and color values.
Update progress to 400 translated and 1,766 remaining while retaining
whole-locale placeholder, markup and Ethiopic-script coverage.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:38:02 +03:00
Lauri Ojansivu cc1acfff12 Record seven Amharic whole-file batches.
Update Upcoming and TODO Later to 350 translated Amharic values, 1,816 still
remaining there, and 198,975 values across 92 locale files globally.

Thanks to xet7.
2026-08-24 11:36:46 +03:00
Lauri Ojansivu 9a7965ef9f Continue Amharic member and typography translation.
Translate the next 50 popup, imported-member mapping, theme and typography
values. Update progress to 350 translated and 1,816 remaining while retaining
whole-locale placeholder, markup and Ethiopic-script coverage.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:36:15 +03:00
Lauri Ojansivu b70b264d95 Continue Amharic voting and import translation.
Translate the next 50 membership, voting, Planning Poker, dependency and import
values. Update progress to 300 translated and 1,866 remaining while retaining
whole-locale placeholder, markup and Ethiopic-script coverage.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:35:08 +03:00
Lauri Ojansivu c6471bb674 Record five Amharic whole-file batches.
Update Upcoming and TODO Later to 250 translated Amharic values, 1,916 still
remaining there, and 199,075 values across 92 locale files globally.

Thanks to xet7.
2026-08-24 11:33:55 +03:00
Lauri Ojansivu d5ba00104c Continue Amharic board-view translation.
Translate the next 50 visibility, display-mode, archive and card-editing values.
Update progress to 250 translated and 1,916 remaining while retaining
whole-locale placeholder, markup and Ethiopic-script coverage.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:33:23 +03:00
Lauri Ojansivu 09a05b33fd Continue Amharic admin and board translation.
Translate the next 50 admin, archive, attachment and board-information values.
Update progress to 200 translated and 1,966 remaining, and extend whole-locale
coverage to preserve HTML markup as well as value placeholders.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:32:04 +03:00
Lauri Ojansivu 61333c55e9 Record three Amharic whole-file batches.
Update Upcoming and TODO Later to 150 translated Amharic values, 2,016 still
remaining there, and 199,175 values across 92 locale files globally.

Thanks to xet7.
2026-08-24 11:30:11 +03:00
Lauri Ojansivu 7a3465e291 Continue Amharic board interface translation.
Translate the next 50 workspace, board-selection, list-sizing and checklist UI
values. Update exact progress to 150 translated and 2,016 remaining while
retaining whole-locale placeholder and Ethiopic-script coverage.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:29:25 +03:00
Lauri Ojansivu 4af04a4e43 Continue the Amharic whole-file translation.
Translate the next 50 activity and workspace values directly into Amharic.
Update exact progress to 100 translated and 2,066 remaining while retaining
whole-locale placeholder and Ethiopic-script coverage.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:27:59 +03:00
Lauri Ojansivu 3831ce56db Record the first Amharic whole-file batch.
Update Upcoming and TODO Later with 50 translated Amharic values, 2,116 still
remaining there, and 199,275 values across 92 locale files globally.

Thanks to xet7.
2026-08-24 11:26:28 +03:00
Lauri Ojansivu c37a15bace Begin the Amharic whole-file translation.
Translate the first 50 activity and board-history values directly into Amharic.
Add progress, Ethiopic-script and repository-wide translated-value placeholder
coverage for the locale; 2,116 values remain.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:25:49 +03:00
Lauri Ojansivu d49285bf73 Update translation progress through Flemish.
Record three completed locale files and the verified transition to 199,325
whole-file values across 92 locale files.

Thanks to xet7.
2026-08-24 11:23:32 +03:00
Lauri Ojansivu cff765a799 Complete the Flemish translation tail.
Translate 73 remaining values using the verified Dutch terminology plus a
regional Flemish board and admin mapping. Preserve existing human translations,
API and storage identifiers, with focused zero-backlog coverage.

Thanks to xet7.
2026-08-24 11:22:46 +03:00
Lauri Ojansivu 1f14d95b80 Complete both Dutch translation tails.
Translate 132 remaining values using established Dutch UI terms instead of
English loanword placeholders. Preserve API and storage identifiers and add
focused zero-backlog and sibling-consistency coverage.

Thanks to xet7.
2026-08-24 11:22:00 +03:00
Lauri Ojansivu 4cd58426ec Update translation progress through the German tier.
Record twelve completed locale files and the verified remaining backlog of
199,530 values across 95 locale files.

Thanks to xet7.
2026-08-24 11:20:28 +03:00
Lauri Ojansivu 6a9b669d05 Complete all German translation tails.
Translate 232 remaining values across four German tags using established German
UI terms instead of English loanword placeholders. Preserve API and storage
identifiers and add focused zero-backlog and sibling-consistency coverage.

Thanks to xet7.
2026-08-24 11:19:43 +03:00
Lauri Ojansivu 1e36d074b1 Complete both West Frisian translation tails.
Translate 110 remaining values using explicit West Frisian alternatives for
valid cognates while preserving API and storage identifiers. Add focused
zero-backlog and sibling-consistency coverage.

These translations have mixed confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:18:44 +03:00
Lauri Ojansivu 87b49dc124 Complete all French translation tails.
Translate 270 remaining values across five French tags using explicit French
phrases for valid cognates while preserving API and configuration literals. Add
focused zero-backlog and sibling-consistency coverage.

Thanks to xet7.
2026-08-24 11:17:35 +03:00
Lauri Ojansivu a28e1df7ec Complete the Wolof translation tail.
Translate the remaining 44 board, interface, Office, API and storage values
while preserving network and configuration literals. Add focused zero-backlog,
terminology and English-remnant coverage.

These translations have low confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:16:31 +03:00
Lauri Ojansivu 03d4fa5f7a Update translation progress through the 42-value tier.
Record eight completed locale files and the verified remaining backlog of
200,186 values across 107 locale files.

Thanks to xet7.
2026-08-24 11:15:02 +03:00
Lauri Ojansivu d506906922 Complete the Norwegian Bokmal translation tail.
Translate the remaining 42 interface, Office, API and storage values using
explicit Norwegian alternatives for valid cognates. Add focused zero-backlog,
terminology and protected-literal coverage.

Thanks to xet7.
2026-08-24 11:14:12 +03:00
Lauri Ojansivu 819f24fdc1 Complete the Danish translation tail.
Translate the remaining 40 interface, Office, API and storage values using
explicit Danish alternatives for valid cognates. Add focused zero-backlog,
terminology and protected-literal coverage.

Thanks to xet7.
2026-08-24 11:13:25 +03:00
Lauri Ojansivu cbae558f29 Complete both Malay translation tails.
Translate 76 remaining values using explicit Malay alternatives for valid
cognates while preserving executable JSON, API and configuration literals. Add
focused zero-backlog and sibling-consistency coverage.

Thanks to xet7.
2026-08-24 11:12:38 +03:00
Lauri Ojansivu 3d55ef1dfe Complete both Afrikaans translation tails.
Translate 74 remaining values using explicit Afrikaans alternatives for valid
cognates while preserving API and configuration literals. Add focused
zero-backlog, sibling-consistency and terminology coverage.

Thanks to xet7.
2026-08-24 11:11:47 +03:00
Lauri Ojansivu f84ba9d2b7 Complete the 36-value translation tier.
Translate 72 remaining values across Igbo and Swedish while preserving API and
storage identifiers. Add focused zero-backlog, terminology and protected-token
coverage.

The Igbo translations have low confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:10:56 +03:00
Lauri Ojansivu 1002adfe86 Update translation progress through the 35-value tier.
Record six completed locale files and the verified remaining backlog of 200,490
values across 115 locale files.

Thanks to xet7.
2026-08-24 11:09:15 +03:00
Lauri Ojansivu 6eeffd5721 Complete the remaining 35-value translation tier.
Translate 70 values across Spanish-tagged Catalan and Klingon while preserving
API, configuration and underscore placeholders. Add focused zero-backlog,
foreign-script and mixed-language coverage for the new values.

The Klingon translations have very low confidence and welcome native-speaker
review; its older mixed-language seed text remains for the whole-file audit.

Thanks to xet7.
2026-08-24 11:08:30 +03:00
Lauri Ojansivu c3debaa3ab Complete the Catalan translation tail.
Translate the remaining 35 board, admin, Office, API and storage values while
keeping standard Catalan terminology distinct from Valencian. Add focused
zero-backlog and protected-literal coverage.

Thanks to xet7.
2026-08-24 11:05:52 +03:00
Lauri Ojansivu cf9a4ce5f7 Complete the Italian translation tail.
Translate the remaining 33 Office, API, interface and storage values using
explicit Italian alternatives for valid cognates. Add focused zero-backlog,
terminology and protected-literal coverage.

Thanks to xet7.
2026-08-24 11:05:03 +03:00
Lauri Ojansivu e8be40c242 Complete the 32-value translation tier.
Translate 64 remaining values across Walloon and Yoruba while preserving API,
search and underscore placeholders and replacing quoted English UI messages.
Add focused zero-backlog and mixed-language regression coverage.

These translations have low confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 11:04:15 +03:00
Lauri Ojansivu f6fac115d6 Update translation progress through the 31-value tier.
Record Valencian completion and the verified remaining backlog of 200,692
values across 121 locale files.

Thanks to xet7.
2026-08-24 11:02:15 +03:00
Lauri Ojansivu b9faa361b0 Complete the Valencian translation tail.
Translate the remaining 31 board, admin, Office, API and storage values while
preserving protected network and configuration literals. Add focused
zero-backlog and Valencian terminology coverage.

Thanks to xet7.
2026-08-24 11:01:35 +03:00
Lauri Ojansivu a8e54c4855 Update translation progress through the 30-value tier.
Record eleven completed locale files and the verified remaining backlog of
200,723 values across 122 locale files.

Thanks to xet7.
2026-08-24 11:00:33 +03:00
Lauri Ojansivu 0e1cbc1c51 Complete the 30-value translation tier.
Translate 90 remaining values across both Slovenian files and Volapuk while
preserving API, configuration, numeric and underscore placeholders. Add focused
zero-backlog, sibling-consistency and mixed-language coverage.

The Volapuk translations have low confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 10:59:32 +03:00
Lauri Ojansivu 1c34ec39ac Complete the 29-value translation tier.
Translate 87 remaining values across Acehnese and both Czech locale files while
preserving executable JSON and protected API literals. Add focused zero-backlog,
sibling-consistency and terminology coverage.

The Acehnese translations have low confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 10:57:44 +03:00
Lauri Ojansivu 762a778aa1 Complete the 28-value translation tier.
Translate 140 remaining values across Indonesian, Occitan, Brazilian
Portuguese, Turkmen and Tamazight while preserving executable JSON, API and
search placeholders. Add focused zero-backlog and terminology coverage.

The Occitan, Turkmen and Tamazight translations have low confidence and welcome
native-speaker review.

Thanks to xet7.
2026-08-24 10:56:25 +03:00
Lauri Ojansivu bccea39af6 Update translation progress through the 27-value tier.
Record eleven completed locale files and the verified remaining backlog of
201,040 values across 133 locale files.

Thanks to xet7.
2026-08-24 10:53:50 +03:00
Lauri Ojansivu a518c69904 Complete the 27-value translation tier.
Translate 108 remaining values across Estonian, Romanian and Walloon locale
files while preserving protected API, transport and search placeholders. Add
focused zero-backlog, sibling-consistency and terminology coverage.

The Walloon translations have low confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 10:52:59 +03:00
Lauri Ojansivu 6b35635550 Complete the 26-value translation tier.
Translate 182 remaining values across Azerbaijani, Croatian, Polish and Slovak
locale files while preserving protected API and transport literals. Add
focused zero-backlog, sibling-consistency and terminology coverage.

The Azerbaijani translations have mixed confidence and welcome native-speaker
review.

Thanks to xet7.
2026-08-24 10:51:16 +03:00
Lauri Ojansivu 9cd783d3ce Update translation progress through the 25-value tier.
Record eleven completed locale files and the verified remaining backlog of
201,330 values across 144 locale files.

Thanks to xet7.
2026-08-24 10:48:58 +03:00
Lauri Ojansivu 46351547a9 Complete Asturian Welsh and Uzbek translation tails.
Translate 175 remaining values across Asturian, two Welsh tags and four
Latin-script Uzbek files. Preserve existing human translations and protected
network and configuration literals, with focused zero-backlog coverage.

The Asturian and Uzbek translations have mixed confidence and welcome
native-speaker review.

Thanks to xet7.
2026-08-24 10:47:47 +03:00
Lauri Ojansivu f7af96193a Complete the Swahili translation tail.
Translate the remaining board, colour, admin, Office, API and CPU values while
preserving protected network and configuration literals. Add focused
zero-backlog and terminology regression coverage.

Thanks to xet7.
2026-08-24 10:45:34 +03:00
Lauri Ojansivu c6d3662b35 Complete Galician and Xhosa translation tails.
Translate the remaining board, admin, Office, API, storage and typography
values in both Galician tags and Xhosa while preserving protected literals.
Add focused zero-backlog and terminology regression coverage.

The Xhosa translations have low confidence and welcome native-speaker review.

Thanks to xet7.
2026-08-24 10:44:37 +03:00
Lauri Ojansivu f31329b160 Update translation progress after the 22-value tier.
Record the completed Breton, Lithuanian and Yiddish batch and the verified
remaining backlog of 201,598 values across 155 locale files.

Thanks to xet7.
2026-08-24 10:42:45 +03:00
Lauri Ojansivu cdefe268d6 Complete Breton Lithuanian and Yiddish translation tails.
Translate the remaining board, admin, Office, API, storage and search-help
values while preserving protected formatting and API literals. Add focused
zero-backlog, terminology and script regression coverage.

The Breton and Yiddish translations have low confidence and welcome
native-speaker review.

Thanks to xet7.
2026-08-24 10:42:01 +03:00
Lauri Ojansivu 529011e41a Update direct translation progress.
Record the completed Hungarian, Latvian, Basque and Uyghur batches and the
verified remaining backlog of 201,664 values across 158 locale files.

Thanks to xet7.
2026-08-24 10:39:17 +03:00
Lauri Ojansivu ac1ce02952 Complete Basque and Uyghur translation tails.
Preserve formatting and API literals while translating the remaining board,
admin, office report and API report strings. Add focused regression coverage
for completeness, target scripts and protected placeholders.

Thanks to xet7.
2026-08-24 10:37:39 +03:00
Lauri Ojansivu 7874d769b9 Complete Latvian translation fill.
Translate the final 21 genuine English board, color, Office, API report, and storage values through the human-preserving fill workflow. Add zero-backlog, terminology, English-remnant, and API-literal coverage.

The Latvian locale now contains only translations and invariant values.

Thanks to xet7 !
2026-08-24 10:33:43 +03:00
Lauri Ojansivu 3e6924f68d Complete Hungarian translation fill.
Translate the final 21 genuine English board, FerretDB, Office, API report, and storage values through the human-preserving fill workflow. Add zero-backlog, terminology, English-remnant, and API-literal coverage.

The Hungarian locale now contains only translations and invariant values.

Thanks to xet7 !
2026-08-24 10:32:56 +03:00
Lauri Ojansivu 99668fd573 Update direct-fill family progress.
Record completed Portuguese, Thai, Venda, Zulu, Esperanto, Spanish, and Turkish fills. Update TODO Later to 201,748 untranslated values across 162 locales.

Thanks to xet7 !
2026-08-24 10:31:42 +03:00
Lauri Ojansivu 4420650482 Complete Turkish translation fill.
Translate the final 20 genuine English checklist, typography, Office, API report, and storage values through the human-preserving fill workflow. Add zero-backlog, terminology, English-remnant, and API-literal coverage.

The Turkish locale now contains only translations and invariant values.

Thanks to xet7 !
2026-08-24 10:30:44 +03:00
Lauri Ojansivu 64bda540f0 Complete Spanish translation fills.
Translate the final 20 genuine English UI, Office, API report, and ticket values across all nine incomplete Spanish tags through the human-preserving fill workflow. Use explicit Spanish alternatives for valid cognates and add zero-backlog plus report coverage.

All Spanish locale tags now contain only translations and invariant values.

Thanks to xet7 !
2026-08-24 10:30:04 +03:00
Lauri Ojansivu 5b9f6874a6 Complete Esperanto translation fill.
Translate the final 20 genuine English board-selection, clipboard, permanent-delete, Office, and API report values through the human-preserving fill workflow. Add zero-backlog, Esperanto-vocabulary, English-remnant, and API-literal coverage.

The Esperanto locale now contains only translations and invariant values.

Thanks to xet7 !
2026-08-24 10:29:09 +03:00
Lauri Ojansivu 3bd4ea9f3f Complete Venda and Zulu translation fills.
Translate the final 19 board-selection, permanent-delete, Office, and API report values in three Tshivenda and two isiZulu tags through separate human-preserving fills. Add zero-backlog, language-distinction, English-remnant, and API-literal coverage.

All five locale files now contain only translations and invariant values. Direct translations have low confidence and welcome native-speaker review.

Thanks to xet7 !
2026-08-24 10:28:17 +03:00
Lauri Ojansivu a3bf822815 Complete Thai translation fill.
Translate the final 19 genuine English board-selection, permanent-delete, Office, and API report values through the human-preserving fill workflow. Add zero-backlog, Thai-script, English-remnant, and API-literal coverage.

The Thai locale now contains only translations and invariant values. Direct translations welcome native-speaker review.

Thanks to xet7 !
2026-08-24 10:26:31 +03:00
Lauri Ojansivu 4a1d4587fc Complete Portuguese translation fill.
Translate the final 19 genuine English color, menu, location, Office, and API report values across all three Portuguese tags through the human-preserving fill workflow. Use explicit Portuguese alternatives for valid cognates and add zero-backlog plus report coverage.

All Portuguese locale tags now contain only translations and invariant values.

Thanks to xet7 !
2026-08-24 10:25:39 +03:00
Lauri Ojansivu 13aa613deb Update compact direct-fill progress.
Record 28 completed locale files across the single-value, Vietnamese, Greek, Indic, eight-value, nine-value, and Khmer batches. Update TODO Later to 202,139 untranslated values across 182 locales.

Thanks to xet7 !
2026-08-24 10:24:35 +03:00
Lauri Ojansivu 68aa99f446 Synchronize the Khmer underscore tag.
Commit the already-verified Khmer translations for the tracked km_KH locale variant. Its zero-backlog and script coverage is shared with the canonical Khmer fill regression.

Thanks to xet7 !
2026-08-24 10:23:25 +03:00
Lauri Ojansivu 41af211f87 Complete Khmer translation fill.
Translate the final 13 genuine English board-selection, permanent-delete, and wait-spinner values across all three Khmer tags through the human-preserving fill workflow. Add zero-backlog, Khmer-script, and English-remnant coverage.

All Khmer locale tags now contain only translations and invariant values. Direct translations welcome native-speaker review.

Thanks to xet7 !
2026-08-24 10:23:12 +03:00
Lauri Ojansivu 9e61400e29 Complete four nine-value translation fills.
Translate the final nine genuine English values in Bulgarian, both Persian variants, and Macedonian through the human-preserving fill workflow. Preserve technical product and JSON identifiers and add zero-backlog, script, and English-remnant coverage.

All four locale files now contain only translations and invariant values. Direct translations welcome native-speaker review.

Thanks to xet7 !
2026-08-24 10:21:54 +03:00
Lauri Ojansivu 78daa99617 Complete six eight-value translation fills.
Translate the final eight genuine English values in Belarusian, Armenian, Georgian, Mongolian, Serbian, and Tamil through the human-preserving fill workflow. Add zero-backlog, target-script, and English-remnant coverage.

All six locale files now contain only translations and invariant values. Direct translations welcome native-speaker review.

Thanks to xet7 !
2026-08-24 10:20:34 +03:00
Lauri Ojansivu eca9301e9f Complete four Indic translation fills.
Translate the final seven board-selection and permanent-delete messages in Gujarati, Odia, Punjabi, and Telugu through the human-preserving fill workflow. Add target-script and English-remnant regression coverage.

All four locale files now contain only translations and invariant values. Direct translations welcome native-speaker review.

Thanks to xet7 !
2026-08-24 10:16:05 +03:00
Lauri Ojansivu aa7f62ffed Complete Greek translation fill.
Translate the final four genuine English storage, backup, and project values in both Greek variants through the human-preserving fill workflow. Add focused Greek-script regression coverage.

Both Greek locale files now contain only translations and invariant values.

Thanks to xet7 !
2026-08-24 10:13:23 +03:00
Lauri Ojansivu 3e6942e6fb Complete Vietnamese translation fill.
Translate the final three genuine English values in both Vietnamese variants and classify date-format masks as intentionally invariant. Add focused locale and CLI regression coverage.

Both Vietnamese and both Hebrew variants now contain only translations and invariant values.

Thanks to xet7 !
2026-08-24 10:12:53 +03:00
Lauri Ojansivu 9d0dd9ffc3 Update completed-locale fill regression.
Make the invariant-source CLI test recognize that Finnish is now fully translated instead of requiring it to retain a real English placeholder.

Thanks to xet7 !
2026-08-24 10:11:10 +03:00
Lauri Ojansivu 1e6b0e73f8 Complete five single-value translations.
Fill the final genuine English value in Finnish, both Hindi variants, and both Ukrainian variants through the human-preserving fill workflow. Add focused language and exact-value regression coverage.

All five locale files now contain only translated or intentionally invariant values.

Thanks to xet7 !
2026-08-24 10:10:41 +03:00
Lauri Ojansivu 3e887b9192 Record completed placeholder repair phase.
Update Upcoming and TODO Later for the Ukrainian, Estonian, Arabic-family, and Tamazight repairs. Record zero repository-wide placeholder mismatches and the corrected 202,628-value direct-fill backlog across 210 locales.

Thanks to xet7 !
2026-08-24 10:09:23 +03:00
Lauri Ojansivu 2694049549 Exclude invariant translation sources.
Make missing and list reports ignore values that intentionally remain identical in every language: empty values, numbers, symbols, placeholder-only formats, units, product names, and storage backends. Keep sentences containing placeholders translatable and preserve separate invariant accounting in status mode.

Add CLI regression coverage. The corrected backlog is 202,628 translatable values across 210 locales.

Thanks to xet7 !
2026-08-24 10:08:32 +03:00
Lauri Ojansivu 70bac688e4 Restore Tamazight placeholders.
Restore the final 41 placeholder inventories in Standard Moroccan Tamazight activity and search-help values. Replace the affected Arabic and French seed prose with Latin-script Tamazight and add whole-file plus wrong-language negative coverage.

The locale is placeholder-clean and the repository-wide mismatch audit is now zero. These direct translations have low confidence and welcome native-speaker review.

Thanks to xet7 !
2026-08-24 10:04:15 +03:00
Lauri Ojansivu f800c6b73e Restore Arabic-family placeholders.
Restore 160 placeholder inventories across Arabic, Algerian Arabic, Egyptian Arabic, and Moroccan Arabic activity and email values. Add whole-file, script, and translated-identifier negative coverage.

All four locale files are now placeholder-clean. The Moroccan Arabic wording has low dialect confidence and welcomes native-speaker review.

Thanks to xet7 !
2026-08-24 10:02:53 +03:00
Lauri Ojansivu 4e15e3d88d Restore Estonian placeholders.
Restore all 35 named and printf placeholder inventories in Estonian activity, email, due-date, range, and search-help prose. Add whole-file and translated-identifier negative coverage.

The Estonian locale is now placeholder-clean. Direct translations welcome native-speaker review.

Thanks to xet7 !
2026-08-24 10:01:00 +03:00
Lauri Ojansivu baafa98725 Restore Ukrainian search placeholders.
Restore 68 placeholder inventories across both Ukrainian variants, including every global-search operator and predicate identifier and the correct activity printf arities. Add whole-file and focused regression coverage.

Both Ukrainian locale files are now placeholder-clean. Direct translations welcome native-speaker review.

Thanks to xet7 !
2026-08-24 09:59:58 +03:00
Lauri Ojansivu a084b1344d Update medium-family translation progress.
Record the 145 repaired Afrikaans, Romanian, and Chinese values and update TODO Later to 304 mismatches across eight locales.

Thanks to xet7 !
2026-08-24 09:58:27 +03:00
Lauri Ojansivu e4659eea34 Repair medium translation families.
Restore 145 placeholder inventories across Afrikaans, Romanian, Simplified Chinese, and Traditional Chinese variants. Replace Italian seed prose in Romanian and reuse only placeholder-clean sibling Chinese translations with asserted inventories.

All ten locale files are now placeholder-clean, leaving 304 mismatches across eight locale files. Direct translations welcome native-speaker review.

Thanks to xet7 !
2026-08-24 09:58:09 +03:00
Lauri Ojansivu fb31d0e39c Update compact-tier translation progress.
Record the completed Latvian, Norwegian, Occitan, and Hong Kong Chinese batch and update TODO Later to 449 mismatches across 18 locales.

Thanks to xet7 !
2026-08-24 09:56:01 +03:00
Lauri Ojansivu f3b17b34e3 Repair four compact placeholder tiers.
Restore 37 placeholder inventories across Latvian, Norwegian Bokmal, Occitan, and Hong Kong Traditional Chinese. Replace French seed prose in the affected Occitan values and add whole-file plus focused negative coverage.

All four locale files are now placeholder-clean, leaving 449 mismatches across 18 locale files. The direct Occitan translations welcome native-speaker review.

Thanks to xet7 !
2026-08-24 09:55:44 +03:00
Lauri Ojansivu 3e720de609 Update seven-mismatch translation progress.
Record the completed Acehnese, Indonesian, Turkish, and Traditional Chinese tier and update TODO Later to 486 mismatches across 22 locales.

Thanks to xet7 !
2026-08-24 09:53:26 +03:00
Lauri Ojansivu fdacd27a1b Complete the seven-mismatch translation tier.
Restore 28 placeholder inventories across Acehnese, Indonesian, Turkish, and Traditional Chinese. Replace the Acehnese files Indonesian seed prose with the locales established terminology and add whole-file plus focused negative coverage.

All four locale files are now placeholder-clean, leaving 486 mismatches across 22 locale files. The direct Acehnese translations have low confidence and welcome native-speaker review.

Thanks to xet7 !
2026-08-24 09:53:02 +03:00
Lauri Ojansivu 855e2e6a6e Update residual placeholder repair progress.
Record the completed Odia, Turkmen, and Uyghur cleanup and update TODO Later to the remaining 514 mismatches across 26 locales.

Thanks to xet7 !
2026-08-24 09:50:34 +03:00
Lauri Ojansivu 2de6cb5567 Finish three marker-repaired translations.
Restore 32 residual named and printf placeholder inventories in Odia, Turkmen, and Uyghur while keeping their surrounding prose in each declared language. Add whole-file and focused negative regression coverage.

These three locales are now placeholder-clean, leaving 514 mismatches across 26 locale files. Direct translations should receive native-speaker review.

Thanks to xet7 !
2026-08-24 09:49:57 +03:00
Lauri Ojansivu ac62d15728 Update machine-placeholder repair progress.
Record the deterministic restoration of 1,208 Transifex markers in 539 values across five locale files, and update TODO Later to the remaining 546 mismatches across 29 locales.

Thanks to xet7 !
2026-08-24 09:39:39 +03:00
Lauri Ojansivu bacbfa3d5c Restore Transifex machine placeholders.
Map numbered PH markers to the protected backtick, HTML, angle, named and printf spans in each English source key. Repair 1,208 markers in 539 translated values across five files with transactional validation and repository-wide negative coverage.\n\nThanks to xet7 !
2026-08-24 09:37:03 +03:00
Lauri Ojansivu 0bd533d209 Complete six-mismatch changelog progress.
Document the Italian, Venda and Volapük repairs in Upcoming and reduce TODO Later to 1,034 mismatched keys across 31 locale files.\n\nThanks to xet7 !
2026-08-24 09:30:40 +03:00
Lauri Ojansivu 09444981d2 Finish the six-mismatch placeholder tier.
Restore exact tokens in Italian and replace wrong-language seed prose with Venda and Volapük across the remaining six-mismatch files. Add whole-file inventory, seed-language negative and due-time value coverage.\n\nThe direct Venda and Volapük translations have low confidence and welcome native-speaker review.\n\nThanks to xet7 !
2026-08-24 09:30:09 +03:00
Lauri Ojansivu e0515f6979 Update six-mismatch translation progress.
Document the completed ten-tag batch in Upcoming and reduce TODO Later to 1,052 mismatched keys across 34 locale files.\n\nThanks to xet7 !
2026-08-24 09:27:00 +03:00
Lauri Ojansivu 6ce409f055 Restore placeholders in ten locale variants.
Repair the coherent Azerbaijani, Catalan and Russian files from the six-mismatch tier. Restore complete activity locations, corrupted search predicates, exact email subjects and printf tokens. Add whole-file inventory and focused negative coverage.\n\nThe direct Azerbaijani wording welcomes human review.\n\nThanks to xet7 !
2026-08-24 09:26:27 +03:00
Lauri Ojansivu d0fcb68d4c Update seeded-locale translation progress.
Document the completed Breton, Walloon, Wolof and Klingon repairs in Upcoming and reduce TODO Later to 1,112 mismatched keys across 44 locale files.\n\nThanks to xet7 !
2026-08-24 09:16:09 +03:00
Lauri Ojansivu 63bbf24609 Repair placeholders in four seeded locales.
Restore exact tokens while replacing French prose in Breton, Walloon and Wolof and German prose in Klingon. Keep search examples literal and add whole-file inventory and wrong-language negative coverage.\n\nThese direct translations have low confidence and welcome native-speaker review.\n\nThanks to xet7 !
2026-08-24 09:15:09 +03:00
Lauri Ojansivu 018b460f2c Update placeholder translation progress.
Document the completed eleven-file batch in Upcoming and reduce TODO Later to 1,132 mismatched keys across 48 locale files, with four wrongly seeded files identified for language-specific repair.\n\nThanks to xet7 !
2026-08-24 09:11:52 +03:00
Lauri Ojansivu 0edb3db90d Restore placeholders in eleven locale variants.
Repair the coherent French, German and Hindi files from the five-mismatch tier. Restore exact activity, email and search tokens, including literal search examples that must not become placeholders. Add whole-file inventory and focused negative coverage.\n\nThanks to xet7 !
2026-08-24 09:11:19 +03:00
Lauri Ojansivu 1327367d91 Update placeholder repair backlog.
Document the completed fourteen-locale tier in Upcoming and reduce TODO Later to 1,187 mismatched keys across 59 locale files.\n\nThanks to xet7 !
2026-08-24 09:07:09 +03:00
Lauri Ojansivu c844eeb813 Restore placeholders in fourteen locale files.
Repair every locale that had exactly four placeholder mismatches. Restore exact token spelling and multiplicity, replace copied Spanish in Esperanto, and use the declared hiragana and Arabic-script variants. Add whole-file inventory and focused negative coverage.\n\nSeveral direct translations are low confidence and welcome human review.\n\nThanks to xet7 !
2026-08-24 09:06:44 +03:00
Lauri Ojansivu 238ccdd86a Update placeholder repair progress.
Document the completed eighteen-locale tier in Upcoming and reduce TODO Later to 1,243 mismatched keys across 73 locale files.\n\nThanks to xet7 !
2026-08-24 08:57:32 +03:00
Lauri Ojansivu ce93ac3880 Restore placeholders in eighteen locale files.
Repair every locale that had exactly three counted placeholder mismatches, plus a hidden Xhosa machine remnant. Replace wrong-language carryovers according to each locale tag and add whole-file inventory and focused negative coverage.\n\nSeveral direct translations are low confidence and welcome human review.\n\nThanks to xet7 !
2026-08-24 08:56:59 +03:00
Lauri Ojansivu 7dcad3360e Update placeholder repair backlog.
Document the completed seventeen-locale tier in Upcoming and reduce TODO Later to 1,297 mismatched keys across 91 locale files.\n\nThanks to xet7 !
2026-08-24 08:51:43 +03:00
Lauri Ojansivu b3f4007e04 Restore placeholders in seventeen locale files.
Repair every locale that had exactly two counted placeholder mismatches, plus four Welsh HTML placeholder remnants exposed by negative coverage. Remove duplicated English from Greek and replace Catalan prose in Basque. Add whole-file inventory and focused negative tests.\n\nThe direct Welsh, Basque, Finnish, Croatian and Slovenian wording welcomes human review.\n\nThanks to xet7 !
2026-08-24 08:51:08 +03:00
Lauri Ojansivu 75a8da1bc3 Update placeholder translation progress.
Document the eleven completed locale files in Upcoming and reduce the exact TODO Later backlog to 1,331 mismatched keys across 108 locale files.\n\nThanks to xet7 !
2026-08-24 08:43:39 +03:00
Lauri Ojansivu 0aa3b9784e Restore placeholders in eleven locale files.
Repair every locale that had exactly one mismatched placeholder. Preserve exact named-token spelling and case, restore missing printf tokens, update obsolete Hungarian prose, and replace copied Portuguese wording with Galician. Add whole-file inventory and negative regression coverage.\n\nThe direct Galician and Hungarian wording welcomes human review.\n\nThanks to xet7 !
2026-08-24 08:43:01 +03:00
Lauri Ojansivu 3a119b28f7 Document Mongolian placeholder repair progress.
Add the completed repair to Upcoming and record the exact repository-wide placeholder backlog in TODO Later.\n\nThanks to xet7 !
2026-08-24 08:39:40 +03:00
Lauri Ojansivu 24bc8efb0b Restore English placeholders in Mongolian translations.
Restore the exact __siteName__ and %s tokens while translating the surrounding Russian prose into Mongolian. Add a whole-file placeholder inventory regression test. The Mongolian wording is a low-confidence direct translation and welcomes human review.\n\nThanks to xet7 !
2026-08-24 08:37:35 +03:00
Lauri Ojansivu cfb0649f46 Document translation placeholder integrity.
Add the underscore-delimited and percent-prefixed placeholder rule to Upcoming and keep the release summary and translation-policy group current.\n\nThanks to xet7 !
2026-08-24 08:34:35 +03:00
Lauri Ojansivu 65d86b4420 Protect translation placeholders from translation.
Require underscore-delimited and percent-prefixed placeholders to match the corresponding English source token exactly. Any translated, renamed, missing or malformed placeholder must be restored from en.i18n.json, including inside otherwise human translations, and regression coverage must compare token inventories.\n\nThanks to xet7 !
2026-08-24 08:33:48 +03:00
Lauri Ojansivu ac3c3fcd9e Track Mongolian label translation repair.
Record the 30 repaired label values, update the cumulative Mongolian repair count to 176 and the remaining exact-match candidates to 998, and add the verified batch to Upcoming.\n\nThanks to xet7 !
2026-08-24 08:29:26 +03:00
Lauri Ojansivu e71c827ec0 Replace Russian Mongolian label text.
Replace 30 exact Russian copies across Mongolian label activities, deletion, multi-selection, automation, search, display settings and validation. Regression coverage compares every value with Russian, preserves named, printf and Markdown placeholders, and requires consistent Mongolian label vocabulary.\n\nThis direct Mongolian repair has low confidence and welcomes native-speaker review. Another 998 exact Russian-match candidates, including shared loanwords, remain for later audited batches.\n\nThanks to xet7 !
2026-08-24 08:28:49 +03:00
Lauri Ojansivu 9271974564 Track Mongolian filter-label translation repair.
Record the 11 repaired filter values, update the cumulative Mongolian repair count to 146 and the remaining exact-match candidates to 1,028, and add the verified batch to Upcoming.\n\nThanks to xet7 !
2026-08-24 08:25:54 +03:00
Lauri Ojansivu 5aa7cb9782 Replace Russian Mongolian filter labels.
Replace 11 exact Russian copies across Mongolian date, list-title, label, member, assignee, custom-field, advanced and card-title filters. Regression coverage compares every value with Russian and requires consistent Mongolian filter vocabulary.\n\nThis direct Mongolian repair has low confidence and welcomes native-speaker review. Another 1,028 exact Russian-match candidates, including shared loanwords, remain for later audited batches.\n\nThanks to xet7 !
2026-08-24 08:25:20 +03:00
Lauri Ojansivu 298b400704 Track Mongolian checklist and subtask translation repair.
Record the 50 repaired values, update the cumulative Mongolian repair count to 135 and the remaining exact-match candidates to 1,039, and add the verified batch to Upcoming.\n\nThanks to xet7 !
2026-08-24 08:20:11 +03:00
Lauri Ojansivu 0aa734872d Replace Russian Mongolian checklist and subtask text.
Replace 50 exact Russian copies across Mongolian checklist and subtask activities, dialogs, settings, automation fragments, search vocabulary and export labels. Preserve every named and printf placeholder and keep the search operator a single token.\n\nThis direct Mongolian repair has low confidence and welcomes native-speaker review. Another 1,039 exact Russian-match candidates, including shared loanwords, remain for later audited batches.\n\nThanks to xet7 !
2026-08-24 08:19:40 +03:00
Lauri Ojansivu 73a1a5698e Track Mongolian attachment translation repair.
Record the 14 repaired attachment values, update the cumulative Mongolian repair count to 85 and the remaining exact-match candidates to 1,089, and add the verified batch to Upcoming.\n\nThanks to xet7 !
2026-08-24 08:17:02 +03:00
Lauri Ojansivu 27ac02b411 Replace Russian Mongolian attachment controls.
Replace 14 exact Russian copies across Mongolian attachment deletion, actions, storage moves and renaming. Retain the valid shared File loanword and preserve GridFS. Regression coverage compares the batch with Russian and rejects common Russian attachment terms.\n\nThis direct Mongolian repair has low confidence and welcomes native-speaker review. Another 1,089 exact Russian-match candidates, including shared loanwords, remain for later audited batches.\n\nThanks to xet7 !
2026-08-24 08:16:29 +03:00
Lauri Ojansivu 0e8d99d4dc Track Mongolian card-control translation repair.
Record the 43 repaired card values, update the cumulative Mongolian repair count to 71 and the remaining exact Russian-copy candidates to 1,103, and add the verified batch to Upcoming.\n\nThanks to xet7 !
2026-08-24 08:15:00 +03:00
Lauri Ojansivu acc164e406 Replace Russian Mongolian card controls.
Replace 43 exact Russian copies across Mongolian card guidance, dates, editing controls, voting, templates, sorting and popup titles. Regression coverage compares every repaired key with Russian, preserves placeholders and established Mongolian vocabulary, and rejects common Russian card terms.\n\nThis direct Mongolian repair has low confidence and welcomes native-speaker review. Another 1,103 exact Russian-copy candidates remain for later audited batches.\n\nThanks to xet7 !
2026-08-24 08:14:16 +03:00
Lauri Ojansivu e69087b20c Track same-script Mongolian translation repair.
Correct TODO Later's former all-fixed claim, record the 1,174-value Russian-copy audit and first 28 repairs, and add the verified batch to Upcoming with the remaining 1,146 candidates.\n\nThanks to xet7 !
2026-08-24 08:11:44 +03:00
Lauri Ojansivu 56f270fa22 Replace Russian Mongolian archive and board controls.
Replace 28 exact Russian copies in the Mongolian archive, template and board-view controls with Mongolian. Keep the valid shared Archive loanword and add same-script vocabulary regression coverage that compares the repaired keys with Russian.\n\nThis direct Mongolian repair has low confidence and welcomes native-speaker review. Another 1,146 exact Russian-copy candidates remain for later audited batches.\n\nThanks to xet7 !
2026-08-24 08:10:45 +03:00
Lauri Ojansivu 3ee6028eef Document correct-language translation policy.
Add the policy change to Upcoming and restore the released v11.10 section exactly to its release state, relocating the five August 24 translation batches into the current release where they belong.\n\nThanks to xet7 !
2026-08-24 08:07:50 +03:00
Lauri Ojansivu 775c56df97 Require each locale file to use its declared language.
Clarify that human-preference protection applies only to correct-language translations. Mixed and wrongly seeded values must be replaced with the locale tag's language, including same-script cases such as Russian text in Mongolian, with vocabulary review and regression coverage.\n\nThanks to xet7 !
2026-08-24 08:06:24 +03:00
Lauri Ojansivu 1d3c7159ac Update translation backlog after Mongolian reports.
Record the 12 completed report translations, reduce the non-Latin TODO Later count to 463, and add the low-confidence batch to the Cyrillic Upcoming group.\n\nThanks to xet7 !
2026-08-24 08:01:57 +03:00
Lauri Ojansivu 2935689996 Translate Office and API reports into Mongolian.
Fill only the twelve report placeholders in the near-complete Mongolian locale, preserving REST API, IPv4, IPv6, WITH_API=true and the intentionally universal API labels. Regression coverage checks completeness, Cyrillic text and technical tokens.\n\nThis direct translation has low confidence and welcomes review by Mongolian speakers. A human Transifex translation remains preferred.\n\nThanks to xet7 !
2026-08-24 08:01:20 +03:00
Lauri Ojansivu 7197271f06 Update translation backlog after Odia and Punjabi reports.
Record the 24 completed report translations, reduce the non-Latin TODO Later count to 475, and add the low-confidence batch to the Indic Upcoming group.\n\nThanks to xet7 !
2026-08-24 07:56:41 +03:00
Lauri Ojansivu 7fac8a9ccc Translate Office and API reports into Odia and Punjabi.
Fill only the twelve report placeholders in each near-complete locale, preserving REST API, IPv4, IPv6, WITH_API=true and the intentionally universal API labels. Regression coverage checks completeness, each language's script and technical tokens.\n\nThese direct translations have low confidence and welcome review by Odia and Punjabi speakers. A human Transifex translation remains preferred.\n\nThanks to xet7 !
2026-08-24 07:56:06 +03:00
Lauri Ojansivu 62352cec63 Update translation backlog after Telugu and Tamil reports.
Record the 24 completed report translations, reduce the non-Latin TODO Later count below 500, and add the low-confidence batch to the Indic Upcoming group.\n\nThanks to xet7 !
2026-08-24 07:51:37 +03:00
Lauri Ojansivu 4e19994a51 Translate Office and API reports into Telugu and Tamil.
Fill only the twelve report placeholders in each near-complete locale, preserving REST API, IPv4, IPv6, WITH_API=true and the intentionally universal API labels. Regression coverage checks completeness, each language's script and technical tokens.\n\nThese direct translations have low confidence and welcome review by Telugu and Tamil speakers. A human Transifex translation remains preferred.\n\nThanks to xet7 !
2026-08-24 07:50:51 +03:00
Lauri Ojansivu a464ac0f8d Update translation backlog after Armenian and Georgian reports.
Record the 24 completed report translations, reduce the non-Latin TODO Later count to 523, and add the low-confidence batch to Upcoming.\n\nThanks to xet7 !
2026-08-24 07:48:05 +03:00
Lauri Ojansivu 71c6d0be67 Translate Office and API reports into Armenian and Georgian.
Fill only the twelve report placeholders in each near-complete locale, preserving REST API, IPv4, IPv6, WITH_API=true and the intentionally universal API labels. Regression coverage checks completeness, each language's script and technical tokens.\n\nThese direct translations have low confidence and welcome review by Armenian and Georgian speakers. A human Transifex translation remains preferred.\n\nThanks to xet7 !
2026-08-24 07:47:17 +03:00
Lauri Ojansivu 34e0fb0cab Update translation backlog after Macedonian and Serbian reports.
Record the 24 completed report translations, refresh the TODO Later date and non-Latin count, and add the verified batch to Upcoming.\n\nThanks to xet7 !
2026-08-24 07:34:58 +03:00
Lauri Ojansivu 7a64f8bd76 Translate Office and API reports into Macedonian and Serbian.
Fill only the twelve report placeholders in each near-complete Cyrillic locale, preserving REST API, IPv4, IPv6, WITH_API=true and the intentionally universal API labels. Regression coverage checks completeness, script, distinct vocabulary and technical tokens.\n\nThese translations were completed directly with low confidence and welcome review by Macedonian and Serbian speakers. A human Transifex translation remains preferred.\n\nThanks to xet7 !
2026-08-24 07:34:09 +03:00
github-actions[bot] 0b83c9fea0 Bump versions for v11.10 2026-08-23 18:50:09 +00:00
Lauri Ojansivu b49f805676 Prepare v11.10 release 2026-08-23 21:49:04 +03:00
Lauri Ojansivu 732d7b7f64 Clarify the Upcoming ImportBleed boundary.
State that authentication follows Meteor's mandatory argument validation while still preceding all import processing, and restore the required body wrapping.\n\nThanks to Char0n1507 and xet7 !
2026-08-23 21:24:34 +03:00
Lauri Ojansivu 1daf66b862 Document restored translation aliases.
Add the Khmer and Russian Transifex alias correction to the Upcoming release and keep its summary current.\n\nThanks to xet7 !
2026-08-23 20:40:05 +03:00
Lauri Ojansivu 2d966f1570 Restore Transifex translation aliases as symlinks.
The Khmer and Russian hyphenated locale aliases had been materialized as regular copies, so translation pulls could update the underscored Transifex targets without updating the app-loaded aliases. Restore both aliases as symlinks; their former contents were byte-identical to their targets, so no translation is lost.\n\nThanks to xet7 !
2026-08-23 20:38:53 +03:00
Lauri Ojansivu d1aa3b5927 Correct the ImportBleed changelog boundary.
Link the Upcoming security entry to the Meteor argument-audit correction and clarify that mandatory synchronous type checks run before authentication while all import processing remains behind it.

Thanks to Char0n1507 and xet7 !
2026-08-23 20:37:14 +03:00
Lauri Ojansivu 2898635dfb Preserve ImportBleed errors under argument auditing.
Complete Meteor's mandatory synchronous argument checks before rejecting anonymous importBoard and importScoped calls. The authorization guard still precedes every feature lookup, parser, creator and database write, while DDP callers now receive error-notAuthorized instead of an audit-generated 500.

Thanks to Char0n1507 and xet7 !
2026-08-23 20:36:49 +03:00
Lauri Ojansivu b3f9187e53 Document bounded legacy E2E waits.
Record the connected DDP check, bounded resume-token callback and identity settlement, diagnostic failure state and final suite deadline in the upcoming release.

Thanks to xet7 !
2026-08-23 19:27:19 +03:00
Lauri Ojansivu 1ccb71baeb Bound legacy E2E login and suite waits.
Wait for a connected Meteor DDP session before resume-token login, bound the callback and identity settlement, and report browser connection state when either fails. Add a final suite deadline and regression coverage so a fresh second session can no longer leave all tests paused indefinitely.

Thanks to xet7 !
2026-08-23 19:26:52 +03:00
Lauri Ojansivu ec501137fe Document the critical ImportBleed fix.
Lead the upcoming release with the unauthenticated board-import write fix, its regression coverage, Admin Panel Problems attribution, advisory and Hall of Fame links. Credit the reporter and maintainer.

Thanks to Char0n1507 and xet7 !
2026-08-23 18:57:04 +03:00
Lauri Ojansivu 3f69df3170 Require authentication before importing boards.
Reject anonymous importBoard and importScoped DDP calls before argument parsing, feature checks, parsers or direct database writers can run. Carry the authenticated method user explicitly into scoped imports and add source-level plus logged-out browser regression coverage for ImportBleed.

Thanks to Char0n1507 and xet7 !
2026-08-23 18:56:39 +03:00
Lauri Ojansivu 8e4c65a9f2 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-23 18:52:25 +03:00
Lauri Ojansivu a69ce9f1af Update translation backlog after Vietnamese reports.
Record the Vietnamese Office and API report batch in TODO Later and the upcoming release notes. Refresh the measured Latin-script near-complete backlog from 2,868 to 2,844 values.

Thanks to xet7 !
2026-08-23 18:52:22 +03:00
Lauri Ojansivu 5bd71c07d3 Translate Office and API reports into Vietnamese.
Fill the twelve translatable report strings across both Vietnamese language tags, following their established login, report and endpoint vocabulary. Add regression coverage for completeness, Vietnamese text, variant consistency and preserved universal literals.

Thanks to xet7 !
2026-08-23 18:52:22 +03:00
Lauri Ojansivu c5c8bd392e Update translation backlog after Cyrillic and Khmer reports.
Record the Belarusian, Bulgarian and Khmer Office and API report batches in TODO Later and the upcoming release notes. Refresh the measured non-Latin near-complete backlog from 631 to 571 values.

Thanks to xet7 !
2026-08-23 18:52:22 +03:00
Lauri Ojansivu d233b4fd44 Translate Office and API reports into Khmer.
Fill the twelve translatable report strings across all three Khmer language tags, while keeping universal API and configuration literals unchanged. Add regression coverage for completeness, Khmer script, variant consistency and preserved literals.

Thanks to xet7 !
2026-08-23 18:52:21 +03:00
Lauri Ojansivu aac41827e9 Translate Office and API reports into Belarusian and Bulgarian.
Fill twelve report placeholders in Belarusian and Bulgarian while preserving REST API, IPv4, IPv6 and WITH_API=true terminology. Add regression coverage for every report key, distinct vocabulary, and Cyrillic script.

Thanks to xet7 !
2026-08-23 18:52:21 +03:00
Lauri Ojansivu de3ebb4eec Document Greek report translations. 2026-08-23 18:52:21 +03:00
Lauri Ojansivu 6d0e5b845b Translate Office and API reports into Greek.
Fill twelve report placeholders across both Greek language tags while preserving REST API, IPv4, IPv6 and WITH_API=true terminology. Add regression coverage for every report key, Greek script, and both tags.

Thanks to xet7 !
2026-08-23 18:52:21 +03:00
Lauri Ojansivu a9b92eb756 Document Hindi and Gujarati report translations. 2026-08-23 18:52:21 +03:00
Lauri Ojansivu 31946787b5 Translate Office and API reports into Hindi and Gujarati.
Fill twelve report placeholders across two Hindi and one Gujarati language tag while preserving REST API, IPv4, IPv6 and WITH_API=true terminology. Add regression coverage for every report key, both Indic scripts, and every tag.

Thanks to xet7 !
2026-08-23 18:52:21 +03:00
Lauri Ojansivu 5c4a9252e8 Document Hebrew and Persian report translations. 2026-08-23 18:52:21 +03:00
Lauri Ojansivu ebfcbd986a Translate Office and API reports into Hebrew and Persian.
Fill twelve report placeholders across two Hebrew and two Persian language tags while preserving REST API, IPv4, IPv6 and WITH_API=true terminology. Add regression coverage for every report key, both right-to-left scripts, and every tag.

Thanks to xet7 !
2026-08-23 18:52:21 +03:00
Lauri Ojansivu a3d24a79b6 Document Arabic report translations. 2026-08-23 18:52:21 +03:00
Lauri Ojansivu c2235fbdae Translate Office and API reports into Arabic.
Fill twelve report placeholders across four Arabic language tags while preserving REST API, IPv4, IPv6 and WITH_API=true terminology. Add regression coverage for every report key, Arabic script, and language tag.

Thanks to xet7 !
2026-08-23 18:52:21 +03:00
Lauri Ojansivu 98a8a1cb7f Document Russian and Ukrainian report translations. 2026-08-23 18:52:21 +03:00
Lauri Ojansivu 58b510e49a Translate Office and API reports into Russian and Ukrainian.
Fill twelve report placeholders across four Russian and two Ukrainian language tags while preserving REST API, IPv4, IPv6 and WITH_API=true terminology. Add regression coverage for every report key and language tag.

Thanks to xet7 !
2026-08-23 18:52:21 +03:00
Lauri Ojansivu f733220ca3 Document Japanese and Korean report translations. 2026-08-23 18:52:21 +03:00
Lauri Ojansivu 18ce975c77 Translate Office and API reports into Japanese and Korean.
Fill twelve report placeholders across three Japanese and two Korean language tags while preserving REST API, IPv4, IPv6 and WITH_API=true terminology. Add regression coverage for every report key and language tag.

Thanks to xet7 !
2026-08-23 18:52:21 +03:00
Lauri Ojansivu 842f0d6243 Document Chinese report translations. 2026-08-23 18:52:20 +03:00
Lauri Ojansivu df7bb69765 Translate Office and API reports into Chinese.
Fill twelve report placeholders across eleven simplified and traditional Chinese language tags while preserving REST API, IPv4, IPv6 and WITH_API=true terminology. Add regression coverage for every tag and report key.

Thanks to xet7 !
2026-08-23 18:52:20 +03:00
Lauri Ojansivu 32a7479a72 Document versioned IP-label translations. 2026-08-23 18:52:20 +03:00
Lauri Ojansivu eec7ea4955 Translate IPv4 and IPv6 address labels.
Reuse each language's existing IP-address vocabulary and word order for the versioned protocol labels. Reset two obsolete English-source sentences so the placeholder-only workflow can see them, and keep universal protocol tokens out of wrong-script prose detection.

Thanks to xet7 !
2026-08-23 18:52:20 +03:00
github-actions[bot] b9a46a9197 Bump versions for v11.09 2026-08-23 11:59:59 +00:00
Lauri Ojansivu 9dd7e8a39a Prepare v11.09 release 2026-08-23 14:58:51 +03:00
Lauri Ojansivu dee8a10616 Document Playwright test stabilization. 2026-08-23 12:25:48 +03:00
Lauri Ojansivu 47b2779321 Stabilize Playwright authentication and WebKit runs. 2026-08-23 12:24:32 +03:00
github-actions[bot] 64d69a1b7e Bump versions for v11.08 2026-08-22 23:18:29 +00:00
Lauri Ojansivu 702615efac Prepare v11.08 release 2026-08-23 02:17:27 +03:00
Lauri Ojansivu 53c52f3b8e Document complete mode wiring coverage. 2026-08-23 02:07:18 +03:00
Lauri Ojansivu ba1c1b1e40 Update complete test mode wiring checks. 2026-08-23 02:07:03 +03:00
Lauri Ojansivu 64e615aa06 Document complete test cleanup. 2026-08-23 01:02:58 +03:00
Lauri Ojansivu 09db353b6c Clean up tests before complete runs. 2026-08-23 01:02:33 +03:00
Lauri Ojansivu 279d442e73 Document complete test execution modes. 2026-08-23 00:38:18 +03:00
Lauri Ojansivu c51e6938dc Add three complete test execution modes. 2026-08-23 00:37:34 +03:00
Lauri Ojansivu 157bb2bdaf Document relevant Playwright progress. 2026-08-23 00:27:05 +03:00
Lauri Ojansivu 9f459303a6 Keep Playwright browser progress relevant. 2026-08-23 00:26:41 +03:00
Lauri Ojansivu 3a8f03b6ae Document platform resource remediation. 2026-08-23 00:16:54 +03:00
Lauri Ojansivu 6ec619e438 Bound platform resources and report runtime failures. 2026-08-23 00:16:15 +03:00
Lauri Ojansivu 933797018f CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-22 23:50:32 +03:00
Lauri Ojansivu bf61dbd624 Document sandbox-local task tool guidance.
Thanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu e8388314ab Document sandbox-local task toolchains.
Keep version-matched development tools and RapidOCR under the ignored .tools directory when the Flatpak sandbox lacks them.\n\nThanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu 8716f504e5 Document bounded full test-suite resources.
Thanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu 69e9a8ba14 Bound full test-suite resource use.
Remove the CPU-exec regression's duplicate nested shell, limit Go package compilation during EVERYTHING, and avoid preloading unused FerretDB module graphs before conformance.\n\nThanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu e95a72935c Document bounded test runtime memory.
Thanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu 1753280610 Bound test runtime memory.
Keep the larger adaptive heap for Meteor compilation, while limiting the long-lived bundle server and Node, E2E, and browser test processes to 2-4 GiB each. Allow explicit test-only overrides without weakening build capacity.\n\nThanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu f99068dd35 Document macOS zsh script support.
Thanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu ef8b1043e4 Support macOS zsh script invocation.
Thanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu 1e74333e35 Document enterprise Linux snap prerequisites.
Thanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu 6d6c574cd0 Configure EPEL for snapd on enterprise Linux.
Thanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu 87b9effcf4 Document additional Linux host support.
Thanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu 87252792f7 Support additional Linux package managers.
Thanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu 18b2cbbef1 Document direct FerretDB test path fix.
Thanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu 7231c6f69a Use .tools FerretDB from direct test entry.
Thanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu 93000975e9 Document local test toolchain fixes.
Thanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu f7f7fc73e3 Use repository-local test toolchain.
Thanks to xet7 !
2026-08-22 23:50:27 +03:00
Lauri Ojansivu d4f85582b1 Document REST security fixes.
Thanks to Char0n1507, senti-man and xet7 !
2026-08-22 23:50:26 +03:00
Lauri Ojansivu 1fa0b158ca Fix REST authorization and error handling.
Thanks to Char0n1507, senti-man and xet7 !
2026-08-22 23:50:26 +03:00
Lauri Ojansivu 896639ffc3 Update conformance test for .tools log path.
Thanks to xet7 !
2026-08-22 23:50:26 +03:00
Lauri Ojansivu 034d20fac8 Document instruction path updates.
Add the synchronized AGENTS.md and CLAUDE.md paths to the upcoming release notes.

Thanks to xet7 !
2026-08-22 23:50:26 +03:00
Lauri Ojansivu c9b88b32a4 Update instruction file tool paths.
Point website and test-log guidance at .tools/wekan.fi and .tools/log in both repository instruction files.

Thanks to xet7 !
2026-08-22 23:50:26 +03:00
Lauri Ojansivu 2c8a11e046 Document .tools path migration.
Record the repository-local website checkout and log directory paths in the upcoming release notes.

Thanks to xet7 !
2026-08-22 23:50:26 +03:00
Lauri Ojansivu 299f943f20 Move website and log paths into .tools.
Use .tools/wekan.fi for the website companion checkout and .tools/log for shell and Windows batch-script output.

Thanks to xet7 !
2026-08-22 23:50:26 +03:00
Lauri Ojansivu a04d5c8127 Update vscodium Flatpak sandbox icon name.
Thanks to xet7 !
2026-08-22 23:50:26 +03:00
Lauri Ojansivu 4124fc9a6c Document Fedora shell script support.
Add the Fedora Workstation dependency and release-tooling changes to the upcoming release notes.

Thanks to xet7 !
2026-08-22 23:50:26 +03:00
Lauri Ojansivu c34daf2bdf Add Fedora support to shell scripts.
Detect Fedora hosts and use dnf package names and setup commands while preserving Debian, Ubuntu, and macOS paths.

Thanks to xet7 !
2026-08-22 23:50:26 +03:00
Lauri Ojansivu 1a098b5f48 Change vscode to vscodium.
Thanks to xet7 !
2026-08-22 23:50:26 +03:00
Lauri Ojansivu f5cc8e3731 Merge pull request #6624 from wekan/dependabot/npm_and_yarn/aws-sdk/lib-storage-3.1114.0
Bump @aws-sdk/lib-storage from 3.1109.0 to 3.1114.0
2026-08-22 23:46:25 +03:00
dependabot[bot] ef6e2311bb Bump @aws-sdk/lib-storage from 3.1109.0 to 3.1114.0
Bumps [@aws-sdk/lib-storage](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/lib/lib-storage) from 3.1109.0 to 3.1114.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/lib/lib-storage/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1114.0/lib/lib-storage)

---
updated-dependencies:
- dependency-name: "@aws-sdk/lib-storage"
  dependency-version: 3.1114.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-22 20:45:00 +00:00
Lauri Ojansivu 3178405e3f Merge pull request #6625 from wekan/dependabot/npm_and_yarn/dompurify-3.4.14
Bump dompurify from 3.4.13 to 3.4.14
2026-08-22 23:43:16 +03:00
Lauri Ojansivu fffa7ce71e Merge pull request #6623 from wekan/dependabot/npm_and_yarn/google-cloud/storage-8.0.1
Bump @google-cloud/storage from 7.22.0 to 8.0.1
2026-08-22 23:42:44 +03:00
Lauri Ojansivu 50bca2aa38 Merge pull request #6622 from wekan/dependabot/npm_and_yarn/aws-sdk/client-s3-3.1114.0
Bump @aws-sdk/client-s3 from 3.1113.0 to 3.1114.0
2026-08-22 23:42:31 +03:00
dependabot[bot] a8ceabbf01 Bump dompurify from 3.4.13 to 3.4.14
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.4.13 to 3.4.14.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.4.13...3.4.14)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-22 20:33:59 +00:00
dependabot[bot] c3a530d723 Bump @google-cloud/storage from 7.22.0 to 8.0.1
Bumps [@google-cloud/storage](https://github.com/googleapis/google-cloud-node/tree/HEAD/handwritten/storage) from 7.22.0 to 8.0.1.
- [Release notes](https://github.com/googleapis/google-cloud-node/releases)
- [Changelog](https://github.com/googleapis/google-cloud-node/blob/main/handwritten/storage/CHANGELOG.md)
- [Commits](https://github.com/googleapis/google-cloud-node/commits/common-v8.0.1/handwritten/storage)

---
updated-dependencies:
- dependency-name: "@google-cloud/storage"
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-22 20:33:17 +00:00
dependabot[bot] ba2e1f5b26 Bump @aws-sdk/client-s3 from 3.1113.0 to 3.1114.0
Bumps [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) from 3.1113.0 to 3.1114.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1114.0/clients/client-s3)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-s3"
  dependency-version: 3.1114.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-22 20:33:03 +00:00
github-actions[bot] 27235e7ead Bump versions for v11.07 2026-08-21 21:23:51 +00:00
Lauri Ojansivu 8c0d8db858 Prepare v11.07 release 2026-08-22 00:22:48 +03:00
Lauri Ojansivu 099b34dd87 Clarify Upcoming security areas and provenance.
Thanks to xet7 !
2026-08-22 00:12:44 +03:00
Lauri Ojansivu 29a8872c3d Document the TenantBleed security fix.
Thanks to Char0n1507 and xet7 !
2026-08-21 23:42:13 +03:00
Lauri Ojansivu 216146edc9 Restrict Organization and Team DDP writes.
Thanks to Char0n1507 and xet7 !

Fixes GHSA-p4cq-83j9-7g73.
2026-08-21 23:41:09 +03:00
Lauri Ojansivu 436e6c06b3 Document the AssignedBleed security fix.
Thanks to Char0n1507 and xet7 !
2026-08-21 23:09:50 +03:00
Lauri Ojansivu d1c75e9950 Use canonical REST board write access.
Thanks to Char0n1507 and xet7 !

Fixes GHSA-f396-42fx-vr88.
2026-08-21 23:08:34 +03:00
Lauri Ojansivu f80cb1c6c9 Document the CalendarBleed security fix.
Thanks to Char0n1507 and xet7 !
2026-08-21 22:56:05 +03:00
Lauri Ojansivu e062042c15 Require write access for ICS imports.
Thanks to Char0n1507 and xet7 !

Fixes GHSA-fpm6-r5fg-2mrg.
2026-08-21 22:54:42 +03:00
Lauri Ojansivu 2a5ff932f6 Document restored card history and destinations.
Thanks to rmb82 and xet7 !
2026-08-21 22:49:22 +03:00
Lauri Ojansivu 394a89fbc3 Restore card history and destination lists.
Thanks to rmb82 and xet7 !

Fixes #6619,
Fixes #6620.
2026-08-21 22:48:57 +03:00
Lauri Ojansivu bf374539a8 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-21 14:45:12 +03:00
Lauri Ojansivu dbf3cfb977 Update Upcoming for resilient riscv64 snap builds.
Remove the completed Launchpad queue item from TODO Later and document the deterministic retry and clean hand-off behavior.

Thanks to xet7 !
2026-08-21 14:45:12 +03:00
Lauri Ojansivu d11987fad0 Hand off queued riscv64 snap builds cleanly.
Keep the flattened Launchpad snapshot deterministic across retries, stop the local riscv64 waiter before GitHub's six-hour cap, and publish only after a validated snap was downloaded. Add positive and negative workflow regression coverage.

Thanks to xet7 !
2026-08-21 14:45:12 +03:00
github-actions[bot] bee4e71d86 Bump versions for v11.06 2026-08-20 23:26:02 +00:00
Lauri Ojansivu 1b7c8aa0b2 Prepare v11.06 release 2026-08-21 02:24:48 +03:00
Lauri Ojansivu d599f50cc2 Document corrected card regression suites.
Thanks to xet7 !
2026-08-21 02:22:21 +03:00
Lauri Ojansivu d61772c085 Update card icon and date regression expectations.
Assert accessible Add icon controls instead of visible Add text, and match the Card-wrapped date context restored in v11.05.

Thanks to xet7 !
2026-08-21 02:20:23 +03:00
Lauri Ojansivu 904b9e6232 Document card person action icons.
Thanks to xet7 !
2026-08-21 01:16:41 +03:00
Lauri Ojansivu 96e598059a Use icons for card person add actions.
Replace the Requested By and Assigned By Add text with plus icons while retaining localized tooltips and accessible names.

Thanks to xet7 !
2026-08-21 01:16:28 +03:00
Lauri Ojansivu 334c919386 Document linked-card label editing.
Thanks to xet7 !
2026-08-21 01:13:56 +03:00
Lauri Ojansivu e633f3c746 Add labels through linked cards.
Keep the popup card as the explicit mutation target and resolve label definitions and creation against the linked source board.

Thanks to xet7 !
2026-08-21 01:13:45 +03:00
Lauri Ojansivu 3532b54d3c Document restored card and LDAP behavior.
Thanks to mimZD, ClemStrummer, Nissulya, rmb82 and xet7 !
2026-08-21 00:31:21 +03:00
Lauri Ojansivu 3a87e33782 Restore card lists, labels, and LDAP full names.
Include board-wide lists in every swimlane chooser, keep label pickers usable when a linked source board is unavailable, tolerate reactive cache gaps while logging moves, and normalize LDAP display names to text.

Thanks to mimZD, ClemStrummer, Nissulya, rmb82 and xet7 !

Fixes #6614,
Fixes #6616,
Fixes #6617,
Fixes #6618.
2026-08-21 00:30:57 +03:00
github-actions[bot] 26af801f81 Bump versions for v11.05 2026-08-19 14:09:02 +00:00
Lauri Ojansivu 8ee4808179 Prepare v11.05 release 2026-08-19 17:07:54 +03:00
Lauri Ojansivu 8520764151 Update Upcoming WeKan changelog.
Thanks to xet7 !
2026-08-19 17:07:31 +03:00
Lauri Ojansivu 2be7aac2b4 Wait for bundles before Launchpad snap builds.
Keep a validated snap when Snapcraft only fails during post-download cleanup,
and skip optional Launchpad builds whose release bundle is absent.

Thanks to xet7 !
2026-08-19 17:07:03 +03:00
Lauri Ojansivu 977d92af9b CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-19 17:00:32 +03:00
Lauri Ojansivu 569388d3c0 Update Upcoming WeKan changelog.
Thanks to ClemStrummer and xet7 !
2026-08-19 17:00:32 +03:00
Lauri Ojansivu e909b475b3 Restore opened-card editing with saved dates.
Pass the Card explicitly beside named Blaze date-template arguments and resolve both direct and wrapped contexts before reading dates or opening editors. Cover existing cards with all dates and a label through title editing.\n\nThanks to ClemStrummer and xet7 !\n\nFixes #6615.
2026-08-19 17:00:32 +03:00
github-actions[bot] b673558a23 Bump versions for v11.04 2026-08-19 13:43:01 +00:00
Lauri Ojansivu 60312a4cc6 Prepare v11.04 release 2026-08-19 16:42:00 +03:00
Lauri Ojansivu 9fc61e97de Updated .gitignore 2026-08-19 16:41:46 +03:00
Lauri Ojansivu 4c97f46e56 Update Upcoming WeKan changelog.
Thanks to Meteor developers and xet7 !
2026-08-19 16:40:38 +03:00
Lauri Ojansivu 9059b1824e Upgrade to Meteor 3.5.2-beta.0 .
Thanks to Meteor developers !
2026-08-19 14:47:17 +03:00
Lauri Ojansivu 42d6306a2f CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-19 13:48:00 +03:00
Lauri Ojansivu df8450a8f2 Update Upcoming WeKan changelog.
Thanks to xet7 !
2026-08-19 13:48:00 +03:00
Lauri Ojansivu 6c118812f4 Stabilize date editor persistence.
Keep native date and time inputs synchronized with the reactive popup draft, and await every date persistence operation before closing the popup. Add regression coverage for the async callbacks and the Chromium start-date interaction.\n\nThanks to xet7 !
2026-08-19 13:48:00 +03:00
github-actions[bot] 24c5b9078d Bump versions for v11.03 2026-08-19 08:54:51 +00:00
Lauri Ojansivu d56154f985 Prepare v11.03 release 2026-08-19 11:53:47 +03:00
Lauri Ojansivu 37d956188e Update Upcoming WeKan changelog.
Document Flatpak host-Docker discovery and the verified Firefox and WebKit browser matrices.

Thanks to xet7 !
2026-08-19 06:30:02 +03:00
Lauri Ojansivu 5ae63a5341 Run Playwright Docker from Flatpak.
Discover the host Docker CLI through flatpak-spawn when VS Code's sandbox does not expose it in PATH. Route Playwright image pulls, browser containers, and conformance cleanup through one host-aware wrapper, with regression coverage.

Thanks to xet7 !
2026-08-19 06:29:27 +03:00
Lauri Ojansivu 0ca84c0eb1 Update Upcoming WeKan changelog.
Document the completed browser regression corrections and their fresh production-bundle coverage.

Thanks to xet7 !
2026-08-19 02:17:46 +03:00
Lauri Ojansivu f9f9fa4c2d Finish browser regression corrections.
Refresh linked-card and date publications after async updates, keep popup keyboard focus contained, correct the phone board-list scroll chain, and make the browser regressions use stable interaction targets. Add coverage for linked-card fields and editing, existing dates, minicard title editing, popup focus, accessibility, and phone board grids.

Thanks to xet7 !
2026-08-19 02:17:23 +03:00
Lauri Ojansivu c8372fd538 Updated dependencies.
Thanks to developers of dependencies !
2026-08-19 01:15:43 +03:00
Lauri Ojansivu be826d1bdd Document the complete browser regression repairs.
Thanks to xet7 !
2026-08-19 01:10:35 +03:00
Lauri Ojansivu c476ee45a9 Repair browser regressions found by the complete test run.
Restore inline minicard title editing, editable opened-card dates, popup focus containment, accessible loading-state controls, phone board scrolling, and browser assertions for the current card UI.

Thanks to xet7 !
2026-08-19 01:10:06 +03:00
Lauri Ojansivu 540c79aa49 Repair changelog release placement and document test fixes.
Move the post-v11.01 archive fix back to Upcoming, give v11.02 its actual release-metadata entry, and document the existing-board custom-fields backfill and restored Node suite.\n\nThanks to xet7 !
2026-08-19 00:11:16 +03:00
Lauri Ojansivu 491781c449 Restore the complete WeKan Node test suite.
Backfill the new default-on opened-card custom-fields setting for existing boards. Keep regression guards aligned with the intended visibility, date controls, resizable card width, supplied initials and board publication layout, and exclude downloaded Go toolchains from the FerretDB source scan.\n\nThanks to xet7 !
2026-08-19 00:10:46 +03:00
Lauri Ojansivu 143da47c43 Document dependency updates in Upcoming changelog.
Thanks to developers of dependencies and xet7 !
2026-08-18 23:34:31 +03:00
Lauri Ojansivu 06ebc5f622 Updated dependencies.
Thanks to developers of dependencies !
2026-08-18 23:33:17 +03:00
Lauri Ojansivu 14ecaf0684 Clarify proxy location requirements in Upcoming changelog.
Thanks to xet7 !
2026-08-18 23:23:58 +03:00
Lauri Ojansivu eb951b4d86 Document portable JFIF download names.
Thanks to xet7 !
2026-08-18 23:14:30 +03:00
Lauri Ojansivu 518a4d2fec Normalize uploaded JFIF names to JPEG.
Replace the unrecognized JFIF suffix with the detected JPEG extension so downloaded images use a portable filename and open through normal desktop file associations. Keep existing JPG and JPEG suffixes unchanged and cover the sanitization log reason.\n\nThanks to xet7 !
2026-08-18 23:14:12 +03:00
Lauri Ojansivu 4e8b56a566 Document Admin Problems pagination fix.
Thanks to xet7 !
2026-08-18 23:10:44 +03:00
Lauri Ojansivu 2c034643b8 Fix Admin Problems nested pagination.
Keep specialized event-stream and Office pagination events from bubbling into the shared Problems report handler, and safely ignore missing report state during pane transitions. Add regression coverage for Next, Previous, and search controls.\n\nThanks to xet7 !
2026-08-18 23:10:11 +03:00
Lauri Ojansivu 1d1c796648 Document People country login location reports.
Thanks to xet7 !
2026-08-18 23:08:06 +03:00
Lauri Ojansivu ff9d0bab8b Add country login locations to Admin Panel People.
Place a Location column immediately before account Status. Each user shows a flag and successful-login count for every country known from that user's addresses.

Clicking a country opens a Table.md report for that person. Its left menu lists countries busiest first, while the selected country shows city, IPv4, IPv6, and the person's first and last login times for each address. Search, pagination and Back use the shared table controls.

Location details are fetched once per People page and restricted through the same site-admin or tenant-admin scope as the People list. Unknown locations are not guessed, stale page responses are ignored, and new logins retain their supplied location on the per-user address tally.

Focused positive and negative coverage checks country totals, ordering, both address families, city rows, timestamps, menu/table wiring, authorization and missing geography.

Thanks to xet7 !
2026-08-18 23:07:48 +03:00
Lauri Ojansivu baa23595e1 Document the Offices initials fallback fix.
Thanks to xet7 !
2026-08-18 22:50:13 +03:00
Lauri Ojansivu 480074969b Show available initials in the Offices people column.
The server computed each person's initials, but person grouping discarded them and the shared table tried to find the user again in client cache. Users not published to that cache therefore rendered as empty grey circles.

Carry initials through every table layer and let the existing avatar template render supplied initials before falling back to its reactive user lookup.

Regression coverage checks the person-grouped response, shared table cell and direct initials fallback.

Thanks to xet7 !
2026-08-18 22:49:53 +03:00
Lauri Ojansivu 290b56a567 Document person-grouped Offices login addresses.
Thanks to xet7 !
2026-08-18 22:48:47 +03:00
Lauri Ojansivu 3539f2bbf3 Group Admin Panel Offices login addresses by person.
Show each person's successful-login addresses together, with separate IPv4 and IPv6 columns, the latest country flag and city supplied by Cloudflare or another supported proxy, and that person's login count for each address.

Search continues to match usernames, full names, addresses and locations. Address metadata for each page is fetched in one batch, and people sharing an address remain separate groups with their own counts.

Focused coverage checks grouping, shared addresses, both address families, proxy location headers, missing locations and the person-first server response.

Thanks to xet7 !
2026-08-18 22:48:26 +03:00
Lauri Ojansivu f307325566 Document the Rspack development heap fix.
Thanks to xet7 !
2026-08-18 22:43:10 +03:00
Lauri Ojansivu e1f842eb56 Prevent Rspack development watchers exhausting the heap.
Long-running meteor run sessions repeatedly rebuild WeKan's large Blaze module graph. Rspack 1.x's experimental persistent cache retains JavaScript-side serialization state across those rebuilds and can eventually consume the full 16 GB V8 heap.

Disable both cache layers only for watch-mode runs. Short-lived production builds keep their persistent cache, and unrelated Rspack experiments continue to merge normally.

Focused configuration coverage checks client and server watchers, the production negative case, and merging over Meteor's cache defaults.

Thanks to xet7 !
2026-08-18 22:42:33 +03:00
Lauri Ojansivu b92dc3347e Document accessibility helper deduplication.
Thanks to xet7 !
2026-08-18 22:38:40 +03:00
Lauri Ojansivu fce4d79453 Deduplicate accessibility keyboard helpers.
Thanks to xet7 !
2026-08-18 22:38:18 +03:00
Lauri Ojansivu 4d17e20817 Document accessibility and tab-order improvements.
Thanks to xet7 !
2026-08-18 22:30:26 +03:00
Lauri Ojansivu 692207bf5e Improve accessibility and tab order across WeKan.
Thanks to xet7 !
2026-08-18 22:30:13 +03:00
Lauri Ojansivu 4f0abcef1d Document accessible card Edit icons.
Thanks to xet7 !
2026-08-18 22:19:34 +03:00
Lauri Ojansivu cc609f9feb Use accessible icons for card Edit actions.
Thanks to xet7 !
2026-08-18 22:18:44 +03:00
Lauri Ojansivu 02cc238164 Document aligned Text editor actions.
Thanks to xet7 !
2026-08-18 21:44:55 +03:00
Lauri Ojansivu 791a3d35a4 Align Text editor actions.
Place Convert to Markdown on the same top control row immediately before Copy in custom field Text editors.

Thanks to xet7 !
2026-08-18 21:44:45 +03:00
Lauri Ojansivu 2c2830cddc Document populated custom field editors.
Thanks to xet7 !
2026-08-18 21:40:26 +03:00
Lauri Ojansivu 9ff711f095 Populate custom field editors.
Bind Text, Number, Checkbox and Currency editors to their persisted template values, and keep Dropdown selection synchronized with its current saved value. Cover every custom-field type's edit initialization.

Thanks to xet7 !
2026-08-18 21:40:16 +03:00
Lauri Ojansivu febecacec0 Document Dropdown value preselection.
Thanks to xet7 !
2026-08-18 21:36:21 +03:00
Lauri Ojansivu 93c6c5d51b Preselect saved Dropdown values.
Keep the persisted custom-field value outside the option iteration context and use it to select the matching option when editing.

Thanks to xet7 !
2026-08-18 21:36:10 +03:00
Lauri Ojansivu 8a2036c515 Document the Date controls row.
Thanks to xet7 !
2026-08-18 21:32:19 +03:00
Lauri Ojansivu 4f513437be Keep Date controls on one row.
Apply the flex layout in the shared datepicker so lowercase custom-field popup names also render Date, Time and Copy from left to right.

Thanks to xet7 !
2026-08-18 21:32:09 +03:00
Lauri Ojansivu 4b42808179 Document Date Copy placement.
Thanks to xet7 !
2026-08-18 21:28:11 +03:00
Lauri Ojansivu 75b17fe838 Place Date Copy after Time.
Force Date, Time and Copy into that visual order and remove the redundant in-form X because the popup header already closes the editor.

Thanks to xet7 !
2026-08-18 21:28:00 +03:00
Lauri Ojansivu 8ba3e40c37 Document Date Copy beside Time.
Thanks to xet7 !
2026-08-18 21:24:01 +03:00
Lauri Ojansivu 413844882a Place Date Copy beside the Time input.
Move the custom field Copy control into the Date and Time row and remove the reserved space above those inputs.

Thanks to xet7 !
2026-08-18 21:23:49 +03:00
Lauri Ojansivu c00b3933b7 Document the visible Date Copy control.
Thanks to xet7 !
2026-08-18 21:19:51 +03:00
Lauri Ojansivu df2426081d Keep Date custom field Copy visible.
Reserve an internal popup action row and place Copy at its top-right instead of clipping it above the Date form.

Thanks to xet7 !
2026-08-18 21:19:40 +03:00
Lauri Ojansivu 0b43c2f55d Document the restored Date popup opener.
Thanks to xet7 !
2026-08-18 21:15:50 +03:00
Lauri Ojansivu 1defd17944 Restore direct Date custom field popup opening.
Return the visible datetime to its original js-edit-date opener and remove the synthetic event, expanded blank target and shared Date route. The title activates that same visible opener.

Thanks to xet7 !
2026-08-18 21:15:29 +03:00
Lauri Ojansivu f3ad261c78 Document nested custom field value clicks.
Thanks to xet7 !
2026-08-18 21:13:06 +03:00
Lauri Ojansivu 8da9be49b6 Handle custom field value clicks in nested templates.
Register the edit route in every type template so Date text and empty space open the popup despite the Blaze boundary, while Checkbox squares remain direct toggles.

Thanks to xet7 !
2026-08-18 21:12:53 +03:00
Lauri Ojansivu e8dce7034e Document minicard custom field formatting fixes.
Thanks to xet7 !
2026-08-18 21:00:28 +03:00
Lauri Ojansivu 3b17a9f859 Fix minicard custom field formatting contexts.
Format Currency and String Template values from the current custom-field row instead of calling Card helpers on that row, with safe empty and invalid-value fallbacks.

Thanks to xet7 !
2026-08-18 21:00:01 +03:00
Lauri Ojansivu 8ff63ab3f9 Document the larger Date custom field edit target.
Thanks to xet7 !
2026-08-18 20:57:47 +03:00
Lauri Ojansivu 8c07000b3c Extend the custom date edit target below its value.
Make the lower empty area of a Date custom field clickable so it opens the same popup as the date text and title.

Thanks to xet7 !
2026-08-18 20:57:33 +03:00
Lauri Ojansivu f6feb24786 Document custom field date popup placement.
Thanks to xet7 !
2026-08-18 20:54:44 +03:00
Lauri Ojansivu ae74bc3bd7 Anchor custom field date popups below their values.
Route title and value clicks through the visible date element instead of a hidden generic trigger so the edit popup opens below the custom field date.

Thanks to xet7 !
2026-08-18 20:54:30 +03:00
Lauri Ojansivu 8d79a2d4f7 Document the String Template close control.
Thanks to xet7 !
2026-08-18 20:50:21 +03:00
Lauri Ojansivu 3928242d5f Add an X to String Template custom fields.
Show the standard close control immediately after Save while preserving restoration of the previously saved template items.

Thanks to xet7 !
2026-08-18 20:50:11 +03:00
Lauri Ojansivu 9971ccb796 Document editable custom field checkboxes.
Thanks to xet7 !
2026-08-18 20:48:54 +03:00
Lauri Ojansivu 31416ce6dd Toggle checkboxes while editing custom fields.
Reflect staged checked and unchecked values immediately in the Checkbox editor while continuing to persist them only when Save is clicked.

Thanks to xet7 !
2026-08-18 20:48:41 +03:00
Lauri Ojansivu 0a6deabec9 Document custom field title and value editing.
Thanks to xet7 !
2026-08-18 20:47:13 +03:00
Lauri Ojansivu 3bb07f8b2d Open custom field editors from titles and values.
Make every displayed custom field value editable by click while preserving the checkbox square as an immediate toggle and using the rest of its row to open editing.

Thanks to xet7 !
2026-08-18 20:46:59 +03:00
Lauri Ojansivu 63a5f54174 Document the non-collapsing layout selector.
Thanks to xet7 !
2026-08-18 20:41:26 +03:00
Lauri Ojansivu 2eff68cb09 Keep Custom Fields open when changing layout.
Handle Grid and List inside the section header and make the collapse handler ignore clicks from that selector.

Thanks to xet7 !
2026-08-18 20:41:16 +03:00
Lauri Ojansivu 893455f58c Document the heading layout selector.
Thanks to xet7 !
2026-08-18 20:40:08 +03:00
Lauri Ojansivu 5c789a6238 Put the layout selector in the Custom Fields heading.
Place Grid and List between the section title and hamburger, hide them while collapsed, and prevent the selector interaction from toggling the section.

Thanks to xet7 !
2026-08-18 20:39:57 +03:00
Lauri Ojansivu eb798127fb Document the Custom Fields selector placement.
Thanks to xet7 !
2026-08-18 20:36:40 +03:00
Lauri Ojansivu 93d681874b Keep the layout selector inside Custom Fields.
Place Grid and List between the section title and its fields, and hide the selector whenever Custom Fields is collapsed.

Thanks to xet7 !
2026-08-18 20:36:30 +03:00
Lauri Ojansivu ea15f7cb54 Document the Grid and List layout selector.
Thanks to xet7 !
2026-08-18 20:31:16 +03:00
Lauri Ojansivu 2d9d41ff46 Show Grid and List icons on the layout toggle.
Replace the unlabeled switch with a paired-icon selector that highlights the active custom fields layout using the theme accent.

Thanks to xet7 !
2026-08-18 20:31:05 +03:00
Lauri Ojansivu 1b2299d1b4 Document responsive custom field grids.
Thanks to xet7 !
2026-08-18 20:28:50 +03:00
Lauri Ojansivu 938e373e2b Add columns to wider custom field grids.
Use an auto-fitting compact grid so widening an opened card increases its custom field columns and reduces its rows.

Thanks to xet7 !
2026-08-18 20:28:32 +03:00
Lauri Ojansivu 626445ae26 Document the restored custom fields layout toggle.
Thanks to xet7 !
2026-08-18 20:25:29 +03:00
Lauri Ojansivu f0ca913d47 Restore the custom fields layout toggle.
Apply the saved preference to switch opened-card custom fields between a compact wrapping grid and one field per row, with focused and browser regression coverage.

Thanks to xet7 !
2026-08-18 20:25:12 +03:00
Lauri Ojansivu e9c2b291d0 Document wider opened card resizing.
Thanks to xet7 !
2026-08-18 20:20:35 +03:00
Lauri Ojansivu 46b276e758 Allow opened cards to resize wider.
Keep the compact 520px opening width while allowing the bottom-right resize handle to grow the desktop card to the viewport edge.

Thanks to xet7 !
2026-08-18 20:20:22 +03:00
Lauri Ojansivu d9dda2ae67 Document top-right custom field copy controls.
Thanks to xet7 !
2026-08-18 20:11:32 +03:00
Lauri Ojansivu a3e5f4ebd0 Place custom field copy controls above editors.
Undo the Save-row alignment and anchor Copy above the top-right corner of every custom field editor.

Thanks to xet7 !
2026-08-18 20:11:11 +03:00
Lauri Ojansivu 2d3e507a35 Document custom field copy alignment.
Thanks to xet7 !
2026-08-18 20:06:34 +03:00
Lauri Ojansivu cb517165d4 Align custom field copy controls.
Keep Copy centered in the Save and Close row by overriding the rich-text toolbar offset that lifted it over Save.

Thanks to xet7 !
2026-08-18 20:06:23 +03:00
Lauri Ojansivu 2c74fac20f Document title-based custom field editing.
Thanks to xet7 !
2026-08-18 20:02:26 +03:00
Lauri Ojansivu 9eb0c76cbd Open custom field editors from their titles.
Keep field controls dedicated to using their values, and show each copy control only inside editing between Save and Close. Add a staged checkbox editor and focused static and browser regression coverage.

Thanks to xet7 !
2026-08-18 20:02:11 +03:00
Lauri Ojansivu 1677f6f1b9 Document custom field copy controls.
Thanks to xet7 !
2026-08-18 19:42:14 +03:00
Lauri Ojansivu ad9300297f Add copy controls to every custom field.
Reuse one clipboard control across number, checkbox, currency, date, dropdown and string-template values while Text keeps the editor's existing copy button.

Thanks to xet7 !
2026-08-18 19:41:51 +03:00
Lauri Ojansivu eae36f1837 Document the text close control.
Record the X placed beside the Custom Field Text Save button.

Thanks to xet7 !
2026-08-18 19:38:38 +03:00
Lauri Ojansivu 3d0e4805ee Add a close button beside text Save.
Render the standard X close control immediately after the Custom Field Text Save button and cover its order in the form.

Thanks to xet7 !
2026-08-18 19:38:27 +03:00
Lauri Ojansivu 0f28c6fcc6 Document the number close control.
Record the X placed beside the Custom Field Number Save button.

Thanks to xet7 !
2026-08-18 19:37:34 +03:00
Lauri Ojansivu 82a0f2f78f Add a close button beside number Save.
Render the standard X close control immediately after the Custom Field Number Save button and cover its order in the form.

Thanks to xet7 !
2026-08-18 19:37:22 +03:00
Lauri Ojansivu 521c9cedb0 Document the dropdown close control.
Record the X placed beside the Custom Field Dropdown Save button.

Thanks to xet7 !
2026-08-18 19:36:35 +03:00
Lauri Ojansivu a67094b1f9 Add a close button beside dropdown Save.
Render the standard X close control immediately after the Custom Field Dropdown Save button and cover its order in the form.

Thanks to xet7 !
2026-08-18 19:36:20 +03:00
Lauri Ojansivu 7270c4e760 Document currency custom field saving.
Describe the acknowledged finite-number write, linked-card authorization, comma-decimal coverage and X close control beside Save.

Thanks to xet7 !
2026-08-18 19:34:23 +03:00
Lauri Ojansivu 6a437ef31d Save currency custom field values.
Persist finite currency values through an acknowledged authorized server method, bind the input to the direct field value, and show the standard X close control beside Save.

Thanks to xet7 !
2026-08-18 19:33:48 +03:00
Lauri Ojansivu a7431a6904 Document custom field visibility controls.
Describe repeated checkbox toggling and the independent default-on card and default-off minicard Custom Fields settings.

Thanks to xet7 !
2026-08-18 19:23:42 +03:00
Lauri Ojansivu 1d4ffd4080 Separate card and minicard custom field visibility.
Allow checkbox values to toggle both ways from the reactive field context. Add board settings that show custom fields on opened cards by default and hide them on minicards by default, with independent menu controls for each surface.

Thanks to xet7 !
2026-08-18 19:23:22 +03:00
Lauri Ojansivu 269351e852 Document opened-card checkbox rendering.
Record that the opened card now reacts to the saved custom checkbox value in step with the minicard badge.

Thanks to xet7 !
2026-08-18 19:14:56 +03:00
Lauri Ojansivu 679d50378d Render saved checkbox values on opened cards.
Bind the opened-card custom checkbox checkmark to its direct persisted value instead of the non-reactive nested data path that left the square visually empty.

Thanks to xet7 !
2026-08-18 19:14:27 +03:00
Lauri Ojansivu 84f8c5f97e Document saved custom checkbox behavior.
Describe the separation between a card checkbox value and the Custom Fields menu visibility control, together with its regression coverage.

Thanks to xet7 !
2026-08-18 19:00:20 +03:00
Lauri Ojansivu 6791ade7fc Save opened-card custom checkbox values.
Give the card value checkbox its own event hook, read the persisted card field before toggling, and stop the click from reaching unrelated visibility or checklist controls.

Thanks to xet7 !
2026-08-18 19:00:01 +03:00
Lauri Ojansivu 3a02d41439 Document editing linked cards from either board.
Remove the completed linked-card editing and avatar publication items from TODO Later and describe the current-board role boundary and regression coverage.

Thanks to hever and xet7 !

Refs #1995.
2026-08-18 18:45:32 +03:00
Lauri Ojansivu 49de60eb04 Edit linked card content from either board.
Delegate source-card edits through a visible active link when the user has write access on the board containing that link. Route content fields, attachments, checklists, subtasks, watchers, labels, custom fields, voting and poker to the source while retaining placement on the linked card.

Thanks to hever and xet7 !

Fixes #1995.
2026-08-18 18:45:14 +03:00
Lauri Ojansivu 5ff6285dec Document complete linked-card field mirroring. 2026-08-18 18:33:03 +03:00
Lauri Ojansivu 8fac3ab891 Mirror every visible source field on linked cards.
Thanks to xet7 !
2026-08-18 18:32:38 +03:00
Lauri Ojansivu 45c5af61ba Document plain coordinate detection. 2026-08-18 18:21:41 +03:00
Lauri Ojansivu 5693c1965a Detect plain coordinate pairs in card locations.
Thanks to xet7 !
2026-08-18 18:21:22 +03:00
Lauri Ojansivu 35bb11fa2f Document the linked-card Blaze view fix. 2026-08-18 18:17:05 +03:00
Lauri Ojansivu a830cae647 Keep linked-card position outside the Blaze event view.
Thanks to xet7 !
2026-08-18 18:16:47 +03:00
Lauri Ojansivu 1e1184b98e CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-18 18:10:48 +03:00
Lauri Ojansivu 89331bd78a Document the phone invitation layout fix. 2026-08-18 18:10:47 +03:00
Lauri Ojansivu dc12789b43 Show board invitations on phone-sized layouts.
Thanks to mimZD and xet7 !

Fixes #6488.
2026-08-18 18:10:47 +03:00
Lauri Ojansivu a3a4588228 Document fixes for issues 6611 through 6613. 2026-08-18 18:10:47 +03:00
Lauri Ojansivu 798390c6fc Restore cross-board linked-card creation.
Thanks to ClemStrummer and xet7 !

Fixes #6613.
2026-08-18 18:10:47 +03:00
Lauri Ojansivu e3588f2a0e Widen card attachment previews.
Thanks to rmb82 and xet7 !

Fixes #6612.
2026-08-18 18:10:47 +03:00
Lauri Ojansivu a7879012c9 Fix custom-field interactions and deleted-field exports.
Thanks to Heart1010 and xet7 !

Fixes #6611.
2026-08-18 18:10:47 +03:00
Lauri Ojansivu 5eb99057a5 Merge pull request #6610 from melbinjp/fix-theme-doc-path
docs: the Theme file table points at the pre-reorganisation path
2026-08-18 18:04:07 +03:00
melbinjp 492522b67a docs: point the Theme file table at the reorganised path
docs/Features/Page/Theme.md listed docs/Theme/Theme.md, which moved to
docs/Features/Theme/Theme.md in c4cb87345 (Reorganize documentation and repair
links., 2026-08-17). The table row still pointed at the old location.
2026-08-18 10:59:40 +05:30
github-actions[bot] e1f7fbc411 Bump versions for v11.02 2026-08-17 21:32:53 +00:00
Lauri Ojansivu b82ecfdd34 Prepare v11.02 release 2026-08-18 00:30:22 +03:00
Lauri Ojansivu 80501753a8 Document the issue 6608 archive fixes. 2026-08-18 00:30:02 +03:00
Lauri Ojansivu 0aad02722c Fix archived board deletion and bulk card archiving.
Archive board deletion is available through the gated, confirmed board
multi-selection action. Card multi-selection now uses one awaited and
server-authorized bulk archive operation, preserving the selection and showing
the server error when it fails.

Thanks to Nissulya and xet7 !

Fixes #6608.
2026-08-18 00:30:02 +03:00
github-actions[bot] 7491de39b8 Bump versions for v11.01 2026-08-17 21:21:43 +00:00
Lauri Ojansivu 7797e787df Prepare v11.01 release 2026-08-18 00:20:33 +03:00
Lauri Ojansivu 1253dff0ab Document permanent-delete argument validation. 2026-08-18 00:18:06 +03:00
Lauri Ojansivu 5a58328465 Check permanent-delete arguments before async work. 2026-08-18 00:17:42 +03:00
Lauri Ojansivu 75ae547995 Document Recovery guidance in Delete settings. 2026-08-18 00:12:33 +03:00
Lauri Ojansivu 71abf3f487 Explain Recovery auditing in Delete settings. 2026-08-18 00:12:16 +03:00
Lauri Ojansivu 3c5c75d354 Document expanded board-control translations. 2026-08-18 00:06:42 +03:00
Lauri Ojansivu 6cab5c7a49 Translate recent board controls into 69 locales. 2026-08-18 00:06:15 +03:00
Lauri Ojansivu 6965471a07 Document translations of recent board controls. 2026-08-18 00:03:59 +03:00
Lauri Ojansivu 34f8b000f7 Translate recent board controls into major languages. 2026-08-18 00:03:32 +03:00
Lauri Ojansivu a02d03a133 Document the Recovery audit description. 2026-08-18 00:01:53 +03:00
Lauri Ojansivu 4b418b0dfd Explain permanent-delete auditing in Recovery. 2026-08-18 00:01:36 +03:00
Lauri Ojansivu bfeb839591 Document Recovery outcome filtering. 2026-08-17 23:58:25 +03:00
Lauri Ojansivu 5f1d422117 Filter Recovery events by operation outcome. 2026-08-17 23:58:04 +03:00
Lauri Ojansivu 9b08008db3 Document complete permanent-delete attempt audits. 2026-08-17 23:55:38 +03:00
Lauri Ojansivu 7a0b7b7cdb Audit failed permanent-delete attempts with status and address.
Record successful, failed, and unauthorized permanent-delete attempts in Recovery with Boolean Done and deleted-data fields, actor ID and username, proxy-aware IPv4 or IPv6, and requested board IDs and titles. Render Done first with green success, red failure, and yellow deletion icons. Preserve original errors after recording failed attempts.

Thanks to xet7 !
2026-08-17 23:54:36 +03:00
Lauri Ojansivu 4c556d2ba5 Document permanent-delete Recovery audits. 2026-08-17 23:43:53 +03:00
Lauri Ojansivu 134bd48de5 Audit permanent-delete changes and board purges.
Route the Admin Panel permanent-delete toggle through a Global Admin-only server method and record actual enabled or disabled changes in Recovery with the actor username and ID. After each successful permanent board removal, record the actor plus board ID and title. Do not log no-op setting writes, failed removals, or unauthorized calls.

Thanks to xet7 !
2026-08-17 23:43:06 +03:00
Lauri Ojansivu 1a38d8cf9f Document the Multi-Selection sidebar close behavior. 2026-08-17 23:36:01 +03:00
Lauri Ojansivu 782200a0da Turn Multi-Selection off from the sidebar close button.
When the All Boards right-sidebar X is clicked during Multi-Selection, disable selection mode before closing the guarded sidebar. Preserve the ordinary close behavior for other sidebar views and cover the operation order.

Thanks to xet7 !
2026-08-17 23:35:43 +03:00
Lauri Ojansivu c044a39924 Document the Global Admin Delete gate. 2026-08-17 23:32:50 +03:00
Lauri Ojansivu 2eb3e0d4b8 Require the Global Admin flag for Archive deletion.
Require Users.isAdmin to be exactly true before showing the Archive Delete action or accepting its server method. Board-admin membership and truthy non-Boolean values do not satisfy the site-wide Global Admin gate.

Thanks to xet7 !
2026-08-17 23:32:31 +03:00
Lauri Ojansivu 44eefdbee7 Document the disabled Archive Delete state. 2026-08-17 23:31:43 +03:00
Lauri Ojansivu 76e4294933 Explain disabled Archive permanent deletion.
Replace the Archive checkbox instruction and Delete action with an explanation while permanent deletion is disabled. Restore the instruction and red action only when the Global Admin setting is enabled, with ordered translation fallbacks and positive and negative regression coverage.

Thanks to xet7 !
2026-08-17 23:31:08 +03:00
Lauri Ojansivu b240b880f1 Document Workspace selection controls. 2026-08-17 23:26:26 +03:00
Lauri Ojansivu 7974c3e661 Show board selection controls in Workspaces.
Display the existing Select All and Select None controls when Multi-Selection is active in an existing Workspace. Keep selection scoped to boardsForView so search and the current Workspace determine exactly which rendered boards are selected.

Thanks to xet7 !
2026-08-17 23:26:05 +03:00
Lauri Ojansivu ef31af1785 Document workspace board drop targets. 2026-08-17 23:21:23 +03:00
Lauri Ojansivu f9a0b3e9a2 Allow workspace boards to target Starred and Archive.
Mark workspace-origin drags so Starred and Archive receive green valid-target hints. Keep Starred additive without workspace reassignment, and retain the confirmed multi-board archive path. Cover source marking, valid targets, additive stars, confirmation, and archive calls.

Thanks to xet7 !
2026-08-17 23:20:58 +03:00
Lauri Ojansivu ea6d4f95a5 Document dashboard performance improvements. 2026-08-17 23:08:00 +03:00
Lauri Ojansivu c1522ad2a6 Avoid throttling FerretDB when it is busy. 2026-08-17 23:07:50 +03:00
Lauri Ojansivu 65aec4501b Paginate All Boards in the database. 2026-08-17 23:07:50 +03:00
Lauri Ojansivu 35825c5401 Snapshot lazy card windows on FerretDB.
Fetch each sorted and limited card window once and publish its cards manually instead of returning a limited live cursor that never becomes ready on FerretDB. Keep window child data and client re-subscription behavior unchanged, with positive and negative source-shape coverage.\n\nThanks to xet7 !
2026-08-17 23:07:50 +03:00
Lauri Ojansivu fd0369b899 Skip empty board visibility branches.
Do not add org, team or domain elemMatch clauses whose cleaned id list is empty. Preserve the active direct-member clause and every RevokeBleed security invariant, with positive and negative selector coverage.\n\nThanks to xet7 !
2026-08-17 23:07:50 +03:00
Lauri Ojansivu 7b52a63133 Shrink the global boards publication.
Publish projected board documents without per-board list and card children, and move template containers to a separate live publication subscribed only by the Templates section. Keep the full singular board publication unchanged and cover projection, fan-out removal and template isolation.\n\nThanks to xet7 !
2026-08-17 23:07:50 +03:00
Lauri Ojansivu 0a41fd167d Document the Home empty-state wording.
Thanks to xet7 !
2026-08-17 22:45:11 +03:00
Lauri Ojansivu 74c3873563 Clarify the Home empty-state drag limit.
Change the Home placeholder to Drag only one board here to open it after login, update its design note and CSS comment, and pin the exact English wording in the Home regression suite.\n\nThanks to xet7 !
2026-08-17 22:45:00 +03:00
Lauri Ojansivu 4bb91c966c Document the Remaining drag targets.
Thanks to xet7 !
2026-08-17 22:43:11 +03:00
Lauri Ojansivu 3edc723f2d Add Remaining drag targets for Starred and Archive.
Mark drags from Remaining so Starred and Archive receive green valid-target hints. A Starred drop adds only missing stars without removing boards from Remaining, while Archive continues through the confirmed single or multi-board archive path. Add positive and negative target regression coverage.\n\nThanks to xet7 !
2026-08-17 22:42:57 +03:00
Lauri Ojansivu 649718ec2e Document the archived tile icon removal.
Thanks to xet7 !
2026-08-17 22:35:10 +03:00
Lauri Ojansivu fb13d01bca Remove the Archive tile action-looking icon.
Keep the archived date as plain text but remove its fa-archive glyph from both board tile variants, where broad hover styling made it look like a lower-left action. Preserve permanent deletion exclusively in the right sidebar and add regression coverage.\n\nThanks to xet7 !
2026-08-17 22:34:57 +03:00
Lauri Ojansivu bb7bcea7a8 Document the multiple-board Home drop warning.
Thanks to xet7 !
2026-08-17 22:31:19 +03:00
Lauri Ojansivu aebc7d9def Reject multiple boards dropped on Home.
Show Please select only one board when a Home drop contains zero or multiple board ids. Stop before changing Home or clearing the selection, while retaining the existing one-board drop behavior.\n\nThanks to xet7 !
2026-08-17 22:31:06 +03:00
Lauri Ojansivu b93509fe7e Document Archive selection drop targets.
Thanks to xet7 !
2026-08-17 22:29:15 +03:00
Lauri Ojansivu 9be1677420 Restrict Archive selection drop targets.
Mark board drags while Multi-Selection is active in Archive so Home, Archive, sharing targets and other board sections refuse them before accepting the HTML5 drop. Highlight only Remaining and existing Workspaces in green, and restore archived boards when assigning them to a Workspace.\n\nThanks to xet7 !
2026-08-17 22:29:00 +03:00
Lauri Ojansivu e3e604ec4b Document the empty Home selection warning.
Thanks to xet7 !
2026-08-17 22:10:30 +03:00
Lauri Ojansivu 9cb5ef5e0f Use the exact-one warning for an empty Home selection.
Require exactly one selected id before setting or unsetting the Home board. Zero and multiple selections now both report Please select only one board, while other bulk actions keep their empty-selection warning.\n\nThanks to xet7 !
2026-08-17 22:10:10 +03:00
Lauri Ojansivu 0a37accdf0 Document the Home selection actions.
Thanks to xet7 !
2026-08-17 22:02:49 +03:00
Lauri Ojansivu 9c6febe1da Specialize multi-selection actions for Home.
Label the Home section's toggle as Unset as Home board and hide Archive and Duplicate there, while retaining those actions in the other live-board sections. Add rendering coverage and the new label to every language catalogue as an untranslated placeholder.\n\nThanks to xet7 !
2026-08-17 22:02:35 +03:00
Lauri Ojansivu d0521c1599 Document the single Home board selection rule.
Thanks to xet7 !
2026-08-17 21:56:24 +03:00
Lauri Ojansivu ddb0ab4a26 Require one board for the Home selection action.
Keep the existing empty-selection warning, reject multiple selected boards with the requested single-board message, and call toggleDefaultBoard only for exactly one id. Add the message to every language catalogue as an untranslated placeholder and cover the zero, multiple and single-selection wiring.\n\nThanks to xet7 !
2026-08-17 21:56:10 +03:00
Lauri Ojansivu c6257dce10 Document always-visible selection actions.
Thanks to xet7 !
2026-08-17 21:50:11 +03:00
Lauri Ojansivu c704b65ccf Keep All Boards selection actions visible.
Always render the applicable Multi-Selection actions and instruction in the All Boards sidebar. Guard every board action with one empty-selection check that reports the requested message before any confirmation or server call. Add the message to every language catalogue as an untranslated placeholder and cover both persistent rendering and every guarded action.\n\nThanks to xet7 !
2026-08-17 21:50:00 +03:00
Lauri Ojansivu a1f7277c77 Document the persistent selection sidebar.
Thanks to xet7 !
2026-08-17 21:43:49 +03:00
Lauri Ojansivu f1934fb3f8 Keep the All Boards selection sidebar visible.
Prevent close, toggle, Escape, Search and Home actions from hiding or replacing the All Boards Multi-Selection sidebar while selection mode is active. Close it normally after the user turns Multi-Selection off, and cover both sides of that state transition with regression tests.\n\nThanks to xet7 !
2026-08-17 21:43:26 +03:00
Lauri Ojansivu 1b18050b32 Document All Boards selection controls.
Thanks to xet7 !
2026-08-17 21:39:50 +03:00
Lauri Ojansivu 2547c52168 Select visible boards in every All Boards section.
Show the same themed Select All and Select None controls above Remaining, Starred, Home, Templates and Archive while Multi-Selection is active. Select All follows the exact section and search result currently rendered; Home keeps Multi-Selection and selects its one visible board.

Thanks to xet7 !
2026-08-17 21:39:34 +03:00
Lauri Ojansivu 01e04717ca Document Archive selection button theming.
Thanks to xet7 !
2026-08-17 21:34:41 +03:00
Lauri Ojansivu 9db74d8498 Use one theme for Archive selection buttons.
Select None now has the same primary theme styling as Select All, with regression coverage requiring both buttons to stay aligned.

Thanks to xet7 !
2026-08-17 21:34:29 +03:00
Lauri Ojansivu b9f9bb9a53 Document Archive selection controls.
Thanks to xet7 !
2026-08-17 21:27:43 +03:00
Lauri Ojansivu 4c0f3b9416 Select all visible archived boards.
While Archive Multi-Selection is active, show Select All and Select None above the board icons. Select All uses the exact filtered list rendered in the pane, while Select None clears the shared board selection.

Thanks to xet7 !
2026-08-17 21:27:29 +03:00
Lauri Ojansivu 9f19dcc7fe Document archived board bulk deletion.
Thanks to xet7 !
2026-08-17 21:25:53 +03:00
Lauri Ojansivu d3034d81ed Permanently delete selected archived boards.
Remove the trashcan from individual archived board controls. When a Global Admin enables permanent delete, Archive multi-selection shows one red Delete action, confirms the irreversible cascade, and sends the complete selection to a server method that validates every gate before removing anything.

Thanks to xet7 !
2026-08-17 21:25:31 +03:00
Lauri Ojansivu 4c6d910c58 Document the permanent delete checkbox fix.
Thanks to xet7 !
2026-08-17 21:15:09 +03:00
Lauri Ojansivu 43d592590c Keep permanent delete checkbox checked.
Publish enablePermanentDelete back to the client after the Global Admin changes it. Without the reactive value, the saved setting was rendered as undefined and the optimistic checkmark immediately reverted.

Thanks to xet7 !
2026-08-17 21:14:53 +03:00
Lauri Ojansivu ad1851bc20 Document the Admin Panel Delete pane.
Thanks to xet7 !
2026-08-17 21:12:34 +03:00
Lauri Ojansivu 4ab3161b4f Add permanent delete to Admin Panel Problems.
The Delete pane sits above Notifications and exposes the existing, default-off enablePermanentDelete gate for Global Admins. It has its own URL, documentation, and regression coverage for menu order, rendering and the setting handler.

Thanks to xet7 !
2026-08-17 21:11:35 +03:00
github-actions[bot] a9a6e62234 Bump versions for v11.00 2026-08-17 13:37:29 +00:00
Lauri Ojansivu 47c0ae7d79 Prepare v11.00 release 2026-08-17 16:36:22 +03:00
Lauri Ojansivu 4e92616524 CHANGELOG: document shared phone page scrolling.
Thanks to xet7 !
2026-08-17 16:31:42 +03:00
Lauri Ojansivu 5b69b63fee Keep every phone page scrollable in both UI modes.
Move the phone viewport and content scroller contract into the shared layout so desktop mode cannot clip the bottom of any page. Keep the All Boards panes natural-height for every left-menu section, and exercise the explicit desktop-mode toggle in the phone UI regression test.\n\nThanks to xet7 !\n\nFixes #6488.
2026-08-17 16:30:30 +03:00
Lauri Ojansivu 92de512c9d Document contextual card text field labels.
Thanks to xet7 !
2026-08-17 16:09:38 +03:00
Lauri Ojansivu bf4c00459a Label populated card text fields as Edit.
Thanks to xet7 !
2026-08-17 16:09:22 +03:00
Lauri Ojansivu dd76dbeaac Document saved card date editor fix.
Thanks to xet7 !
2026-08-17 15:50:20 +03:00
Lauri Ojansivu 14b04575bf Reopen saved card dates with the Card context.
Thanks to xet7 !
2026-08-17 15:49:53 +03:00
Lauri Ojansivu 5d9703faf5 Document full-suite regression fixes.
Thanks to xet7 !
2026-08-17 15:37:57 +03:00
Lauri Ojansivu a2ba64c0fe Fix regressions found by the full test run.
Thanks to xet7 !
2026-08-17 15:36:50 +03:00
Lauri Ojansivu 9ac138aa03 Require regression coverage for Upcoming changes.
Thanks to xet7 !
2026-08-17 15:18:49 +03:00
Lauri Ojansivu aa30e517d3 Document Upcoming release regression coverage.
Thanks to xet7 !
2026-08-17 15:17:29 +03:00
Lauri Ojansivu bb9680f00c Add Upcoming release regression coverage.
Thanks to xet7 !
2026-08-17 15:16:54 +03:00
Lauri Ojansivu 691a9f3c43 Document the placeholder-only translation progress.
Add the same-language translation-memory fill and the completed Finnish Office/API report strings to Upcoming, including the safeguards that preserve human translations.

Thanks to xet7 !
2026-08-17 15:10:08 +03:00
Lauri Ojansivu 73c6bc0592 Translate the new office and API reports into Finnish.
Replace all fourteen translatable English placeholders added by the office and API Problems reports. Product names, protocol acronyms, numbers and symbols remain unchanged by design.

Thanks to xet7 !
2026-08-17 15:07:55 +03:00
Lauri Ojansivu f94d5cbdae Reuse unambiguous same-language translation memory.
Fill 3,838 English placeholders across 227 language files only when another key in the same file has the identical English source and exactly one established non-English translation. Ambiguous sources and every existing target translation remain untouched.

Add a dry-run-first reusable tool and regression coverage for language isolation, placeholder-only writes, ambiguity refusal and English-variant exclusion.

Thanks to xet7 !
2026-08-17 15:06:01 +03:00
Lauri Ojansivu c9297e0342 Move the post-v10.99 minicard fix into Upcoming.
The implementation is a child of the v10.99 version bump, so the released section could not contain it. Move its entry and current commit link to Upcoming, update both summaries, and verify every current-month commit link resolves.

Thanks to Heart1010 and xet7 !
2026-08-17 15:01:00 +03:00
Lauri Ojansivu 86c264850b Document RouteBleed in the upcoming release.
Put CodeQL alert #434 first in the Upcoming security section, link its Hall of Fame page and record why the test-only finding has no runtime Problems event.

Thanks to GitHub CodeQL code scanning and xet7 !
2026-08-17 14:50:42 +03:00
Lauri Ojansivu ed4b8cd648 RouteBleed: remove incomplete route-pattern escaping.
CodeQL alert #434 found that boardExportScope escaped only forward slashes before interpolating an export path into a RegExp. Replace the pattern with an exact route-literal inclusion check, so there is nothing to escape and metacharacters cannot change the match.

Add positive and negative cases with backslashes and regex metacharacters, reject the reported partial-escape shape across tracked JavaScript, and remove a second partial dynamic pattern from release bundle coverage. This was test-only code with no runtime request path, so there is no attributable attempt for Admin Panel Problems.

Thanks to GitHub CodeQL code scanning and xet7 !
2026-08-17 14:50:07 +03:00
Lauri Ojansivu 8cd5127914 Document restored card date editing.
Add the #6607 fix to the Upcoming release notes and summary.

Thanks to Alishara and xet7 !
2026-08-17 14:42:38 +03:00
Lauri Ojansivu fd41970ed2 Make existing card dates editable again.
The date form consolidation put the inputs inside a child Blaze template but left the event maps on the parent popup. Blaze does not dispatch child-template events to a parent template event map, so Save, Delete and validation no longer ran after a date existed. Keep the common form and move its common event map onto that form, passing each popup's date state and field-specific callbacks explicitly.

Regression coverage pins the child event ownership and changes a previously stored due date through the browser UI.

Thanks to Alishara and xet7 !

Fixes #6607.
2026-08-17 14:42:00 +03:00
Lauri Ojansivu becb3afc1c Document the Helm startup memory fix.
Thanks to titver968 and xet7 !
2026-08-17 14:36:10 +03:00
Lauri Ojansivu 794771ed86 Size the container heap from its memory limit.
Give V8 three quarters of the cgroup memory, bounded between 768 MiB and 4 GiB, while preserving an administrator's NODE_OPTIONS. This raises the official 1 GiB pod's failing ~640 MiB automatic ceiling and retains one quarter for native allocations.

Add regression coverage for cgroup v1/v2, bounds, overrides, startup ordering, and the companion Helm chart's corrected 2 GiB budget.

Thanks to titver968 and xet7 !

Fixes #6606.
2026-08-17 14:34:40 +03:00
Lauri Ojansivu fc03ed7715 Document the snap database recovery fix.
Thanks to waltermhl and xet7 !
2026-08-17 14:21:54 +03:00
Lauri Ojansivu 9cea2f7969 Recover retained MongoDB data with compatible readers.
Make database comparison and merge use the same MongoDB 7, 5.0 and 4.2 compatibility ladder as migration. Reuse a live FerretDB as the merge target instead of opening its SQLite database with a second writer, and report the startup log when no bundled reader can open MongoDB.

Add positive and negative regression coverage for comparison, merge, live-service reuse, safe shutdown and architectures without legacy readers.

Thanks to waltermhl and xet7 !

Fixes #6583.
2026-08-17 14:21:01 +03:00
Lauri Ojansivu 6a7c01b072 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-17 14:20:18 +03:00
Lauri Ojansivu a12794ee76 Document the mobile All Boards scroll fix.
Thanks to mimZD and xet7 !
2026-08-17 14:20:17 +03:00
Lauri Ojansivu ae2cfd8aac Fix mobile All Boards scrolling.
Use the content page as the single vertical scroll owner on phones. Let the All Boards wrapper, menu, icon grid and table view grow naturally inside it so nested overflow panes cannot trap a swipe or clip invited-board controls.

Add regression coverage for ordinary boards, table view, invitation controls, viewport sizing and the absence of competing nested vertical scrollers.

Thanks to mimZD and xet7 !

Fixes #6488.
2026-08-17 14:20:17 +03:00
Lauri Ojansivu 1369967eb8 Document the minicard title fix. 2026-08-17 14:20:17 +03:00
Lauri Ojansivu f9934ebde2 Fix minicard title editing.
Thanks to Heart1010 and xet7 !

Fixes #6604.
2026-08-17 14:20:17 +03:00
github-actions[bot] a5ec2a862c Bump versions for v10.99 2026-08-17 01:38:19 +00:00
Lauri Ojansivu fa1bb7ad2f Prepare v10.99 release 2026-08-17 04:37:25 +03:00
Lauri Ojansivu 3129705e5e Classify the export consolidation in Upcoming. 2026-08-17 04:36:22 +03:00
Lauri Ojansivu 76848698d2 Document shared PDF and Excel card mapping. 2026-08-17 04:04:34 +03:00
Lauri Ojansivu 0885b9a10b Share one card document adapter across PDF and Excel exports. 2026-08-17 04:04:13 +03:00
Lauri Ojansivu 3e9ed805e6 Document detailed board Excel exports. 2026-08-17 03:58:13 +03:00
Lauri Ojansivu ec9f9e76d8 Use the detailed card layout for board Excel exports. 2026-08-17 03:57:58 +03:00
Lauri Ojansivu 2df8a4c796 Document restored subtask creation. 2026-08-17 03:56:32 +03:00
Lauri Ojansivu f3ca168e25 Restore subtask creation after helper-board setup. 2026-08-17 03:56:04 +03:00
Lauri Ojansivu 12840ba1f6 Document complete scoped card exports. 2026-08-17 03:43:18 +03:00
Lauri Ojansivu 1b0f2075e5 Export complete card fields at every scope. 2026-08-17 03:43:01 +03:00
Lauri Ojansivu a5686504ec Document card people picker titles and placement. 2026-08-17 03:22:33 +03:00
Lauri Ojansivu c183b5bf88 Title and anchor card people pickers. 2026-08-17 03:22:17 +03:00
Lauri Ojansivu d294e2fb62 Document requester and assigner member selection. 2026-08-17 03:06:03 +03:00
Lauri Ojansivu bb5275e974 Select requesters and assigners from board members. 2026-08-17 03:05:41 +03:00
Lauri Ojansivu e713998925 Document hierarchical PDF and Excel exports. 2026-08-17 02:38:16 +03:00
Lauri Ojansivu 57c806705c Order detailed exports by board hierarchy. 2026-08-17 02:38:04 +03:00
Lauri Ojansivu 01cf597b74 Document localized export filenames. 2026-08-17 02:35:07 +03:00
Lauri Ojansivu 4b27f4fc45 Name PDF and Excel exports by their localized scope. 2026-08-17 02:34:38 +03:00
Lauri Ojansivu 244a4a4b2a Document the shared custom-field checkbox.
Thanks to xet7 !
2026-08-17 02:21:53 +03:00
Lauri Ojansivu 5723b56927 Use the shared checkbox in the custom-field picker.
Replace the Font Awesome checkbox imitation with the materialCheckBox markup used by Admin Panel, Settings, and Announcement, including its shared animation and checked-state styling.\n\nThanks to xet7 !
2026-08-17 02:21:32 +03:00
Lauri Ojansivu 8ca9980394 Document the six-column Excel preview grid.
Thanks to xet7 !
2026-08-17 02:07:53 +03:00
Lauri Ojansivu ef66347f07 Place six Excel attachment previews per row.
Use one worksheet column per preview, put each filename in the cell directly below, and begin a new image and filename row after every six attachments.\n\nThanks to xet7 !
2026-08-17 02:07:39 +03:00
Lauri Ojansivu b9e09668d2 Document matching PDF and Excel attachment layouts.
Thanks to xet7 !
2026-08-17 01:52:51 +03:00
Lauri Ojansivu fb52149e94 Align PDF and Excel card attachment layouts.
Place three previews per row with filename-only captions below, retain every attachment in the six-field details table, and draw PDF labels, metadata positions, and checklist progress like the printable Excel card.\n\nThanks to xet7 !
2026-08-17 01:52:34 +03:00
Lauri Ojansivu 50edcf6851 Document removal of duplicate attachment names.
Thanks to xet7 !
2026-08-17 01:43:39 +03:00
Lauri Ojansivu 7eb0d5ac50 List previewed image attachments only once.
Remove successfully displayed images from the attachment bullet list while retaining non-image files and images whose preview could not be read. Keep filename and size in each preview caption.\n\nThanks to xet7 !
2026-08-17 01:43:26 +03:00
Lauri Ojansivu 03c57e4f4b Document the bundled Unicode font startup fix.
Thanks to xet7 !
2026-08-17 01:20:51 +03:00
Lauri Ojansivu 4deff691eb Start PDFKit with the embedded Unicode font.
Avoid PDFKit's startup lookup for Helvetica.afm, whose package-relative path is unavailable after Meteor production bundling, so Unicode PDF export no longer falls back to the base-font writer.\n\nThanks to xet7 !
2026-08-17 01:20:38 +03:00
Lauri Ojansivu 2a358f3707 Document the PDF date and preview layout fixes.
Thanks to xet7 !
2026-08-17 01:18:06 +03:00
Lauri Ojansivu 9ff542b9b1 Wrap PDF metadata and arrange image previews in rows.
Keep complete date and time values visible, place up to three attachment previews on one atomic row, and caption each preview with its filename and human-readable size.\n\nThanks to xet7 !
2026-08-17 01:17:53 +03:00
Lauri Ojansivu 1e0c7cc5eb Document the production export startup fix.
Thanks to xet7 !
2026-08-17 01:03:04 +03:00
Lauri Ojansivu 3b47e10ee5 Normalize markdown-it module interop.
The Meteor production bundle exposes markdown-it through its default export while bare Node returns the constructor directly. Normalize both shapes so loading the export routes cannot stop WeKan during startup.\n\nThanks to xet7 !
2026-08-17 01:02:51 +03:00
Lauri Ojansivu d80b106a0a Keep the Upcoming changelog summary current.
Thanks to xet7 !
2026-08-17 00:52:48 +03:00
Lauri Ojansivu b4f73fdbc5 Document multilingual PDF and Excel exports.
Thanks to xet7 !
2026-08-17 00:50:19 +03:00
Lauri Ojansivu c796fc6c6b Complete multilingual PDF and Excel exports.
Embed OFL-licensed GNU Unifont fonts in PDF exports, centralize the printable Excel card renderer on the shared card document, and consistently use the saved user language or browser fallback plus the opened card's date format. Document the XLSX font-embedding limitation and cover the locale, font, PDF, and workbook behavior with regression tests.\n\nThanks to xet7 !
2026-08-17 00:50:10 +03:00
Lauri Ojansivu bdfbe8117e Document PDF attachment previews.
Add the completed JPEG and PNG PDF export work and its relocated format documentation to the upcoming release notes.\n\nThanks to xet7 !
2026-08-17 00:37:54 +03:00
Lauri Ojansivu 74888f1edf Embed attachment images in PDF exports.
Move the Excel and PDF export design and progress out of TODO Later into format-specific documentation, then complete JPEG and PNG previews for card and board PDFs with failure-safe image handling and byte-level tests.\n\nThanks to xet7 !
2026-08-17 00:37:22 +03:00
Lauri Ojansivu d5c2415346 Document the reorganized documentation hierarchy.
Add the completed documentation move and recursive link validation to the upcoming release notes.\n\nThanks to xet7 !
2026-08-17 00:25:22 +03:00
Lauri Ojansivu c4cb87345b Reorganize documentation and repair links.
Move feature and webserver documentation into the reorganized hierarchy, update references to their new relative paths, and extend link validation across the complete documentation tree.\n\nThanks to xet7 !
2026-08-17 00:24:38 +03:00
Lauri Ojansivu 11b6504040 TODO Later: Requested By and Assigned By as people, and the last two export steps.
Captured rather than started. Both are one feature across several places, and
half of either landed is worse than none.

REQUESTED BY AND ASSIGNED BY BECOME PEOPLE. They are free text today and should
keep that field AND gain member fields of the same kind Assignees has - a user
picked from a popup, shown as an avatar or initials - on the card, in both
exports and through every import. docs/Features/Cards/Requested-Assigned-By.md
has the shape: mirror `assignees` exactly (`requesters`, `assigners` as
[String], named after what WeKan already calls them internally -
js-card-details-requester, editCardRequesterForm), keep the two strings beside
them, and give Members, Assignees, Requested By and Assigned By ONE template,
because they are the same control written four times. The avatar needs nothing
new: it is +userAvatar in a `.member` box, which is what the board sidebar, the
cards, Admin Panel / People and Admin Panel / Problems already use - the box is
what gives an avatar its size, and every rule in userAvatar.css is scoped to it.

It is five places - schema, the card, the picker popups, both exports, the
import round trip - and a card that shows a person an export drops is a worse
state than the one it is in now.

AND THE TWO REMAINING EXPORT STEPS of the batch that closed #6586: moving the
EXCEL renderer onto the shared card document, where unchanged output is what
makes the step verifiable, and embedding images in the PDF - JPEG as /DCTDecode
with the file's own bytes, PNG as /FlateDecode with /DecodeParms. Until then a
PDF lists an attachment by name where Excel embeds the picture.

Thanks to xet7 !
2026-08-17 00:05:51 +03:00
Lauri Ojansivu e6df1a5d6d Assigned By shows when there is an assigner, not when there is a requester.
Its read-only branch - what somebody who may not edit the card sees - was
written `else if getRequestedBy` above `= getAssignedBy`. A card with an
assigner and no requester therefore showed neither: the section drew its heading
with nothing under it.

I also thought the two fields were hidden from everyone but workers, and was
WRONG: the `else` sits at the same indent as `+inlinedForm`, so it is the form's
own else - WeKan's inlinedForm idiom - and the block is inside `unless
currentUser.isWorker` on purpose, because a worker may not modify a card. The
edit I made on that mistaken reading mangled the template and was reverted; this
commit is the one-word fix that was actually needed.

The comment explaining it could not stay where it was written: a comment between
an `if` and its `else if` breaks jade nesting, which tests/jadeCompiles.test.cjs
has a negative test for and which I have now hit three times.

Thanks to xet7 !
2026-08-16 23:59:21 +03:00
Lauri Ojansivu f6e3144452 Step 4: the PDF draws the shared card document, so both formats have one layout.
"PDF export layout should be like Excel layout", and "export board, swimlane,
list, card to Excel and PDF should have same layout like export card to Excel" -
with markdown rendered and only the fields that have data.

The PDF laid a card out itself: a hundred and sixty lines that had to be kept in
step with the Excel exporter's layout by hand, and were not. The Excel export
drew sections under filled headers with label/value columns; the PDF drew a flat
list of monospaced lines. Both ask models/lib/cardDocument.js for the layout now
and only the drawing differs, so a section added to one is in the other.

WHAT A PAGE MAKES OF THE DOCUMENT, in pdfDocument.js: a section is a filled grey
bar, a meta block is a row of label/value columns with the labels in bold, text
is markdown wrapped and styled, a list keeps its marker, a comment keeps its
date and its author. The bars are real rectangles - drawn outside BT/ET, because
a PDF text object may not contain a path - and the y of each line is known from
its index, so it needs no layout pass.

What is left in the exporter is the MAPPING: Meteor documents, user ids and
dates turned into the plain names and strings the document takes. That is the
only part that knows a Mongo document from a string.

FOUR REGRESSIONS THE EXISTING GUARDS CAUGHT, every one a field that would have
gone missing from the file:

  * requested by / assigned by - a card's own record of who asked for it, and a
    suite exists precisely because they have been lost from an export before;
  * card number, last activity, spent time and overtime;
  * the labels and the dates, which two suites check by name;
  * and every label's English fallback. The document was passing keys without
    one, so a missing translation would have printed "card-due" on the page.
    The words live in one table now, which also stops a PDF saying "Due" where
    a spreadsheet says "Due date".

The guards themselves follow the layout to where it went: they read the exporter
AND the document, because a field is now carried by the pair, and reading only
the exporter would report a field as lost when it had merely moved to where both
formats take it from.

Verified as far as this sandbox allows: the mapping is extracted from the file
and executed against a stand-in exporter (tests/pdfDrawsTheDocument.test.cjs),
the document it builds is checked section by section, and the bytes are checked
to be a PDF whose xref offset points at its table. There is no PDF viewer here,
so how the page LOOKS is still to be checked by eye.

Thanks to Heart1010 and xet7 !

Fixes #6586,
2026-08-16 23:50:10 +03:00
Lauri Ojansivu 50edf13a89 Only the fields that have data are added, decided once for both formats.
An export should not carry a Comments heading with nothing under it, or a Custom
fields section for a card that has none, or a row of dashes where a description
would be.

"Empty" is a JUDGEMENT, not a length check - a checklist with no items yet is
empty, a checklist whose items are all unticked is not; a custom field with a
definition and no value is not data, and most cards carry several of those - so
it is answered in ONE function, models/lib/cardDocument.js's hasSectionData, and
both exporters ask it. Two exporters answering it separately is how a PDF and a
spreadsheet of the same card come to contain different sections, which is the
whole reason the document model exists.

The document itself now contains only the sections that have something in them,
and the Excel and PDF exporters call the same predicate beside their existing
`?fields=` check - which stays, because it is a different question: the
selection says what the reader ASKED for, the data says what there is to give
them, and a section needs both.

One thing worth naming, because it nearly shipped: an edit that added the
guards threw part-way through, so the file kept a call to hasSectionData and
lost the import that defines it - which no test would have caught, since none of
them can import a Meteor path, and the first symptom would have been a card that
would not export at all. The import is there, and every guard's local was
checked against what cardBlockLines actually destructures from its data.

Thanks to xet7 !
2026-08-16 23:30:13 +03:00
Lauri Ojansivu d8fe42c1e0 Step 3: the card layout, described once, as a document both formats can draw.
The layout is the one "Export card to Excel" already draws - a title, meta rows
of label/value pairs, then a section per part of the card, each under a filled
header. It is the one that was designed, and it is what every export should be.

What was missing is that only ONE exporter could draw it. The Excel code knew
the layout AND how to write a worksheet, so the PDF could not use it without
becoming a worksheet - and grew its own instead: a list of monospaced lines with
none of the structure. That is the duplication, and "make the PDF look like the
Excel code" would have doubled it rather than removed it.

models/lib/cardDocument.js describes the layout as data - blocks that name no
colour, no column letter and no font size:

  title | section | meta | text | list | rows | images | note

A renderer decides what those look like in its medium, and only the renderer
knows the difference between a spreadsheet and a page. A test fails if a colour,
a merge or a page dimension appears in the document.

Three things it settles by construction, which two exporters could otherwise
disagree about:

  * WHICH SECTIONS. `?fields=` - the export popup's checkboxes - is applied here,
    so unticking "Comments" means the same thing in a PDF as in a spreadsheet.
  * WHAT IS THE AUTHOR'S TEXT AND WHAT IS THE EXPORT'S OWN MARK. A checklist
    item's `[x]` and a comment's author name are separate from the runs beside
    them, so a renderer can style them and the markdown that follows is not
    pasted into a string with them.
  * MARKDOWN IS ALREADY PARSED. A renderer is handed blocks, never a source
    string, so there cannot be a second parser.

Pure: no Meteor, no database, no ExcelJS. It is given a card and the rows that
belong to it and returns an array, which is what lets both exporters be checked
against the same document and lets the suite run as arithmetic under bare node.

Nothing renders it yet - this commit adds the description and its tests only, so
both exports still work exactly as they did. The Excel renderer moves onto it
next, with unchanged output as the check, then the PDF renderer and its images.

Thanks to xet7 !
2026-08-16 23:20:42 +03:00
Lauri Ojansivu c1b68efbd3 Step 2: every text in an Excel card export renders its markdown.
The description went first. A description that renders and a comment that does
not is worse than neither, because the file then disagrees with itself about
what markdown is - so the comments, the checklist items and the custom-field
values go through the same shared parser now.

Each keeps what is the EXPORT's own mark rather than the author's: a comment
still has its author's name in front of it, in bold, and a checklist item still
has its `[x]` or `[ ]` box - written as their own runs so the rendered text
follows them instead of being pasted into a string. A finished item's strike
carries onto every run of its title, or half of a struck-out line would look
live.

One fault found while doing it: splitRow estimates a row's height from the
length of its value, and a rich-text value is an OBJECT whose `.length` is
undefined. Measuring the value itself would give every formatted comment the
minimum height and clip it, so the runs are added up instead.

A value with no markdown in it is still written as a plain string - the same
cell, byte for byte, as before this change - which is what keeps the step
verifiable: only cards that actually contain markdown produce a different file.

Thanks to Heart1010 and xet7 !
2026-08-16 23:17:15 +03:00
Lauri Ojansivu b4e9d1f8d1 Exports render markdown the way WeKan renders it, from one shared parser.
A card's description, its comments and its checklist items are markdown, and
WeKan draws them as markdown everywhere it shows them. The exports did not: the
PDF flattened `**bold**` to the word with its asterisks still attached, and
Excel wrote the source text into a cell. A card that reads well on screen
exported as something nobody would write on purpose - #6586, "markdown is not
formatted - this doesn't make sense in a pdf file, does it?"

models/lib/exportMarkdown.js parses it with THE SAME markdown-it options the
reader's renderer uses - html, linkify, typographer, breaks - so the export and
the screen agree about what is emphasis, what is a list and where a paragraph
ends. It answers in a shape neither exporter is: blocks carrying runs of styled
text, `{ text, bold, italic, code, strike, link }`. Excel turns runs into an
ExcelJS richText array; the PDF will pick one of its four Courier faces. Neither
has to know markdown, and a fix to how a list nests reaches both at once - which
is the whole reason there is one module rather than two.

HTML in a card is kept as TEXT, deliberately. An export is a document, and a
card containing a script tag should put those characters in the file rather than
have anything act on them.

Wired into the Excel card export's description first, where a single unstyled
run is still written as a plain string - so a card with no markdown in it
produces exactly the file it did before.

AND THE PLAN FOR THE REST IS WRITTEN DOWN, because the request is one rule:
the same functions, layouts and templates for every PDF and Excel export, with
no duplicated code. docs/Features/ImportExport/One-Card-Layout.md says what that
means and in what order it lands.

Two of the four quarters are already true, and by the same method - one
renderer, called by every scope. Excel draws the card layout at every scope
(ExporterExcelBoard renders each card through ExporterExcelCard.renderCardBlock),
and the PDF already shares one block across scopes (ExporterBoardPDF and
ExporterCardPDF both render through PDFExporterBase.cardBlockLines). What is
missing is that the PDF's block is not that LAYOUT: monospaced lines, no
sections, no label columns, no boxes, no images.

So the next step is not "make the PDF look like the Excel code". It is a card
DOCUMENT - one description of the layout, built once from a card, rendered twice
- with the Excel renderer moved onto it first, unchanged output being what makes
that step verifiable, and the PDF renderer and its images following.

Thanks to Heart1010 and xet7 !
2026-08-16 23:05:44 +03:00
Lauri Ojansivu 8b077a911d CHANGELOG: the export-that-was-HTML and the checkbox shape join Upcoming.
Thanks to xet7 !
2026-08-16 22:33:39 +03:00
Lauri Ojansivu 58bf9774a2 Exporting a card to PDF produced an HTML file, and checkboxes are square.
THE PDF THAT WAS AN HTML PAGE. Not a broken PDF - WeKan's own page, saved as
`<card>.pdf`. Every export in the interface is a download from an `/api/...`
address, and server/apiMiddleware.js refuses every /api request unless WITH_API
is exactly "true". It refused by answering `301 Location: /`, so the browser
followed it to the front page and the download link's `download="<card>.pdf"`
wrote the HTML it got there to that name. Reproduced against the running
instance: `GET /api/boards/x/exportPDF` -> 301.

Two faults, and either alone is enough:

  * The bundle launchers did not set WITH_API. The snap has defaulted it to true
    for years (snap-src/bin/config) and every docker-compose*.yml in this
    repository sets it, so the bundle was the ONE platform where exporting was
    off by default - which is why it was the one platform where an export came
    back as HTML. start-wekan.sh and start-wekan.bat now default it to true,
    overridable, and say WHY: the name reads like a developer feature, and
    somebody switching it off to harden an instance should know they are turning
    off every export in the interface.

  * The refusal was a redirect. Whatever the setting, "the API is off" must not
    arrive as a page: it is now 403 text/plain saying that exports use the API
    too and naming the variable to set. A 403 with a sentence in it cannot be
    mistaken for the file that was asked for.

CHECKBOXES ARE SQUARE, WHEREVER THEY ARE PUT. The export popup's unchecked box
drew a thin vertical sliver beside a long label and a proper square beside a
short one: the row is a flex container, the box is a flex item, and a flex item
shrinks. That is the THIRD time this shape has broken - settingBody.css already
carries a fix for a 41px min-height that "turns a 13px box into a tall
rectangle", and another for a `height: 100%` that stretched it - so the fix goes
in the rule that DEFINES the checkbox: `flex: none` in forms.css, for all 90 of
them across 19 templates, rather than one more local patch.

The local patch is removed with it. It had set `box-sizing: border-box`, which
would have made the export popup's boxes 13px INCLUDING their border while every
other checkbox in WeKan is 13px plus 2px - a fix for one page that made that
page the odd one out.

The Admin Panel's checkboxes are native inputs STYLED to be the material box,
and a guard already compares the two rules declaration by declaration, so they
take the new declaration too - written the same way, since `flex-shrink: 0` and
`flex: none` mean the same thing and only one of them can be compared.

Audited the rest rather than assuming: every CSS rule in the client that sizes a
checkbox gives it equal width and height, including the two "clean" board themes
that deliberately draw theirs at 24px and 18px. tests/checkboxesAreSquare.test.cjs
pins that invariant for every rule, and tests/exportNeedsTheApi.test.cjs pins
that the launchers turn the API on, that the refusal is never a redirect, and
that the exports really are /api addresses - which is what made the gate their
business in the first place.

Thanks to Heart1010 and xet7 !
2026-08-16 22:27:48 +03:00
Lauri Ojansivu a7c18bf44c CHANGELOG: the export toggle entry says what actually fixed it.
The first attempt shipped and did not work; the entry said it did.

Thanks to xet7 !
2026-08-16 21:34:39 +03:00
Lauri Ojansivu 6053d32271 Export popup: bind the checkbox toggle where nothing can drop the click.
The previous fix was not enough. #6586 comment 5308548585 said "I can't
select/deselect those arrows here"; after moving the handlers onto
exportScopeSelect - the template that actually draws the rows - and rebuilding
BOTH the release and the development bundle, the answer was "clicking a checked
option, like labels, does not uncheck it". So the diagnosis was wrong twice.

What is known, read out of the built bundle rather than assumed:

  * the templates are registered, and their helpers run - the rows render, with
    their labels, so `exportFields` resolves;
  * both event maps are attached: `Template.exportScopeBody.events(dt)` and
    `Template.exportScopeSelect.events(dt)` are both in the shipped code;
  * the handler itself is right - `c0.set(t, !c0.get(t))` on the same
    ReactiveDict the helpers read.

The click simply never arrives. This list is drawn inside FIVE popups - board,
swimlane, list, card, checklist - each rendered by Popup into its own Blaze view
tree, and rather than keep guessing which link in that chain drops the event,
the toggle is now bound where nothing in it can: one native listener on the
document, in the CAPTURE phase.

Capture, because it runs on the way DOWN to the row, so a stopPropagation()
anywhere between the row and the document cannot eat it - that being one of the
few things that could have been eating it. Native rather than jQuery, because a
`window.jQuery` that turned out to be undefined would fail silently, which is
indistinguishable from the bug not being fixed. And ONE binding rather than one
per template: two would toggle twice and cancel out, which is the same "nothing
happens" arriving from the other direction. The template event maps for these
two are removed, so there is exactly one.

The display is unchanged and still reactive: `selection` is what draws the
checkbox, and nothing here touches the DOM by hand.

I could not reproduce this locally - there is no browser in this sandbox and
puppeteer's installer fails in it - so this removes the dependency on the
mechanism that failed rather than proving which link was at fault. The test
pins that there is exactly ONE binding per class, that it is a native capture
listener, and that it does not depend on jQuery being present.

Thanks to Heart1010 and xet7 !
2026-08-16 21:31:39 +03:00
Lauri Ojansivu b1b17ca75c CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-16 20:57:13 +03:00
Lauri Ojansivu f017c157a0 CHANGELOG: the export popup and the two tile fixes open the next release.
The v10.98 section is a record and stays as it shipped; these are fixes to what
it shipped, so they go under a new Upcoming above it.

Thanks to xet7 !
2026-08-16 20:57:12 +03:00
Lauri Ojansivu 580b03e172 Admin Panel avatars are avatar-sized, and All Boards tiles fill their row.
Three places, one cause each, all visible as soon as the page was opened.

ADMIN PANEL / PROBLEMS drew a user's photograph at its NATURAL size - a 300px
portrait in a table row, a screen tall, with the login counts scattered around
it. Reported for Offices and for the Impersonation Report, and it was every pane
that names a user. Every avatar rule in userAvatar.css is scoped to `.member`,
which is what makes an avatar 24px and round and crops the image to fill it, and
the shared table page's cells had no such box: the image and the initials had
been copied, and the thing they belong in had not. The cells use `.member` now
rather than a fourth private copy of "how big is an avatar" - there were already
three, which is how the three came to disagree - with the two board-specific
declarations it carries (a float and a sidebar margin) turned off for a table.
That covers every Problems pane at once, because they all draw users through the
same table page and none has avatar markup of its own.

ALL BOARDS / STARRED: the grey "+ Add Board" tile hung short of the boards
beside it. `min-height: 114px` is a FLOOR, and a board whose title wraps to
three lines grows past it; the grid stretches every other tile in the row to
match, but the grey comes from the `.label` INSIDE the li rather than from the
li itself, so it kept its 114px while its li grew. The li is a flex box now and
the label grows into it.

ALL BOARDS / HOME: "Drag a board here to open it after login" was padding around
a line of text, about 85px, on a page whose entire content is that box. It
stands where a board tile will be, so it is a board tile's height.

CHECKED AND ALREADY RIGHT, rather than changed: Templates and the workspaces.
Every view - Starred, Remaining, Home, Templates, Archive and each workspace -
is the SAME `ul.board-list` with a different set of boards in it, so they share
one rule, and every tile variant computes to the same 114px border-box floor.
A test now pins that there is one list and that no variant sets a height of its
own, so "do the workspace tiles match" keeps having one answer instead of six.

tests/adminPanelAvatars.test.cjs verified to fail on the real fault: dropping
`.member` reports "these avatar links are not `.member`, so the image has no
size and renders at whatever the file is".

Thanks to xet7 !
2026-08-16 20:57:12 +03:00
Lauri Ojansivu ea8bd12a9f Export popup: the checkboxes can be changed, and they say what they are.
Two faults in the unified export popup, reported in #6586 comment 5308548585
("with v10.98 we have that unified export dialog, but I can't select/deselect
those arrows here") and confirmed as "clicking a checked option, like labels,
does not uncheck it".

THE LIST COULD NOT BE CHANGED. The two toggle handlers were registered on
exportScopeBody, while the checkboxes they act on are drawn by
exportScopeSelect. Blaze resolves a helper, and delivers an event, against the
template the element is IN - never an enclosing one - so nothing happened when
a row was clicked. adminProblems.js has the same rule written out, learned the
same way, where three panes each needed their own copy of a shared pair. The
toggles are one object now, registered on both templates like the helpers.

AND NO ROW SAID WHETHER IT WAS TICKED. Each drew an unconditional
`i.fa.fa-check` on a `li.active`, which is popup.css's OTHER convention: that
tick is hidden and shown only for an active row by
`.pop-over-list .pop-over-list.checkable .fa-check` - a selector needing a
NESTED list carrying `checkable`, and this list was neither. So the tick was
never hidden and `active` was never styled: every row looked ticked whatever it
was. Each row now carries `.materialCheckBox`, the checkbox Admin Panel /
Settings / Announcement uses, which needs no ancestor to be right - and the box
aligns with the FIRST line of a label that wraps, which several of them do in
any language with compound words.

WHAT IS TICKED IS WHAT THE FILE CONTAINS, checked rather than assumed: every
format entry with a path builds its URL through exportUrl, which always appends
`fields=<the ticked ones>`, and every route that serves one parses it through
parseExportFields - the JSON, .zip and Kanboard exports, Excel, PDF, the CSV
(where the selection lands on columns), and the ten external-tool exports, which
share one handler. A test reads the format table and fails if a format is
offered that no route serves.

Both new guards were verified to fail on the real faults: removing the
exportScopeSelect registration reports "must have the toggle handlers, or the
list cannot be changed", and putting the old markup back reports "card details
must draw a materialCheckBox that reflects its state".

Thanks to Heart1010 and xet7 !
2026-08-16 20:57:12 +03:00
github-actions[bot] 39e893f52a Bump versions for v10.98 2026-08-16 15:40:27 +00:00
Lauri Ojansivu 7b30a7fff9 Prepare v10.98 release 2026-08-16 18:39:19 +03:00
Lauri Ojansivu c58f2d9c16 CHANGELOG: wrap the In short paragraph at 80.
Thanks to xet7 !
2026-08-16 18:26:17 +03:00
Lauri Ojansivu a2460222f1 CHANGELOG: the browser-breaking lockout package joins Upcoming.
Thanks to xet7 !
2026-08-16 18:24:54 +03:00
Lauri Ojansivu 5ce9f181ca The lockout package is server-only: it was breaking every page in the browser.
The JamBleed fix hashes the source address of a login attempt with
`require('crypto')`, in packages/wekan-accounts-lockout. That package declared

    api.mainModule('accounts-lockout.js');

with no architecture, which loads it into the CLIENT as well as the server. So
the browser bundle pulled in crypto-browserify, which pulls in cipher-base,
which does `require('stream')`, and the page died on load:

    Uncaught Error: Cannot find module 'stream'

before WeKan drew anything at all. The server started perfectly and answered
HTTP 200 with a page that could not run.

It is server-only now, which it always should have been: nothing in client/
imports it - only server/accounts-lockout-config.js and
server/methods/lockoutSettings.js do - and shipping a brute-force lockout's
decision to the browser would hand an attacker the rules even if it cost
nothing. A rebuilt client bundle is 758 KB smaller and no longer contains
crypto-browserify or cipher-base at all.

WHY NOTHING CAUGHT IT. bundle-smoke-boot.sh starts the bundle and waits for it
to reach its database, which proves the SERVER image loads - every package
linked, every map read. This was the client, and no check looked at it. Both
crashes it was written for were server-side, so it answered the question it was
asked and the question next door went unasked.

tests/packagesLoadOnTheRightArch.test.cjs pins the whole class rather than this
one package: it reads each package.js for the entry files that are NOT
restricted to the server, follows their imports inside the package, and fails
when any of them requires a Node builtin - and it does the same for every file
under client/. Verified to fail on the real fault: putting the architecture back
to the default reports

    wekan-accounts-lockout/src/lockoutScope.js requires 'crypto'
    (reached from accounts-lockout.js)

Meteor's default being "both architectures" is what makes this silent - the code
works, the tests pass, and the cost lands in a browser bundle nobody reads. The
two other packages that use Node builtins (wekan-accounts-cas, wekan-oidc) keep
theirs in *_server.js files added with an explicit 'server', which is the
pattern this now enforces.

Thanks to xet7 !
2026-08-16 18:24:21 +03:00
Lauri Ojansivu 6524c8326a CHANGELOG: an Upcoming audit against the rules in CLAUDE.md.
Checked, and what was wrong:

* The **In short:** paragraph named `build.sh option 2`, which is two entries
  now with names of their own, and stopped three entries short of the section -
  the map popup, the API pane and the Directory-Structure rewrite had landed
  since it was last written. It also said 2016 where git says the commit that
  page described is from 2017.
* A line inside the Directory-Structure entry began `**Bold** — `, which is the
  shape of a GROUP header and reads as one when the entry is scanned. Reworded.
* Two prose lines were 81 and 86 characters. The one line still over 80 is
  inside a fenced block quoting a build log verbatim, which is left as the log
  printed it.

Checked and correct: all 21 commit links resolve to commits in this branch; no
summary exceeds 110 visible characters or carries markup; the subsections read
as one sentence in the order the rules set - CRITICAL, CodeQL, features, bugs,
developer-facing, documentation - and every entry of every grouped subsection
belongs to a group; 21 `<details>` open and 21 close, each summary on one line
with a blank line under it; the section still opens with the **In short:**
paragraph and the binaries table and ends with the Thanks line.

TODO Later is unchanged and still correct: its eleven issues are LDAP, WebHooks,
Sandstorm, the Winston logger, an Apache proxy configuration and the rest -
none of them is what this release fixed, so nothing is to be removed from it.

Thanks to xet7 !
2026-08-16 18:05:59 +03:00
Lauri Ojansivu b07b74bc65 CHANGELOG: the API report, the location map popup and the directory-structure rewrite join Upcoming.
Thanks to xet7 !
2026-08-16 17:58:33 +03:00
Lauri Ojansivu e9963e8b8a Docs: Directory-Structure.md describes the tree WeKan has now.
It described the tree at commit e2f768c, and had gone on describing it for
years. Two failures, and the second is the larger one:

FOURTEEN LINKS POINTED AT NOTHING. Five files had moved - the manifest into
public/svg-etc, the translations into imports/i18n/data, the migrations into
migrations/, Contributing.md to CONTRIBUTING.md - and seven described files that
no longer exist anywhere: the mixins that were replaced, presence.js,
reactiveTabs.js, fast-render.js, and a monkey patch for a bug fixed upstream.
The moved ones are repointed and the deleted ones removed, because an entry for
a file that is not there sends a reader looking for it.

Every one of the fourteen was written as a full
https://github.com/wekan/wekan/tree/main/... URL rather than a relative path,
which is exactly why none was noticed: a broken relative link is visibly broken
in an editor and on GitHub, while an absolute one looks like a link and 404s
only for the reader who follows it. All 144 relative links were fine.

AND TWO THIRDS OF THE REPOSITORY WAS NOT MENTIONED AT ALL - imports/, packages/,
releases/, tests/, docs/, migrations/, server/lib, server/methods, server/routes,
models/lib, client/features. The page walked through four directories out of
twenty and did not say that was what it was doing, so a reader could not tell
the difference between "not here" and "does not exist".

It now opens with a table of the whole tree, and gains sections for the four
server directories that hold most of the server, models/lib (the pure modules
that let a decision be tested as arithmetic under bare node), client/features
(the list of what the client LOADS - a component not named there is not in the
bundle however finished it is), and the build/release/test tooling.

tests/docsLinksResolve.test.cjs checks both link forms against the tree, and
fails when Directory-Structure.md stops mentioning a top-level source directory -
the silence being the failure that lasted longest.

Thanks to xet7 !
2026-08-16 17:56:41 +03:00
Lauri Ojansivu 6dc2651b48 Admin Panel / Problems / API: who called which endpoint, and how often.
Every other pane under Problems answers what went WRONG. This one answers what
is being DONE: `POST /api/boards` called 34 times by alice last month, 812 times
yesterday by nobody with an account. Without it the only record of REST API use
is whatever the reverse proxy happens to keep, and most instances keep none.

The table is username, endpoint, calls, the window they fall in, and the
addresses - sorted by calls, because a usage report's question is "what is used
most" where a problem report's is "what happened last".

THE NAME IS THE ROUTE PATTERN, NOT THE PATH. `/api/boards/:boardId/lists` is one
endpoint; `/api/boards/abc123/lists` and ten thousand of its siblings are that
one endpoint being used. Naming rows after paths would put a row per board in
the collection - the one-row-per-event cost this whole page exists to remove -
and a request that matched NO route is counted under a single "(no route)" name
rather than under the path it invented, because a 404 sweep is an attacker
walking a wordlist and a row per guess would let them fill the database.

ONE ROW PER ACCOUNT AND ENDPOINT, which makes the account part of a row's
identity - the one deliberate exception to the rule every other stream follows.
For those the question is "what is happening" and the caller would multiply the
rows; here "who called what" IS the report, and the cardinality is bounded by
real accounts times real endpoints. The account is stored by ID, so a rename
does not split its history in two.

NOTHING PER REQUEST. Ordinary API traffic is not rare the way a guard firing is
rare, so calls are counted in memory and folded on a timer - a thousand requests
become one write. A few seconds of counts are lost if the process stops between
flushes, which is acceptable here and nowhere else on this page: this report
decides nothing and nothing is protected by it. The tracked pairs are capped and
the rest counted, not dropped.

Counting hooks the middleware chain, not the routes, so a route cannot be added
without being counted - and it counts on the response's `finish` event, which is
where Express has filled req.route.path and authentication has run.

The pane is the SHARED event-stream report with a different column list, not a
second table page. The API stream is deliberately NOT in EVENT_STREAMS: it is
usage, not a problem, and an instance serving its API would otherwise report
thousands of new problems.

AND THE IPv4/IPv6 SPLIT IS FINISHED WHILE HERE. The summary rows have had ipv4
and ipv6 fields since the summaries were written, and not one of the four
loggers ever filled them - so the columns the design asked for could not have
worked. The fold splits the address now, once, for every stream, and the two
columns are one shared pair used by both reports rather than a copy each. They
fall back to classifying the stored `ip`, so rows written before today display
correctly instead of showing two empty columns for all of history.

Seven i18n keys in 239 language files, in en.i18n.json's order, English as the
placeholder where nothing is translated yet.

Thanks to xet7 !
2026-08-16 17:52:18 +03:00
Lauri Ojansivu ebe985d453 A location in an Admin Panel table opens a map, through the card's own chooser.
Admin Panel / Problems / Offices names the places accounts log in from -
"London", with the country's flag - and a name is only useful if it leads
somewhere. Clicking one now asks which map to open it at, from the same eleven
providers a card's location offers.

THE SAME CHOOSER, not a second one. The provider list was eleven options inside
cardDetails.jade; it is client/components/main/mapProvider.jade now, and both
callers include it. Two lists would be eleven places to add a provider instead
of one, and they would disagree the first time only one was edited. The
isMapProvider helper moves with it, so neither caller needs its own copy, and so
do its styles - they were scoped to .js-card-location-form, which stopped being
true the moment a second template used them.

What the two do NOT share is the rest. A card's popup EDITS a location - name,
address, coordinates, delete - and none of that applies to an office: its
location arrived in a CDN header, WeKan did not ask for it and cannot correct
it. So tablePageMapPopup is the map half alone: where this is, the link, the
chooser, and a save that makes the choice the default for cards too, because
profile.mapProvider is the one setting both read.

A cell is a link only when the row HAS coordinates. A city name is not a
position, and a map URL built from one would either search for the word or
invent a place, so buildRows drops a location without lat/lon and the cell stays
plain text. The link follows the selection BEFORE it is saved: choosing a
provider and finding the link still pointing at the old one reads as broken.

The handler lives on the shared table page, not on the report. That is also
where the "clicking a user opens the Edit user popup" handler goes: three
reports each had their own identical copy, because each was written where its
own page was. A Blaze event bubbles through the template hierarchy, so one
handler on tablePage reaches every report - and a report added later gets both
without being told.

Two things the guards caught, both worth naming. The popup had no title, which
in WeKan means no header and so no close button; it opens with
titleKey: 'location-open-map-at' - the phrase the chooser is already labelled
with in every language - rather than adding a key to 154 files. And a comment
between an `if` and its `else if` breaks jade nesting, which
tests/jadeCompiles.test.cjs has a negative test for; the comments sit above the
chain.

Thanks to xet7 !
2026-08-16 17:33:09 +03:00
Lauri Ojansivu 410b13a071 CHANGELOG: the two build menu entries join the release-bundle entry.
Thanks to xet7 !
2026-08-16 17:20:43 +03:00
Lauri Ojansivu 0258e6b8b8 Setup menu: "Build WeKan release bundle" and "Build WeKan development bundle".
One entry cannot mean both, and after the previous commit "Build WeKan" meant
the expensive one: it downloaded a Node.js, a FerretDB and eight MongoDB tools,
trimmed 389 MiB and smoke-booted the result. That is the right answer to "would
a release start", and the wrong one to "give me a bundle to poke at".

So the entry is now two, and they say which is which:

  Build WeKan release bundle       what the Release All workflow builds for
                                   this platform, minus the .zip
  Build WeKan development bundle   plain `meteor build .build --directory` -
                                   exactly what "Build WeKan" did before

The development one also says what it is NOT, when it finishes: a bundle from
there still has the legacy client, the source maps and uWebSockets.js, and no
Node.js, FerretDB or launcher of its own, so it cannot answer whether a release
starts. Somebody who builds it and then wonders why ./start-wekan.sh is missing
should not have to find that out by looking.

build.bat gains :builddev beside :build, with the clean-and-build half they
share moved into :buildcommon - the same reason the git actions there call
build.sh rather than reimplementing it: two copies of the steps is how that file
drifted before.

The test path is unchanged and still takes the plain build, which is what it
wants: it runs the bundle under its own node and its own mongod.

Thanks to xet7 !
2026-08-16 17:20:35 +03:00
Lauri Ojansivu 9e947e5a52 CHANGELOG: the local release-bundle build joins Upcoming.
Thanks to xet7 !
2026-08-16 17:14:08 +03:00
Lauri Ojansivu ccb2d818a2 build.sh / build.bat option 2 builds the RELEASE bundle, minus the .zip.
"Build WeKan" ran `meteor build .build --directory` and stopped there, and that
bundle is not the one a release publishes. A release adds the server's npm
modules, three prunes, the sockjs / legacy-client / source-map trim, a verified
Node.js, FerretDB, the eight MongoDB Database Tools and a launcher - and three
releases running broke in exactly that difference:

  v10.96  boot.js read a source map the trim had deleted and left NAMED
  v10.97  the prune's graph could not see module.link(), so the bundle shipped
          without nodemailer-openpgp, which packages/email.js links on its
          first tick
  v10.98  meteor-spk rebuilt the bundle, throwing away 405 MiB of trimming

None of it was reproducible here, because here there was only `meteor build`.
Answering "does the bundle a release would publish start at all" took a
release, a workflow run and a download.

releases/build-release-bundle.sh runs the workflow's OWN steps, in its order,
with its arguments, for whichever platform it is run on - amd64, arm64, the two
macOS arches and the three Windows ones. Not a second implementation of the
release: the same scripts, so a fix to one is a fix to both. What it does NOT do
is zip, checksum or record provenance, because those describe a published
artifact and this one is a directory to start:

    cd .build/bundle && ./start-wekan.sh

That launcher is start-wekan.sh because that is what a release ships; a start.sh
added here would be the first way this bundle differed from one.

Verified by running it, on Linux arm64: 850M bundle, trim -349 MiB, prune
-40.0 MiB, smoke boot passed, Node.js v24.19.0 verified against nodejs.org's
SHASUMS256.txt, FerretDB and the eight tools downloaded per-arch, 686M with all
of it embedded. Then started: FerretDB on SQLite, WeKan serving HTTP 200 fifteen
seconds later. The only gap it reported is qemu-aarch64, because
qemu-user-static is not installed on this machine - a warning, not a failure,
and the reason it is a warning is that only #6458's emulation path needs it.

Downloads are cached under .tools/bundle-binaries/<platform>/ so an afternoon of
rebuilds does not re-fetch 150 MB each time, and every cache HIT re-checks the
published SHA256 rather than trusting the file for having been there before.

build.bat calls the same script through bash, for the reason it already gives
for the git actions: a batch copy of the steps is how that file drifted before.

The test path is deliberately unchanged. It runs the bundle under its own node
and its own mongod, so downloading a hundred megabytes of binaries it will not
use is the wrong trade - tests/releaseBundleMatchesWorkflow.test.cjs asserts
that exactly one caller asks for the release bundle. It also finds the release
scripts the workflow runs rather than listing them, so a step added there and
not here fails the suite instead of quietly putting the difference back.

Thanks to xet7 !
2026-08-16 17:12:39 +03:00
Lauri Ojansivu 5b5ec5c726 CHANGELOG: the ten changes already in main join Upcoming.
Upcoming had the CRITICAL fix, the CodeQL alerts and three bug entries, and
main had ten commits it did not mention - the whole of the login rework the
CRITICAL fix grew into, and the docs and tooling work around it.

Added, in the order CLAUDE.md sets: a new-features subsection for the increasing
delays, the login-address tally, Problems / Offices and the per-problem summary
rows; the Sandstorm pack that was throwing its own trim away and the Admin Panel
/ People lockout display, in the bugs subsection, which is now grouped by area
like the rest; a developer-facing subsection for the active-pane consolidation,
the Problems route and template rename, and the two security-fix rules CLAUDE.md
gained; and a documentation subsection for the two docs refilings.

The **In short:** paragraph described a release that no longer exists - it
opened on the lockout and went straight to the bundle that could not start,
with nothing in between about what the lockout fix turned into. It now says
what the release amounts to, and accounts for the rest in its closing clause.

Wrapped at 80. Only the Upcoming section is touched; the released sections
below it are records.

Thanks to xet7 !
2026-08-16 17:00:08 +03:00
Lauri Ojansivu 760bd09044 Admin Panel / Problems: one active pane, instead of eleven booleans saying the same thing.
Every pane on the Problems page had a ReactiveVar of its own -
`this.showSummary`, `this.showSecurity`, `this.showIntegrity` and eight more -
on top of `activeReport`, which already held the id of the open pane. The
booleans could only ever be "activeReport equals my id", so they were forty-four
lines restating one value, and keeping them in step was manual: opening a pane
reset all eleven and set one.

That cost a pane four wiring points instead of one, and missing any of them
failed SILENTLY. Filesystem integrity got three of the four - menu entry, setter
and template branch - and no helper, and in Blaze an undefined helper is not an
error but a falsy value, so the pane drew a blank page while Summary went on
counting the problems it could not show. The Offices pane added in this release
had the same shape of hole in a different place: it set no `loading.set(false)`,
so opening it would have spun for ever.

The template asks `else if isPane 'report-integrity'` now, against one helper.
Panes that fetch through a method rather than a subscription are a list -
SELF_LOADING_PANES - which is also what fixes Offices: it is in the list, so the
spinner clears. Adding a pane is a menu entry, a branch, and one line saying how
it loads.

The guards move with it. tests/adminPaneHelpers.test.cjs, written when the
integrity pane was blank, now pins both halves of the new mechanism: the helper
must exist, and every id the template branches on must be an id the menu sets -
a typo either way is dead template or a blank pane, and neither says anything at
runtime. tests/problemsMenuOrder.test.cjs checks the invariant for EVERY pane
rather than three named ones: each menu entry must be rendered, and must either
load itself or have a report config, so a pane that spins for ever cannot ship
again. A negative test fails if a `this.showX = new ReactiveVar` comes back.

Thanks to xet7 !
2026-08-16 16:51:33 +03:00
Lauri Ojansivu 961ff6cb47 Admin Panel / Problems / Offices: where people log in from, grouped.
A new pane at /admin/problems/office, through the shared table page like every
other report here (docs/Features/Page/Table.md) - same layout, same search, same
paginator - so it needed no design of its own.

The columns are what an admin actually asks:

  Location   the country flag and the city: "London", not 100.100.100.100. An
             admin recognises their own offices at a glance, and the flag says
             WHICH London. Empty when nothing in front of WeKan resolved it.
  Address    the IPv4 or IPv6 it really is
  People     each account as its INITIALS, or its avatar where it has one, with
             its own login count beside it - the same way the board sidebar and
             a card show a member, and for the same reason: a column of avatars
             fits where a column of usernames does not. Clicking one opens the
             existing Edit user popup.
  Logins     how many, and the first-seen / last-seen window they fall in

Two additions to the shared table page rather than a second table: a cell that
holds SEVERAL people, and a leading flag kept apart from the cell's text so it
is not searched or sorted as text. Both are available to every other report now.

The pane is registered the way the others are - slug, title, menu entry,
reactive var, helper, reset and menu branch - and three guards caught the pieces
I would otherwise have missed: adminUrls wanted the URL in the design doc,
adminPanelDocsMatchMenu wanted the pane in the Problems README with its gap
count updated, and tablePage wanted the third paginator handler explained rather
than merely allowed. Ten strings in en.i18n.json, in key order, and appended to
238 language files as English placeholders for the fill rules to replace.

Thanks to xet7 !
2026-08-16 16:29:31 +03:00
Lauri Ojansivu d8d1863dba Login offices: who logs in from where, and users shown as initials or avatar.
WHAT WAS ALREADY THERE, checked first: server/lib/loginAttemptThrottle.js
resolves a client address for the REST throttle, the lockout resolves one for
DDP, models/users.js has getInitials() and profile.avatarUrl, the board sidebar
and cards show members through +userAvatar / +userAvatarInitials, cardDetails.js
builds "open in map" links for a dozen providers, and Admin Panel tables already
had a js-table-page-edit-user handler and a `userId` column option. All of it is
reused rather than rewritten; only geolocation from CDN headers is new.

THE TALLY, both directions from one successful login. On the user: which
addresses this account uses - `100.100.100.100 25, 122.122.122.122 50`. On a new
loginAddresses collection: which accounts use each address. Capped both ways
with an overflow count, as the Problems summaries are.

WHY THE SECOND DIRECTION EXISTS: an address several accounts log in from is an
office, a VPN, a university, a carrier's NAT. Blocking it takes everyone behind
it off WeKan at once - including the reverse proxy itself if
HTTP_FORWARDED_COUNT is wrong, which is every user there is. So WeKan blocks the
ACCOUNT that attempted the vulnerability and never the address, and this record
is what lets an admin see that shape before drawing any conclusion from an
address.

Blocking uses loginDisabled, the field Admin Panel / People already toggles, so
an admin unblocks from the table they know and no second notion of "blocked"
appears beside the one that exists. `services.securityBlock` records why, from
what and when. Only a 'blocked' action at high or critical severity, and only
with a userId: an unauthenticated attempt has nobody to block, and guessing from
the address is the mistake above.

LOCATION, when something in front of WeKan already knows it: Cloudflare, Fastly,
CloudFront, Vercel, Google Cloud or a hand-configured proxy. WeKan does no
geolocation of its own - no database to ship, no third-party lookup of a user's
address. An office in London is labelled London. DISPLAY ONLY, and the file says
so twice: anything a client can send it can forge, and X-Forwarded-For at least
has HTTP_FORWARDED_COUNT to say how much to believe, where a city name has
nothing.

USERS AS INITIALS OR AVATAR, in every Admin Panel / Problems table at once - it
is one line of the shared table page, so Security, Speed, Tests, CPU, Database,
Integrity and Impersonation all change together. The name is the title, so
hovering still says who it is, and clicking opens the existing "Edit user"
popup. Not +userAvatar: it renders its own anchor for the board's member popup,
and an anchor inside the cell's anchor is invalid and would swallow the click.

mapLinkFor and its Chinese datum conversions move to models/lib/mapLink.js so
the card and the Admin Panel build map links the same way, with the user's own
profile.mapProvider choice applying in both.

Thanks to xet7 !
2026-08-16 16:17:13 +03:00
Lauri Ojansivu 9476cdc0fe Increasing delays after a wrong password, per source address.
WHAT WAS ALREADY THERE, checked before writing any of this: the DDP login has
wekan-accounts-lockout (per user, now per user+address), the REST login has
server/lib/loginAttemptThrottle.js (per address, in memory), and
server/lib/loginTimingDefense.js equalises timing so a missing user cannot be
told from a wrong password. Nothing anywhere does increasing delays, and nothing
at all blocks an address. So this extends the lockout decision rather than
adding a second mechanism beside it.

THE LOCKOUT ON ITS OWN IS A STEP FUNCTION: two failures cost nothing, the third
costs sixty seconds. A guesser spends the free attempts of every window and
waits; somebody who mistyped gets no sign they are about to be locked out. A
delay that GROWS - 1s, 2s, 4s, 8s, capped - costs a guesser far more than it
costs a person, and it degrades instead of slamming shut: the account is never
unavailable, only slower to try again.

Per (user, source address), like the counter, and for the same reason: an
attacker must not be able to slow down the account's owner. A correct password
ignores the delay entirely and clears it. An attempt refused for being too soon
is NOT counted - otherwise trying faster would reach the lockout faster, and
impatience would become a weapon against the address.

Two settings, beside the three the lockout already has: known-loginDelayBase
(1s) and known-loginDelayMax (30s). Base 0 switches delays off and leaves the
lockout behaving exactly as before, so an instance that does not want this is
not forced into it.

AND A BUG OF MINE, found by looking for existing code: knownUser.js had grown a
`require('/models/lib/accountLockout')`. A Meteor package is compiled separately
from the app and cannot import app modules - no other package here reaches
across that boundary. The check is inlined and a test fails if the import comes
back.

Thanks to xet7 !
2026-08-16 15:52:54 +03:00
Lauri Ojansivu efa25f78d9 Sandstorm: pack the TRIMMED trees, instead of rebuilding them first.
v10.98's log shows the trim doing exactly what it was meant to and then being
thrown away:

  --- .meteor-spk/bundle: 856M total        (first pack, fails)
  bundle-trim: removed 5236 files, 355 MiB
  prune-unreachable-npm: removed 28 package(s), 49.9 MiB
  --- .meteor-spk/bundle: 424M total        (424M + 336M deps = 760M, under 1 GiB)
  Building Meteor app...                    <- pack rebuilds it
  App exceeds uncompressed size limit of 1 GiB

`meteor-spk pack` runs the Meteor build first, so the retry regenerated the
bundle and packed the untrimmed one.

I HAD THIS WRONG, and the comment in this file said so confidently: v10.95's
"pack REUSES the bundle" was inferred from a 852M -> 833M reading taken right
after the prune - the same point in the sequence as the 424M above - and never
checked against what the SECOND pack did. The trim never survived a pack.

Sandstorm's own `spk pack` does only the packing: it reads
sandstorm-pkgdef.capnp, whose sourceMap searchPath is exactly the two trees the
trim has just finished with, and signs with ~/.sandstorm-keyring. The retry uses
that when the binary is there - the job installs Sandstorm, so it is - and falls
back to meteor-spk with a ::warning:: saying the fallback undoes the trim,
rather than failing silently in a way that looks like the trim not working.

Unverified here: this sandbox has no spk binary, so the invocation is written
from the pkgdef's own contract rather than run. The next release either packs or
says which of the two paths it took.

Thanks to xet7 !
2026-08-16 15:45:22 +03:00
Lauri Ojansivu b06a0714b2 Sync current repository instructions.
Bring the changelog archive, frequent release, failed release, and security event guidance from CLAUDE.md into AGENTS.md.\n\nThanks to xet7 !
2026-08-16 15:42:27 +03:00
Lauri Ojansivu 9fbfb26d29 Docs: Admin Panel docs are filed the way the Admin Panel menu is.
The menu is the structure a reader already has in their head, so the docs match
it: one directory per tab, one page per pane, and a README in each listing the
panes IN MENU ORDER with the URL slug beside them - so somebody with
/admin/problems/integrity in the address bar can search for `integrity` and
find the page.

Settings (7 panes) and People (9) already had a page each and were already in
order; they gain the slug column. Problems (17) and Attachments (10) listed
almost none of theirs, so their READMEs now index every pane.

WHAT IS NOT DONE, and is now visible instead of invisible: 15 of the 17 Problems
panes and all 10 Attachments panes have no page of their own. They are dashes in
the table with the count stated under it. Writing 25 pages of guessed content
would be worse than the gap - what the panes do is described in each README
until somebody writes them up properly.

tests/adminPanelDocsMatchMenu.test.cjs is what stops this rotting again: the
menu is defined in code and the docs are written by hand, so a pane added to
models/lib/adminUrls.js would otherwise appear in the product and nowhere in the
documentation, silently. Every pane must be listed, in menu order, by slug;
every page that exists must be linked from its README; and the stated gap count
must match the dashes in the table, so the gaps cannot quietly grow while the
sentence under them still claims a smaller number.

Thanks to xet7 !
2026-08-16 15:40:24 +03:00
Lauri Ojansivu fa289e4a4d Admin Panel Problems: the route and the template say what the menu says.
The pane is called Problems in the menu, its address is /admin/problems, and
docs/Features/Admin-Panel/Problems is where it is documented. Two things still
called it Reports, from before it was renamed:

  * the route NAME was 'admin-reports', where its three siblings are 'setting',
    'people' and 'attachments' - the bare page key. So the one route whose name
    did not match its own address;
  * the template, and its three files, were adminReports.

Both are 'problems' / adminProblems now, across 41 files.

What did NOT change is `legacyBase: '/admin-reports'`. That is an address people
have bookmarked and it still redirects here - the point of the rename is that
the code agrees with the menu, not that an old link should break. The CHANGELOG
entries describing the old addresses are left alone too: a released section is a
record of what shipped.

Thanks to xet7 !
2026-08-16 15:36:04 +03:00
Lauri Ojansivu 36d8263634 Docs: the last test path that pointed at the moved page designs.
A sed that matched 'Design/Page/' missed 'docs/Design/Page' without the trailing
slash, so allBoardsPage still looked for the shared design where it used to be.

Thanks to xet7 !
2026-08-16 15:16:59 +03:00
Lauri Ojansivu daaf563910 Docs: implemented designs move to docs/Features, filed the way the menu is.
A design that describes what WeKan already DOES belongs with the feature it
describes, not in the folder for things being proposed. Fifteen docs move:

  Design/Accessibility.md      -> Features/Accessibility/Accessibility-Improvements.md
  Design/Original-Positions.md -> Features/Board/Original-Positions.md
  Design/Page/*                -> Features/Page/*   (thirteen pages that exist)
  Design/AdminPanelProblems.md -> Features/Admin-Panel/Problems/Design.md

What stays in docs/Design is what the folder is for: the principles
(Design-Principles, Monkey-Proof-Software), the comparisons, the roadmap
(Multiverse), and the proposals not yet built (Multitenancy, Autoupdate).

The Admin Panel Problems design is new and covers six changes - summaries, the
per-actor tally, IPv4/IPv6 columns, an API report, a per-address login tally,
and blocking with increasing delays. Three are shipped and three are planned;
it is filed under the feature because the pane exists, with each planned item
marked so it can become plain description as it lands.

Every reference in the repository is rewritten - 152 files, including the CSS
comments that point at the table-page design, which a first pass over .md and
.js alone missed. Two suites asserted the old paths and now assert the new
ones. A moved doc whose links nobody fixed is worse than one nobody moved.

Thanks to xet7 !
2026-08-16 15:15:01 +03:00
Lauri Ojansivu 6e208c6152 Admin Panel / People shows who is locked again, and why.
Moving the lockout counter to one per (user, source address) broke three readers
that were still looking at the flat field it replaced: the People table's lock
icon, its unlock click handler, and the lockedUsers methods behind them. Every
account would have shown as unlocked, and no admin could have unlocked one.

That is the "and nowhere else" half of the rule I had just written into
CLAUDE.md, missed on the very next fix. The negative test now fails if anything
reads the flat field again.

models/lib/accountLockout.js is the one place that knows the shape - locked or
not, since when, how many addresses and how many failures between them - so the
client helper, the click handler and the server methods cannot drift apart
again. Admin Panel / People / People answers WHY as well as whether: how many
addresses were locked out of the account and how many failures they made.
WHICH addresses is in Admin Panel -> Problems, where it is a tally rather than a
field on every user document.

Mongo cannot ask "which accounts are locked" over dynamically-named
subdocuments without an aggregation, so a DISPLAY field - lockedUntil - is
written beside the per-address state for that query alone. It is not the
counter: the decision stays per address, and a test fails if the decision or the
scope ever reads it, because a field that says "this ACCOUNT is locked" is the
vulnerability that was just fixed. Unlocking one address clears it only when no
other address is still locked.

Thanks to daniais and xet7 !
2026-08-16 15:05:00 +03:00
Lauri Ojansivu 3d08c65bb2 Admin Panel Problems: one summary row per problem, with who tried and how often.
A guard on a path an attacker controls fires as fast as they can send. One
document per event meant the database grew with the attack, the Problems page
became a scroll of near-identical lines, and the one event that mattered was
buried under ten thousand that did not. The admin's question is never "list
every attempt" - it is what is happening, how much, since when, and who.

So each problem is ONE row that accumulates:

  count            how many times this problem has happened
  firstAt … at     the window it happened in
  actors           who, each with their own count:
                     username1 25, 100.100.100.100 30
  username/ip      the most recent occurrence

WHAT MAKES TWO EVENTS THE SAME PROBLEM is the whole design. Identity is the KIND
of thing that happened - stream, bleed, category, action, source, severity, CWE,
and the database stream's own type/db/kind. The actor is NOT part of it: a
username or an address in the key gives a row per attacker per attempt, which is
the cost being removed.

A USERNAME AND AN ADDRESS ARE TALLIED SEPARATELY, not as a pair. They answer
different questions - which account, and where from - and an unauthenticated
attempt has an address and no name. Pairing them would split one attacker across
a line per address and answer neither.

THE TALLY IS CAPPED at 50 actors, with the rest counted in actorsOverflow.
Otherwise an attacker rotating addresses grows the row with the attack and
reintroduces the same bug one level down - and "and 9,412 others" is itself the
signal that the source is spread rather than single. The cap needs to know
whether an actor is new to the row, so the row's actor keys are cached per
process: reading it on every attempt would double the database work of the thing
being made cheap, and under attack the same few actors repeat.

All three loggers share the fold (server/lib/eventLogFold.js) - a slow query in
a loop and a test failing on every run repeat just as a guard under attack does.
Still fire-and-forget and still unable to throw into the caller.

EXISTING PER-EVENT ROWS ARE FOLDED IN PLACE at startup, in batches, in JS so it
behaves the same on FerretDB as on MongoDB, and idempotently - a row with firstAt
is already a summary. Nothing is lost that the summary does not carry: counts are
summed, the earliest becomes firstAt, the latest becomes at, and the latest
occurrence's actor and detail are what the row keeps.

AND A COVERAGE CHECK, as asked: tests/hallOfFameProblemsCoverage.test.cjs
requires every hall-of-fame vulnerability to be keyed, deliberate (with the
reason, as HashBleed is), or pending. 30 of 66 are keyed; the other 35 predate
the rule and are listed as pending rather than judged here, because deciding
whether a given fix has anything to detect means reading that fix and claiming
otherwise would be worse than the gap. The pending list may shrink and may never
grow, so a NEW vulnerability without a key fails.

It found one real fault immediately: the catalog said OidcBleed and the site says
OIDCBleed, so attempts were grouping under a name nobody could look up.

Thanks to xet7 !
2026-08-16 14:57:18 +03:00
Lauri Ojansivu 88fe3597ad CHANGELOG: JamBleed CRITICAL and the CodeQL alerts join Upcoming.
Thanks to daniais and xet7 !
2026-08-16 14:39:35 +03:00
Lauri Ojansivu c3299c4901 Security fixes: a test, a negative test, and an attempt visible in Problems.
Two rules in CLAUDE.md, both learned from fixes in this repository rather than
proposed in the abstract.

A TEST AND A NEGATIVE TEST, written so the fault cannot exist ANYWHERE in the
codebase rather than only where it was reported. A test that pins one call site
leaves the same mistake free to live in the other five, and that is how most of
these arrive: SignupBleed's guard read an option nothing sets and the same shape
sat in a second endpoint; the source-map trim was safe on the client and fatal
on the server. So the test proves the fix does what it claims - driving the
decision itself where it can, as arithmetic, reproducing the reporter's attack
exactly - and the negative test proves the fault is GONE rather than moved, by
searching the tree for the shape of it and asserting nothing matches. A test
that parses a source file is a real test here: it is what makes "and nowhere
else" checkable at all.

AND AN ATTEMPT HAS TO BE VISIBLE in Admin Panel -> Problems, wherever the fix
denies an operation and the denial can be attributed. That is the difference
between a hole that is closed and one that is closed and watched. The catalog
key, the wrapped record() call on the refusal path, and the rule that only an
ATTEMPT is logged and never ordinary use - a log that fills with normal traffic
hides the one line that mattered, which is why HashBleed deliberately has no
key.

JamBleed did not meet the second rule when it was committed: the catalog entry
was there and nothing ever wrote to it. It does now. A Meteor package cannot
import app code, so the reporter is injected - AccountsLockout takes an
onLockout callback, KnownUser calls it when a lock FIRES, and the app passes one
that writes a security-log event. On the lock only, not on every refused attempt
during it, or an attacker could fill Problems by holding down a key.

There are TWO construction sites - startup and the Admin Panel settings reload -
and a reload that dropped the reporter would stop recording attempts on a
running server while the guard kept working, which is the worst version of this:
nobody can see it working. So the negative test walks server/ and fails on any
`new AccountsLockout({...})` without onLockout, present or future.

Thanks to daniais and xet7 !
2026-08-16 14:33:31 +03:00
Lauri Ojansivu 78478c39d0 JamBleed: the account lockout counted an attacker's failures against the victim.
GHSA-rf3w-rj48-jxcc, reported by daniais. wekan-accounts-lockout kept ONE
counter per user - services.accounts-lockout.failedAttempts - with no notion of
where the attempts came from. Any unauthenticated attacker who knew a username
could spend three wrong passwords and lock that account out FROM EVERY ADDRESS,
repeatably, for as long as they cared to keep going. Usernames are public in
normal WeKan use, so choosing a target was trivial, and an administrator was as
easy to lock out as anyone else. Reproduced on v10.91:

  attacker, address A : Incorrect / Incorrect / Too many attempts
  victim,   address B : (correct password) Too many attempts

Affected from v10.59, and not before, for a reason worth keeping in view: the
flat counter is much older, but until the LockoutBleed fix
(GHSA-2g94-9x3m-hv37) the hooks gated on English error strings that Meteor's
ambiguousErrorMessages had already rewritten, so the counter never moved and no
account ever locked. Making the lockout WORK is what made this reachable. A fix
that turns on a mechanism inherits whatever that mechanism gets wrong.

TWO FAULTS, and the second is the one that hurts:

  1. the counter was global, so an attacker's failures were charged to the
     victim's account rather than to the attacker's address;
  2. a CORRECT password was refused while the lock held, and counted as a
     further failure on the way. The old code allowed an attempt only when
     there was no error AND no lock, so the owner typing the right password
     fell through to the same throw as the attacker.

Both are fixed in the decision itself, which is now a pure module:

  * the counter is per (user, source address). The address comes from
    X-Forwarded-For under HTTP_FORWARDED_COUNT - the same rule
    server/lib/loginAttemptThrottle.js uses, so a lockout and a throttle cannot
    disagree about who somebody is - and only the position `hops` from the right
    is read, so a forged header cannot pick its own bucket. The key is a SHA-256
    prefix: an IPv4 address is all dots and cannot be a Mongo field name, and a
    locked account should not carry a list of the addresses that attacked it.
  * a correct password is allowed FIRST, before anything reads the lock, and
    clears the state behind it. The lock exists to stop guessing; somebody who
    did not have to guess has proved they are not who it is for. It also clears
    the pre-fix flat fields, so an account left locked by the old counter is
    freed by its owner's next correct login rather than by a wait with no
    visible end.

Hammering during a lock no longer extends it either - otherwise the denial of
service returns inside the mechanism meant to stop it.

The three methods that wrote the flat counter are removed rather than left
unreachable, and a test fails if a flat write appears in the hook again.
tests/lockoutPerSourceAddress.test.cjs is 15 tests, driving the decision as
arithmetic rather than through a server: the reported attack, the correct
password during a lock, that the lock still fires and still expires, the
forwarded-header rules, and that malformed state reads as "nothing yet" rather
than throwing - a lockout that threw on an unexpected document would lock
everybody out of a database that had one.

Thanks to daniais and xet7 !
2026-08-16 14:29:18 +03:00
Lauri Ojansivu cc3201292a CHANGELOG.md holds the current MONTH; earlier months move to old-CHANGELOG/.
Moving whole years out left 1.9 MB, and that was still too large, because
releases here are FREQUENT: 2026 alone is 272 releases over eight months, and
July was 80 on its own. A year is not a small enough unit when a year is that
busy. So the cut is by month:

  CHANGELOG.md                    the current month, plus the Platforms, TODO
                                  Later and Upcoming sections
  old-CHANGELOG/<year>/<MM>.md    earlier months of the current year
  old-CHANGELOG/<year>.md         years that are over, whole

2.5 MB becomes 822 KB. Past years stay one file each because they are already
small - 30 to 107 KB - and splitting them further would trade a size problem
nobody has for a hundred more files to navigate. A month file counts releases
per day, a year file per month; both tables are regenerated from each file's own
headings on every run, so neither can drift from the sections beneath it.

TWO BUGS FOUND WHILE DOING IT, both the same shape - a pattern that matched less
than it looked like it did, and silently absorbed what it missed into the section
above rather than failing:

  * the version is MAJOR.MINOR most of the time and MAJOR.MINOR.PATCH sometimes
    (v1.49.1, v6.99.5). Requiring exactly two numbers skipped 28 sections, which
    were then filed under whatever month preceded them - 2018 counted 127
    releases instead of 147, 2023 counted 58 instead of 66, and 2016 had no file
    at all;
  * the same assumption sat in the append and refresh paths, where an archive
    whose first heading was a three-part version would have been re-read from the
    wrong offset.

Verified against the untouched original, section by section: 1,100 release
headings before and after, none lost, none duplicated, and all 1,100 section
bodies byte-identical once whitespace is normalised.

releases/changelog-archive.mjs replaces changelog-archive-years.mjs, which only
knew about years. It takes the month to keep from the FILE rather than the
clock, so two people running it on the same day agree and a quiet month cannot
archive itself out from under the next release.

Thanks to mimZD and xet7 !
2026-08-16 14:20:17 +03:00
Lauri Ojansivu 34e07e17e1 CHANGELOG: repoint the archive-split link after the amend.
Thanks to xet7 !
2026-08-16 14:04:39 +03:00
Lauri Ojansivu a7fa0deb5e Each year's archive opens with a count of releases per month.
`| 2025 | Releases |` over rows of `01`, `02` and so on. "How busy was 2019" is
the first thing a year file is asked and the last thing 159 collapsed sections
answer.

Only months that HAD releases get a row. A fixed twelve rows would put ten
zeroes in 2015's table, which is noise standing in for a fact the reader can
already see from the rows that are there.

The table is regenerated from each file's own headings on every run of
changelog-archive-years.mjs - including runs where nothing moves, so a table
added or changed later reaches every file rather than only the ones a move
happens to touch. It therefore cannot drift from the sections beneath it, and
editing it by hand is the one way to make it wrong.

Verified against every archive: the table totals 40, 52, 58, 75, 122, 111, 159,
127, 52 and 2, and so do the release headings in each file.

tests/changelogArchive.test.cjs pins the pair, plus what the move itself has to
be: no release in both CHANGELOG.md and an archive, CHANGELOG.md holding exactly
one year and linking every archive, and no archive growing an Upcoming or a TODO
Later - which is what "somebody started writing in here" looks like in a file
that is meant to be a record.

Thanks to mimZD and xet7 !
2026-08-16 14:03:25 +03:00
Lauri Ojansivu 7fa8ba12ea CHANGELOG.md keeps the current year; older years move to old-CHANGELOG/.
CHANGELOG.md had reached 2.6 MB and 51,365 lines across 1,070 releases going
back to 2015, which is slow to open and slower to read on the web.

The current year stays; every older year moves out whole to
old-CHANGELOG/<year>.md, with a bullet in `# Platforms` linking each one. 798
releases move, 272 stay, and the file goes from 2.6 MB to 1.9 MB - 2026 is a big
year on its own, because releases here are frequent.

Nothing is deleted and no entry is rewritten. An archived section reads exactly
as it did before, for the same reason a released section is never edited in
place: it is a record. That `git blame` is less useful on the split file is
accepted rather than worked around - the history is still in git for anyone who
wants it, through gitk, git-gui or `git log --follow`, and being small enough to
open is worth more.

releases/changelog-archive-years.mjs does it, and it is a JANUARY job: run it
once when a year turns over. It is idempotent, so a stray run does nothing.
Cutting by YEAR rather than by a count of releases is what makes a link into the
archive stable - once a year is over, nothing in it moves again.

One thing the script had to learn: eleven years of headings do not agree on
their own wording. Of 1,070, 539 say "Wekan release", 524 say "WeKan ® release",
and the rest are one-offs - "Sandstorm-only Wekan release", "Wekan relase", and
one that explains it was NOT released. Matching the strict modern form found
only half of them and silently absorbed the others into the section above, so
the version and the DATE are what is matched and whatever follows is left alone.
Verified by counting headings before and after: 1,070 both times, none lost and
none duplicated.

Thanks to mimZD and xet7 !

Fixes #6580,
2026-08-16 13:58:40 +03:00
Lauri Ojansivu aebfe1fcba CHANGELOG: the release-tooling fix joins Upcoming.
Thanks to xet7 !
2026-08-16 13:47:01 +03:00
Lauri Ojansivu 43341fc753 Releases are frequent: say so in CLAUDE.md, and make the tooling suit it.
I had been describing releases as happening "mid-work" and treating each one as
an interruption to reason around. That is backwards. The maintenance loop here
is build.sh option 1 (git pull + git push) and releases/release-all.sh with no
arguments, several times a day when a fault is being chased. A release landing
in the middle of a piece of work is not a special case; it is the normal case,
and anything that only works when releases are rare is broken here.

CLAUDE.md now says that where the release instructions are, with the two
consequences that have actually cost a released section its accuracy: work
continues immediately after a release, and a released section is a record rather
than a draft.

And the tooling stops making the mistake possible. release-all.sh renamed
`# Upcoming WeKan ® release` to `# v<NEW> ...` and left nothing behind, so the
next entry written had nowhere correct to go - it landed above the closing
"Thanks to above GitHub users" line, which is now INSIDE the release just
published. v10.96 and v10.97 both ended up that way; the second was worse than
misplaced, because an entry already published was EDITED afterwards and the
notes then described a smaller, tidier change than the one that shipped - and
the one that shipped was the one that stopped the bundle starting.

releases/changelog-open-next.mjs opens the next section as soon as a release is
named, carrying the `**In short:**` placeholder and the binaries table that
tests/changelogFormat.test.cjs requires, so the file is valid the moment
release-all.sh finishes. Re-running it is a no-op, since release-all.sh can be
run again after a failure. changelogFormat now allows an Upcoming that is empty
AND still carries the placeholder - and fails one that has entries and still
carries it, because that is a summary nobody replaced.

tests/changelogEntriesBelongToTheirRelease.test.cjs is the check that the home
was used: git knows which commits a release contains, so an entry linking a
commit that is not an ancestor of its release is in the wrong section. Scoped to
the newest three releases on purpose - over the whole file it flags 83 entries
back to v2.99, from old release practices and history rewrites, and a guard
reporting 83 things nobody will act on is a guard people learn to skip.

Thanks to xet7 !
2026-08-16 13:45:30 +03:00
Lauri Ojansivu ba8bc48d51 Credit the reporter of the nodemailer-openpgp crash, and close the issue.
#6602 is the v10.97 crash-loop, reported from a Docker instance upgraded from
v10.91: `Cannot find module ".../nodemailer-openpgp/lib/nodemailer-openpgp.js"`.

Already fixed - the reachability graph now reads Meteor's `module.link()` and
not only `require()`, so the package it links on its first tick is no longer
mistaken for dead code, and the policy entry that removed it is withdrawn. That
commit went in without a `Fixes` trailer, so the issue stayed open; this closes
it and records where the fix is.

Not released yet. Until it is, v10.97 cannot start: pull `wekanteam/wekan:v10.96`
or earlier, or use a bundle from before the npm prune.

Thanks to Heart1010 and xet7 !

Fixes #6602,
2026-08-16 13:33:33 +03:00
Lauri Ojansivu dde77861b1 CHANGELOG: v10.97 gets its record back, and the fix moves to Upcoming.
v10.97 was released mid-work, exactly as v10.96 was, so `# Upcoming` became
`# v10.97` and the entry written afterwards landed inside it again.

Three repairs, and two of them are to what STAYS in v10.97, because a released
section is a record of what shipped:

  * the module.link fix moves out, into a new Upcoming above;
  * its `**In short:**` no longer claims a third fix that came later, and says
    61 MiB again rather than 40;
  * its npm-prune entry is restored to the four categories and 61.3 MiB that
    v10.97 ACTUALLY shipped. Editing it down to three was rewriting history to
    look better than it was.

What that entry gains instead is the truth about itself: one of its four
categories was wrong, this release cannot start because of it, and the fix is in
the release above. Somebody reading v10.97's notes is most likely somebody whose
10.97 bundle just died - that is the one thing those notes have to say.

Thanks to xet7 !
2026-08-16 13:31:21 +03:00
Lauri Ojansivu bd178cbc77 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-16 13:27:52 +03:00
Lauri Ojansivu ef8e295ba5 CHANGELOG: the npm prune's measurement is corrected where it was made.
61.3 MiB becomes 40.0 MiB and four categories become three, because the fourth
was withdrawn: the graph that called openpgp unreachable could not read
Meteor's module.link(). The entry that shipped the mistake is corrected rather
than quietly left, and the fix gets its own entry beside it.

Thanks to xet7 !
2026-08-16 13:27:51 +03:00
Lauri Ojansivu 71ff74c6d2 The reachability graph must read Meteor's module.link, not only require().
v10.97 shipped a bundle that crash-looped:

  Error: Cannot find module ".../nodemailer-openpgp/lib/nodemailer-openpgp.js"
    at packages/email.js:347

packages/email.js does not require() that package. Meteor compiles an ESM import
to its own linker call:

  module.link('nodemailer-openpgp',{openpgpEncrypt(v){openpgpEncrypt=v}},6);

and prune-unreachable-npm.mjs only ever looked for require(). So it missed every
ESM import in every Meteor package, which is most of them, and reported live
code as dead. The reachable count goes from 211 to 450 with the fix - the
measurement that entry was based on was badly wrong, not marginally.

Four forms count now: require(), module.link(), module.watch(require()) and
module.dynamicImport().

THE SAFETY WORKED, which is the one good part. With the corrected graph the
policy still named openpgp, the graph vetoed it, and the tool refused and said
so rather than deleting it. That entry is now removed outright rather than left
to be vetoed every run: the reason is what grants the permission, and this
reason - "reachable only through an optional plugin nothing requires" - was
never true. 61.3 MiB becomes 40.0 MiB, all of it typescript, @types/* and sinon.

AND THE CHECK THAT WAS MISSING BOTH TIMES. v10.96 died on a source map that had
been deleted but not un-named; v10.97 on this. Both were reasoned about by
reading the code, and reading the code is how both mistakes were made. So
releases/bundle-smoke-boot.sh starts the bundle with a database address that
cannot answer and requires it to get as far as trying to reach it - which proves
the whole server image loaded, because the database is the first thing WeKan
needs that the check does not provide. The amd64 build runs it after the trim
and the prunes, and every other architecture's bundle derives from that one.

It is verified against both real failures: a bundle with nodemailer-openpgp
removed, and a manifest naming maps that are not there. Each fails with its own
diagnosis, because the fix for each is a different one. A bundle that exits
quietly or hangs is not a pass either - a smoke test whose failure mode is
passing when it learned nothing is worth less than none.

Thanks to xet7 !
2026-08-16 13:27:51 +03:00
github-actions[bot] e6ec503ae1 Bump versions for v10.97 2026-08-16 02:27:53 +00:00
Lauri Ojansivu d7efcbb2f8 Prepare v10.97 release 2026-08-16 05:26:47 +03:00
Lauri Ojansivu eacbb670a3 CHANGELOG: the post-release fixes move out of v10.96 and into Upcoming.
v10.96 was released in the middle of this work, which renamed Upcoming to
`# v10.96` - and the four entries written after it went on landing in that
section, because the anchor they were added above (the closing "Thanks to above
GitHub users" line) had moved into it.

A released section is a record, not a draft. So the three post-release entries -
the source-map boot fix and the snap's two - move into a new
`# Upcoming WeKan ® release` above it, with the two commits that had no entry at
all beside them: the npm prune and the i18n guard.

Two repairs to what stays behind, both of which now describe work that did not
ship in v10.96: its `**In short:**` claimed the snap had stopped taking itself
offline, and its source-map entry pointed at "the entry below" for the boot
crash. The first sentence is removed and the second is rewritten to stand on its
own and say the fix is in the release above - a reader of v10.96's notes should
still learn that its bundle could not start, which is the one thing about that
release worth knowing.

Thanks to xet7 !
2026-08-16 05:26:09 +03:00
Lauri Ojansivu faf9bd8027 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-16 05:21:05 +03:00
Lauri Ojansivu a113464b61 CHANGELOG: the snap's two-database startup gets its own group in Upcoming.
Two entries under it: the read that stamped the files it then read as evidence,
and the comparison that held the web port shut while it ran. They are halves of
one outage - the first made it recur at every restart, the second made each
recurrence an outage - so they sit together.

Thanks to xet7 !
2026-08-16 05:21:03 +03:00
Lauri Ojansivu 062d4fede5 The database comparison at startup cannot take the site down any more.
An instance on 10.96 went offline at every restart. wekan-control ran
bin/database-autopick synchronously and unbounded before anything opened the web
port - and that comparison starts each database on a temporary port to read it,
which on a real instance is minutes. Until it returned a browser got a
connection timeout, and the reason was in `snap logs`, the last place somebody
whose site is down thinks to look.

That is #6592 one step earlier than #6592 was fixed. The database WAIT already
serves a "waiting for its database" page; the helpers for it were defined 200
lines BELOW the comparison that needed them. They move up, and the comparison
uses them - after the same grace period, so a comparison that finishes in
seconds does not flash a page up and teach proxies to cache a 503 for a healthy
site.

And a bound: WEKAN_AUTOPICK_TIMEOUT, 900s by default. When it runs out WeKan
says so, changes nothing, and starts on the database already selected. Stopping
the comparison is safe - it never deletes anything on either side, and the merge
it may be in the middle of only INSERTS what is missing, so a partial one is
fewer documents copied and the next start finishes the job. Serving the site
beats finishing the comparison.

WEKAN_AUTOPICK was an env var and nothing else, so an admin whose site was down
had no supported way to skip the comparison. It is a snap config key now, with
the bound beside it:

  snap set wekan autopick=false
  snap set wekan autopick-timeout=1800

The timeout message names all three of those, because somebody reading it is
somebody whose site just came up late.

This is the other half of the mtime fix. Before that, the comparison ran at
EVERY start - autopick's own mongod stamped the files the staleness check reads
- so every restart took the site down for a comparison that could never
conclude. One made it recur; this one makes it survivable.

Thanks to xet7 !
2026-08-16 05:21:03 +03:00
Lauri Ojansivu cf283be5e1 CHANGELOG: the source-map entry gets its correction, and the boot fix its own.
The maps entry claimed nothing on any loading path reads a .map. That was true
of the client and false of the server, and a released image crash-looped on it.
The claim is corrected where it was made, and the fix has an entry of its own.

Thanks to xet7 !
2026-08-16 05:21:03 +03:00
Lauri Ojansivu dc19f06612 Dropping a source map must un-name it too, or the server will not boot.
A released image crash-looped:

  Error: ENOENT: no such file or directory,
    open '/build/programs/server/packages/ecmascript.js.map'
    at /build/programs/server/boot.js:101:29

My claim that "nothing on any loading path reads a .map" was true of the client
and false of the server. boot.js reads every map NAMED in
programs/server/program.json, at boot, unconditionally:

  serverJson.load.forEach(function (fileInfo) {
    if (fileInfo.sourceMap) {
      var rawSourceMap = fs.readFileSync(path.resolve(serverDir, fileInfo.sourceMap), 'utf8');

63 of the 102 load entries name one, 60 MiB of them, so removing the files left
63 dangling names and the server died before opening its port.

The names now go with the files: the same pass deletes `sourceMap` and
`sourceMapRoot` from every load entry. The client is unaffected and always was -
its program.json names no maps at all (678 manifest entries, zero sourceMap
fields) and webapp reads only program.json itself at startup, so a client map is
found through the //# sourceMappingURL comment and 404s harmlessly when absent.

Verified by BOOTING a trimmed bundle rather than by reading the code again: with
uWebSockets.js, the legacy client and all 4766 maps removed, `node main.js`
loads the whole server and reaches AccountsServer.init, failing only on the
deliberately unreachable MONGO_URL it was given. That is the check that was
missing the first time.

tests/bundleTrim.test.cjs pins the invariant boot.js actually requires - every
map program.json names exists on disk - for BOTH settings of --keep-maps, so
neither branch can drift back into the crash.

Thanks to xet7 !
2026-08-16 05:21:03 +03:00
Lauri Ojansivu da221c5496 Reading MongoDB to compare the copies must not look like writing to it.
An instance on 10.96 serving FerretDB prints this at every restart:

  WeKan: BOTH databases have been written to since the migration.
    MongoDB  last written 2026-08-16 01:41 (WiredTiger.wt)
    FerretDB last written 2026-08-16 01:38

Nothing had opened that MongoDB in a month. The giveaway is in the report
itself: the MongoDB timestamp is the minute the snap started, three minutes
AFTER the FerretDB it is compared against.

bin/ferretdb-migration-stale decides "MongoDB has been written to" from the
mtimes of the WiredTiger data files, and its own header admits an mtime cannot
tell "somebody used this database" from "this database was started".
bin/database-autopick is the answer to that - it reads both copies and compares
their contents. But to read MongoDB it STARTS mongod, and starting mongod does
recovery and a checkpoint, which stamps exactly the files the staleness check
reads. So the diagnostic manufactures its own evidence: after autopick has run
once, MongoDB looks freshly written forever, and the ambiguity can never resolve
however long nobody touches it.

The read now notes the newest data-file mtime before mongod starts and puts
anything newer back afterwards - files mongod CREATED during the read included,
since a new journal file is the newest thing in the directory and reports as a
write on its own. mongod does not use mtimes (the catalog and the journal are
what it reads) and what it checkpointed is not user data, so the metadata is
made to say what is true: nobody wrote to this database. Every way out of the
read restores, including the failures, because a failing read is exactly when
the ambiguity gets reported.

A write that happened BEFORE the read still survives it - the point is to hide
WeKan's own read, never somebody's work - and the log and pid files that
ferretdb-migration-stale already ignores are left alone.

tests/autopickReadLeavesNoTrace.test.cjs runs the real shell functions against a
WiredTiger-shaped directory, and pins the two filename lists together: a file
the staleness check reads as evidence and the read does not put back is a stamp
left behind.

Thanks to xet7 !
2026-08-16 05:21:03 +03:00
Lauri Ojansivu 1619a6c37a Guard that every language stays lazily loaded.
WeKan ships 246 languages, 37 MB of JSON. What makes that affordable is one
character in each of 246 near-identical blocks in imports/i18n/languages.js:

  load: () => import('./data/ace.i18n.json'),

A dynamic import() is a SPLIT POINT. rspack emits each language as its own
chunk - measured on a built bundle: 238 chunks, 34 MiB of JS - and a browser
fetches exactly one, about 145 KB for the language in use. Written instead as
`import data from './data/ace.i18n.json'` or with require(), the same line stops
splitting and 34 MiB joins the main bundle for every user, in every language.

That edit is made by hand each time a language is added, which is why it wants a
guard rather than a convention. Seven checks: every entry has a load:, every
load: is the dynamic form, none uses require(), no static import of a data file,
nothing outside languages.js imports one, every file is claimed and every claim
has a file, and a Transifex pull writes the file the app actually loads.

The last two are symlink-aware, and that is the point of them. `.tx/config`'s
lang_map renames most of Transifex's underscored locales to WeKan's hyphenated
files; for the two it does not - km_KH and ru_RU - the hyphenated name is a
SYMLINK to the file Transifex writes. Two names for one file, not two copies.
Reading it the other way costs a language its real translations, so both checks
compare through realpath and say so in their comments.

Thanks to xet7 !
2026-08-16 05:21:03 +03:00
Lauri Ojansivu d6e21202f2 Prune what no require() in the server bundle can reach.
programs/server/npm/node_modules is 347 MB of an ~850 MB bundle, and measuring
its require graph says 206 MB of that is unreachable from any server entry
point. Two things cause it, neither a bug in Meteor:

  * the tree is OUTSIDE the bundler's graph. Meteor 3.5 compiles through rspack
    - the server output is minified and programs/server/app/app.js requires only
    76 bare specifiers, because rspack inlined the app's real dependencies into
    it - but this tree is what Atmosphere packages declare with Npm.depends() and
    load through Npm.require(), which rspack cannot follow. The tree is largely
    the INPUT to a build whose OUTPUT ships beside it;
  * it is a full `npm install`, devDependencies included.

Duplication is the smaller half: 586 distinct packages exist as 815 copies, but
the redundant copies are only ~28 MB, because Meteor keeps per-package
node_modules on purpose so packages can pin conflicting versions.

releases/prune-unreachable-npm.mjs removes 61.3 MiB of it, in four categories
whose reason is PROVABLE rather than merely plausible - the standard the
uWebSockets.js removal met:

  * typescript, 23.2 MiB - a devDependency of 196 packages here (the @aws-sdk
    family) and a runtime dependency of none;
  * openpgp + nodemailer-openpgp, 21.3 MiB - reachable only through an optional
    nodemailer plugin nothing in meteor/email requires;
  * @types/*, 9.6 MiB across 24 copies - verified to contain no .js at all, so
    no require() can resolve into one;
  * sinon, 7.2 MiB - a test framework inside meteor/ostrio_files.

The remaining 145 MB of unreachable packages STAYS. jquery, hotkeys-js and the
@azure storage adapters are almost certainly dead too, but "almost certainly" is
not the standard, and the tail of 590 packages is where a static scan is most
likely to be wrong.

Two independent safeties, because this is riskier than the uws removal was. The
reachable set is recomputed from the bundle every run and deliberately
OVER-approximated - once a package is reached, every file in it is scanned and
every require() string literal counts. The policy then only proposes; the graph
has a VETO, so an entry whose package is actually required is refused and said
out loud rather than applied. And after deleting, every path in the reachable
set must still exist or the run fails.

Verified on a real bundle: 473M -> 410M, and the reachable count is 211 before
and after - nothing that was reachable became unreachable. A second run finds
nothing left to do.

Thanks to xet7 !
2026-08-16 05:21:02 +03:00
github-actions[bot] 024a4e3a42 Bump versions for v10.96 2026-08-16 01:01:12 +00:00
Lauri Ojansivu f98f5b75c3 Prepare v10.96 release 2026-08-16 04:00:07 +03:00
Lauri Ojansivu 61a5a4c92e CHANGELOG: Upcoming gets the entry it was missing, and the right subsections.
The source-map removal was folded into the sockjs entry, whose commit had
KEPT source maps everywhere except Sandstorm - so that entry claimed a change
its commit did not make, and the commit that did make it had no entry at all
and no link. It has both now.

The last two entries are also not build failures: they are a decision about
what a bundle carries. They move to their own 'and changes what every platform
ships:' subsection under a group line of their own, and the In short paragraph
stops saying 'two build failures' for what is now five entries across two
subsections.

Thanks to xet7 !
2026-08-16 03:55:55 +03:00
Lauri Ojansivu a1e175e5f5 CHANGELOG: the source maps go from every platform, not only Sandstorm.
Thanks to xet7 !
2026-08-16 03:50:19 +03:00
Lauri Ojansivu c1e0f02979 Source maps go from every platform, not only the Sandstorm .spk.
The .zip bundles and the Docker image were trimmed with --keep-maps, so only
Sandstorm - which had a 1 GiB ceiling to get under - dropped them. Every
platform drops them now.

152 MiB per bundle, measured: 188 MiB of maps across 4766 files, less the 36 MiB
in web.browser.legacy that --drop-legacy-client already takes with it.

Nothing on any loading path reads one. A source map translates a position in
built code back to the source that produced it - this bundle's server side is
one 117 MiB programs/server/app/app.js, and its 58 MiB app.js.map is what turns
`app.js:1284531` into a file and a line. The two things that read it are browser
devtools, which fetch the .map only when they are open, and Node stack traces
through source-map-support. A released bundle has neither attached to it.

The `//# sourceMappingURL` comments stay behind, and are comments: a missing
target means devtools shows compiled positions and a server stack trace prints
bundle offsets. Debugging a production crash goes back to reproducing it against
a development build, which is where the maps still are.

Thanks to xet7 !
2026-08-16 03:49:55 +03:00
Lauri Ojansivu c9c2bcdb29 CHANGELOG: sockjs everywhere joins Upcoming.
Thanks to xet7 !
2026-08-16 03:42:31 +03:00
Lauri Ojansivu 33c867f4d8 Every platform talks sockjs, and no bundle ships uws or the legacy client.
uws is not reliable enough yet to be what a default points at, so nothing WeKan
ships selects it any more, and nothing carries the module.

DEFAULTS. Every docker-compose file, start-wekan.sh, start-wekan.bat, build.sh,
build.bat, the image's own ENV block, the snap config and the Sandstorm grain
now say sockjs. The commented-out `#- DDP_TRANSPORT=uws` alternatives beside
them are replaced by a note saying why there is no alternative, rather than
advertising a value that no longer works.

BUNDLES. Every place a built bundle is post-processed - the amd64 build, each
per-arch rebuild, the repack container, install-node-for-arch.sh and the
Dockerfile - now runs bundle-trim.mjs beside the prune it already ran:

  * --transport sockjs removes uWebSockets.js entirely. 121 MiB of prebuilt
    binaries for OS/CPU/ABI combinations one machine cannot use, and a sockjs
    server never requires the module at all: ddp-server's
    `Npm.require('uWebSockets.js')` is inside the uws transport's setup(), which
    runs only for the transport that was chosen.
  * --drop-legacy-client removes programs/web.browser.legacy, a whole second
    copy of the client for browsers without modern JS. 81 MiB measured. Meteor
    supports running with architectures excluded and says so in webapp's
    categorizeRequest(): "If our preferred arch is not available, it's better to
    use another client arch that is available than to guarantee the site won't
    work". An old browser is served web.browser; the 404 branch below that is
    reached only when NO arch matches, which cannot happen while web.browser is
    there, and autoupdate iterates the programs that actually loaded.

    The files are only half of it. The arch is NAMED in programs/server/config.json
    and star.json, and boot.js builds a dynamic-import root for every name in the
    first - so the name is removed with the files. Those manifests are mode 444
    as Meteor writes them, so they are made writable, rewritten, and set back:
    failing there would leave the one state that actually breaks a server, files
    gone and manifests still naming the arch.

UPGRADES. An existing docker-compose.yml that says DDP_TRANSPORT=uws would ask
the new image for a module it does not have and crash-loop on the require. So
wekan-entrypoint.sh and the bundle's start-wekan.sh coerce uws back to sockjs
before starting anything, and print why - a setting silently ignored is worse
than one that fails.

tests/sockjsEverywhere.test.cjs pins all three halves together, because each is
useless or harmful without the others: a default without the coercion is an
upgrade trap, a coercion without the trim is dead weight, and a trim without
both is a crash. tests/bundleTrim.test.cjs covers the legacy removal, the
manifest deregistration and the read-only rewrite.

Thanks to xet7 !
2026-08-16 03:40:57 +03:00
Lauri Ojansivu 15b94e7e58 CHANGELOG: the sockjs grain gets its own entry beside the trim.
Two commits, so two entries: the trim that thins uWebSockets.js to one
platform's prebuilds, and the removal of the module altogether now that the
grain's transport is pinned to sockjs. Each links its own commit.

Thanks to xet7 !
2026-08-16 03:22:41 +03:00
Lauri Ojansivu cdbd33b590 The Sandstorm .spk ships no uWebSockets.js at all: a grain talks sockjs.
Trimming uWebSockets.js down to the prebuilds a grain's own platform can open
freed 93M of its 121M. It can all go, because a grain loads NONE of it.

The uws transport is optional in Meteor 3. `ddp-server`'s transports/index.js
resolves the transport from `Meteor.settings.packages['ddp-server'].transport`,
then `DDP_TRANSPORT`, then `DISABLE_SOCKJS`, and DEFAULTS to sockjs - and
`Npm.require('uWebSockets.js')` sits inside the uws transport's `setup()`, which
runs only for the transport that was actually chosen. `sandstorm-pkgdef.capnp`
sets none of those, and its `environ` is by its own comment the app's ENTIRE
environment, so a grain has been running sockjs all along while carrying 121M of
a module it never required.

So the pkgdef now PINS `DDP_TRANSPORT=sockjs` - the removal should rest on a
stated fact, not on an upstream default staying put - and `bundle-trim.mjs`
takes `--transport sockjs`, which removes the module whole rather than thinning
its prebuilds. 120 MiB measured, 27 MiB more than the platform trim, and the
.spk is now ~860M against the 1 GiB limit.

It is a FLAG and not the default because WeKan asks for uws nearly everywhere
else - docker-compose, start-wekan.sh, build.sh - and those bundles keep the
module, trimmed to the prebuilds their own platform can open.

tests/bundleTrim.test.cjs ties the grain's pinned transport to the flag the .spk
is trimmed with. If they ever disagreed, the grain would require a module that
was left out and fail to boot, which is the one way this can go wrong.

Thanks to xet7 !
2026-08-16 03:20:46 +03:00
Lauri Ojansivu 453d4bdf4d CHANGELOG: the cancellation fix and the Sandstorm trim join Upcoming.
The Sandstorm size limit leaves TODO Later with them: v10.95's size report
named what filled the gigabyte, so it is no longer a question a run has to
answer.

Thanks to xet7 !
2026-08-16 03:10:15 +03:00
Lauri Ojansivu 14e8ca4a1e The Sandstorm .spk gets under 1 GiB, by dropping what a grain cannot run.
v10.93, v10.94 and v10.95 all failed to pack with *App exceeds uncompressed size
limit of 1 GiB*. v10.95 was the first run whose size report worked, and it named
the gigabyte: `.meteor-spk/bundle` 852M plus `.meteor-spk/deps` 336M, so 1188M
against a 1024M ceiling. It also answered the question that report was written
to ask — a second `meteor-spk pack` REUSES the bundle rather than rebuilding it,
because the pruned tree stayed pruned across the retry (852M to 833M). So
trimming between the two attempts works; 19M was simply never going to close a
165M gap.

Measuring the bundle rather than guessing at it turned up two passengers that
are large, unreachable at runtime, and safe to drop:

  * uWebSockets.js ships TWENTY prebuilt binaries — linux/macOS/Windows times
    x64/arm64 times four Node ABIs, 121M — and its loader is one line:
    `require('./uws_' + process.platform + '_' + process.arch + '_' +
    process.versions.modules + '.node')`. A grain is one platform running one
    Node; the other sixteen files can never be opened by it. Keeping every ABI
    of the target platform+arch, so a Node major bump still finds its binary,
    still frees ~93M.
  * Source maps — 4766 files, 188M, over a fifth of the bundle. They exist for
    a debugger attached to the process. A packed app has none, and a missing
    .map degrades a stack trace at worst.

releases/bundle-trim.mjs does both, measured at 281 MiB on a real bundle, and
the Sandstorm leg runs it beside the existing prune before the retry pack:
1188M becomes ~888M, with 136M of headroom.

An architecture with NO uWebSockets.js prebuild at all — ppc64le, s390x,
riscv64, where ddp-server falls back to sockjs — is left completely alone, since
deleting the other platforms' files there would free nothing that matters and
could only break the fallback. tests/bundleTrim.test.cjs pins that, the four
kept ABIs, that a directory merely ending in `.map` is not a source map, and
that the trim runs before the retry pack rather than after it.

Thanks to xet7 !
2026-08-16 03:09:02 +03:00
Lauri Ojansivu 0f374a0d41 Cancelling a release run stops it, and a stuck job no longer needs a human.
Pressing Cancel on a release-all run did not stop the `docker` job: it kept
going for another twenty minutes and would have pushed a multi-arch image for a
release that was being abandoned.

The cause is `always()`. It is true while a run is CANCELLING, which is exactly
why it was chosen in v10.80 — a `build-mac-x64` queued on a runner label GitHub
had retired sat for two hours, was cancelled by hand, and that cancellation
SKIPPED `charts`, `ucs` and `nextcloud`, so the Helm chart for a published WeKan
was never pushed. `always()` fixed that by ignoring cancellation entirely, and
ignoring cancellation is also what disabled the Cancel button on a job that
takes hours.

`!cancelled()` keeps the half that matters — do not skip me because a SIBLING
failed — and drops the half that overrides a person. Nine job-level conditions
across release-all.yml, release-all-missing.yml, Flatpak.yml and AppImage.yml
change; the 22 step-level `if: always()` reporting steps do not, because those
correctly print CANCELLED.

That alone would reopen v10.80, so the other half of the fix is that no job
needs a hand cancellation any more: the eight jobs that had no `timeout-minutes`
now have one, and every job in release-all.yml is bounded. A stuck job FAILS on
its own, and a failure satisfies `!cancelled()` the same way `always()` let it
through. Cancel is left meaning only what a person meant by it.

tests/releaseCancelSafeJobs.test.cjs records both incidents and now pins the
opposite of what it used to: no job-level `always()` anywhere, and a timeout on
every job.

Thanks to xet7 !
2026-08-16 03:08:47 +03:00
Lauri Ojansivu 262d2befae CHANGELOG: the two dependabot merges join Upcoming.
Both landed after 'Bump versions for v10.95', so they are not in v10.95:
#6600 raised @aws-sdk/lib-storage 3.1104.0 to 3.1109.0 and #6599 raised
@aws-sdk/client-s3 3.1108.0 to 3.1109.0. They are one dependency batch of
plain bullets closing with 'Thanks to dependabot.', as CLAUDE.md keeps
them, and they say what each package is for rather than only its numbers -
the S3 client the attachments go through, and the multipart helper that
streams the large ones.

The In short paragraph accounts for them in a clause, and the SignupBleed
entry's commit link is checked against the current history, which has moved
twice since it was written.

Thanks to xet7 !
2026-08-16 02:40:57 +03:00
Lauri Ojansivu 4bbb023f5d CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-16 02:37:32 +03:00
Lauri Ojansivu 1991aed713 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-16 02:37:31 +03:00
Lauri Ojansivu a3f1b62629 SignupBleed: registration disabled, and the REST API created accounts anyway.
POST /users/register guarded itself with
Accounts._options.forbidClientAccountCreation, which NOTHING IN WEKAN EVER
SETS:

  * the only Accounts.config() call, in server/accounts-common.js, sets
    loginExpirationInDays and nothing else;
  * forbidClientAccountCreation: disableRegistration in config/accounts.js
    is passed to AccountsTemplates.configure() - the useraccounts package's
    own options object, not Meteor's Accounts;
  * and that disableRegistration is only assigned inside an async
    Meteor.call('isDisableRegistration') callback that fires AFTER
    configure() has run, which the file's own comment already records.

So the condition was always falsy and the endpoint never refused anybody.
Turning registration OFF in the Admin Panel closed the sign-up form and
left POST /users/register creating accounts for whoever asked - on an
instance whose administrator had decided nobody else may join. The setting
is read from where it actually lives now, the same way the
isDisableRegistration Meteor method reads it, and a missing Settings
document still means registration is allowed rather than locking a fresh
instance out.

Every call that reaches the refusal is an attempt, because there is no
legitimate caller once the admin has turned registration off, so it is
recorded and shows in Admin Panel / Problems under authz.register /
SignupBleed.

Found while reviewing PR #6598 by AhmedLukman, filed against #4774 - a 403
nobody could reproduce, and which this line cannot produce. Looking for it
turned up the opposite fault.

Nine tests, four of them negative, on a route that had none at all: that
the setting is read where the Meteor method reads it, that the dead Meteor
option is not consulted again AND is still never set anywhere (so a future
change cannot quietly reintroduce two sources of truth that disagree), that
an enabled instance still creates the user and gets its token, and that no
Settings document does not refuse everybody.

Thanks to AhmedLukman and xet7 !
2026-08-16 02:37:31 +03:00
Lauri Ojansivu 6f46d836d8 Merge pull request #6599 from wekan/dependabot/npm_and_yarn/aws-sdk/client-s3-3.1109.0
Bump @aws-sdk/client-s3 from 3.1108.0 to 3.1109.0
2026-08-16 02:37:15 +03:00
dependabot[bot] 45a22792bc Bump @aws-sdk/client-s3 from 3.1108.0 to 3.1109.0
Bumps [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) from 3.1108.0 to 3.1109.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1109.0/clients/client-s3)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-s3"
  dependency-version: 3.1109.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-15 23:36:25 +00:00
Lauri Ojansivu 7120a9fb1e Merge pull request #6600 from wekan/dependabot/npm_and_yarn/aws-sdk/lib-storage-3.1109.0
Bump @aws-sdk/lib-storage from 3.1104.0 to 3.1109.0
2026-08-16 02:33:57 +03:00
github-actions[bot] 38bf09d3ee Bump versions for v10.95 2026-08-15 23:14:01 +00:00
Lauri Ojansivu 26d31f42ff Prepare v10.95 release 2026-08-16 02:12:54 +03:00
Lauri Ojansivu a7928388f0 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-16 02:12:17 +03:00
Lauri Ojansivu e2b305d5fe CHANGELOG: the Sandstorm diagnostic fix goes in a new Upcoming.
v10.94 was tagged and its section is a record, and the fix came after that
prepare, so it belongs above it rather than inside it - the same rule the
v10.93 fixes followed.

The entry says what the v10.94 log actually showed: the size report added
in that release printed nothing, because it ran before meteor-spk pack and
pack is what builds the trees it was measuring. It now reports after pack,
dereferences the symlink that hid most of the size, and prunes the
build-only toolchain for one more attempt before giving up.

Thanks to xet7 !
2026-08-16 02:12:16 +03:00
Lauri Ojansivu 671da7c166 Sandstorm: the size report measured directories that did not exist yet.
v10.94's log shows the report printing NOTHING:

  --- packing (Sandstorm refuses over 1 GiB uncompressed)
  Building Meteor app...

meteor-spk pack is what BUILDS the Meteor app - "Building Meteor app..."
comes after it starts - so .meteor-spk/deps and .meteor-spk/bundle do not
exist until it has run. The report was measuring two directories that were
not there, found neither, and skipped both silently. A diagnostic that runs
before the thing it diagnoses tells you nothing, which is what v10.94 got.

The sizes are taken AFTER pack now: on failure, which is when they are the
whole point, and on success, where the headroom against 1 GiB is worth
knowing before the next thing is added. du -shL DEREFERENCES, because deps
is a symlink to the tree build-deps.sh assembles and plain du -sh on a
symlink reports the link, not the gigabyte behind it. The bundle's server
packages are listed separately, since they are the part every other bundle
prunes.

And ONE bounded retry, which is a real attempt at the fix rather than more
looking. meteor-spk pack runs npm install inside the bundle's
programs/server - that is where tar@6.2.1 and node-gyp's tree come from in
the log - and the Sandstorm leg is the only one that never removes them
afterwards; prune-build-only-modules.mjs drops 83 of 120 packages
everywhere else, and nothing in a packed app runs any of them. Whether it
helps turns on something no log has answered yet: does a second pack REUSE
.meteor-spk/bundle or rebuild it and undo the prune? Both answers are
useful and neither is worse than the hard failure we have now - if it
rebuilds, the second failure is identical and the log says the prune was
undone; if it reuses, the .spk packs. It runs once, only after a failure,
and only if the bundle is actually there.

Exercised both paths against a mock pack: the failure path reports sizes,
prunes, retries and still exits 1 with the reason; the success path packs
and reports as before.

Thanks to xet7 !
2026-08-16 02:12:16 +03:00
github-actions[bot] a69b2f0064 Bump versions for v10.94 2026-08-15 22:21:43 +00:00
Lauri Ojansivu d60d178a05 Prepare v10.94 release 2026-08-16 01:20:39 +03:00
Lauri Ojansivu c5d80ef990 CHANGELOG: an Upcoming section for the v10.93 build fixes.
v10.93 published, so its section stays as the record of what shipped, and
the two build fixes made since go in a new Upcoming above it - which is
what CLAUDE.md now says to do when a release ships with something broken.

Two entries: the Windows one, which is a fixed code fault, and the
Sandstorm one, which is a fixed BLINDNESS - the size limit still stands,
but the pack step now names what filled the gigabyte instead of only
refusing.

The binaries table carries the four platforms this run recorded a complete
provenance for - amd64, arm64 and both macs, with the Node.js and FerretDB
each build verified - and says so, rather than carrying the previous
release's table forward. That habit is why v10.93's table still lists
FerretDB v1.49.0 while every job in that run resolved v1.53.0: the table
is hand-carried between releases and drifts, while the release notes are
generated from provenance.tsv and do not. The release job regenerates this
one the same way.

Thanks to xet7 !
2026-08-16 01:00:16 +03:00
Lauri Ojansivu be0965611e TODO Later: the two v10.93 build failures a reading of the tree cannot answer.
Both from the job logs, both left with the finding rather than a guess.

build-sandstorm hit Sandstorm's 1 GiB uncompressed limit, which means no
.spk is written and nothing says what is big. Recorded with what DOES count
towards that limit - sandstorm-pkgdef.capnp packs alwaysInclude = ["."]
from .meteor-spk/deps and .meteor-spk/bundle, so the assembled Node 24,
FerretDB, mongod 3.0, niscud and Mongo CLIs count beside every package in
the bundle - and with the two candidates worth measuring first: the
Sandstorm leg is the only one that never runs prune-build-only-modules.mjs,
which removes 83 of 120 packages everywhere else, and the deps tree carries
two database engines because the migration needs both. The pack step now
prints the sizes before packing, so the next run names the offender.

snap-launchpad riscv64 was cancelled after printing "Pending: riscv64"
until the job timed out. s390x, ppc64el and armhf all built and published
in the same run, so this is Launchpad queue time, not the recipe.

Thanks to xet7 !
2026-08-16 00:55:58 +03:00
Lauri Ojansivu 21363baf6d Windows builds died on spawnSync npm ENOENT, after all the work was done.
v10.93: build-win64 and build-win-arm64 both failed, and both had already
built the bundle and compiled its native modules. What killed them was one
line of bump-bundle-npm-deps.mjs:

  Error: spawnSync npm ENOENT

npm on Windows is npm.cmd, a batch script, and Node applies no PATHEXT when
it spawns - so execFileSync('npm', ...) resolves to nothing. build-win32 was
skipped that run for want of a published Node.js, so it never reached this
and looked fine; the fault was never architecture-specific.

npm's own CLI is run with the Node already running instead:
execFileSync(process.execPath, [npm-cli.js, ...]). No PATH lookup, no
PATHEXT, no shell - and the same npm either way. shell: true would have
found the .cmd and broken differently, because with a shell Node joins the
arguments and quotes NOTHING, so the first Windows temp path with a space
would corrupt the install. A bare npm on PATH remains as the last resort,
for a Node with no npm beside it, and on Windows it now says which case
that is instead of letting ENOENT speak for itself.

Verified end to end: a fake bundle holding qs 6.0.0 is bumped to 6.15.3
through the new path, with its new dependencies copied in beside it.

Also, build-sandstorm failed with "App exceeds uncompressed size limit of
1 GiB" and nothing else - no list, no sizes, and the .spk that would have
shown them was never written. sandstorm-pkgdef.capnp packs alwaysInclude =
["."] from .meteor-spk/deps and .meteor-spk/bundle, so everything in both
trees counts. The step now prints the total and the twenty biggest
directories of each BEFORE packing, so the next run says what to trim
rather than only that trimming is needed. Trimming it needs a run to
verify, so it is recorded rather than guessed at.

Thanks to xet7 !
2026-08-16 00:55:58 +03:00
dependabot[bot] f76e560e91 Bump @aws-sdk/lib-storage from 3.1104.0 to 3.1109.0
Bumps [@aws-sdk/lib-storage](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/lib/lib-storage) from 3.1104.0 to 3.1109.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/lib/lib-storage/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1109.0/lib/lib-storage)

---
updated-dependencies:
- dependency-name: "@aws-sdk/lib-storage"
  dependency-version: 3.1109.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-15 20:33:28 +00:00
github-actions[bot] fe65bb658d Bump versions for v10.93 2026-08-15 20:14:00 +00:00
Lauri Ojansivu 94b1bc60e5 Prepare v10.93 release 2026-08-15 23:12:50 +03:00
Lauri Ojansivu dd588eac63 The Docker fix moves to a new Upcoming, and CLAUDE.md says which case is which.
v10.92 PUBLISHED, and Docker did not work in it. A released section is a
record, not a draft, so v10.92 keeps everything it shipped with - including
the release-notes fix, which is what let it publish at all - and the
waiting-page fix moves to a fresh '# Upcoming WeKan (R) release' above it.
release-all.sh will rename that to v10.93 on its own.

CLAUDE.md described the happy path correctly and said nothing about the two
ways a release goes wrong, both of which happened with v10.92 within an
hour of each other, and which need OPPOSITE handling:

  * nothing published - there is no release for that number, so the heading
    goes back to Upcoming and the same number is used again;
  * published but broken - the section stays, and the fix goes in a NEW
    Upcoming.

What decides it is whether the GitHub Release exists, not whether the tag
does: prepare pushes the tag early, so v10.92 had a tag for hours while
nothing was published under it. That is written down now, because guessing
it wrong either erases a release people downloaded or claims one that never
existed.

Also made the workflow description exact while there: bump bumps the
versions and rebuilds the API docs, prepare pushes the tag and checks the
notes, and release publishes - it said 'the release jobs tag v<new>', which
is the wrong job and the wrong order.

Thanks to xet7 !
2026-08-15 23:11:45 +03:00
Lauri Ojansivu 53d64f2e77 CHANGELOG: the waiting page that never left goes under Upcoming.
Beside the entry that ADDED the page, in the same Starting up group, since
the two are one story: the page is new in this unreleased version, 10.91
had none, and the page is what broke Docker for the reporter.

Says what it was (the probe could not resolve a driver, so it never asked
the database anything), why the snap was fine (it never runs db-ready.mjs),
what else was found while proving it (the forced directConnection, and the
silence), and that WEKAN_DB_WAIT_PAGE=false still turns it all off.

Thanks to Alishara and xet7 !
2026-08-15 23:01:02 +03:00
Lauri Ojansivu df41f61c26 The waiting page hid a healthy WeKan: the probe never found a driver to ask with.
The login page did not load. The container logged "The database is not
answering yet" and served the waiting page, while mongosh on the host
talked to the same MongoDB without complaint - a Meteor 3 production setup
from docs/Platforms/FOSS/Container/Docker/Meteor3, MongoDB 7.0.40, rs0,
network_mode host, MONGO_URL a single host with authSource.

The database was never the problem. db-ready.mjs asked for the driver at
programs/server/node_modules only - and mongodb is a devDependency, not a
dependency, so a production bundle has NOTHING there. Meteor's own driver
lives under programs/server/npm/node_modules/meteor/npm-mongo. require
threw MODULE_NOT_FOUND on every ask, in every container, whatever the
database was doing; the catch turned that into "not ready", the entrypoint
sent the reason to /dev/null, and WeKan sat behind the page for the whole
ten-minute window before starting.

The snap already carries this scar. Its db-eval.mjs says so in a comment -
"made WeKan loop 'MongoDB not ready' forever" - and resolves the driver
from a list of bundle paths. db-ready.mjs now uses the same list. That is
also why the snap was unaffected: it never runs db-ready.mjs at all.

And no driver no longer means a page. It exits 2, distinct from 1, and the
entrypoint starts WeKan without the page: "I could not ask" is not evidence
that anything is wrong, and a page shown on that basis hides a WeKan that
would have served fine. Docker with an external MongoDB needs no waiting
screen, and now it does not get one it has not earned.

Reproduced against a bundle-shaped tree with the driver only under
npm-mongo: the old probe exits 1 and prints nothing, the new one resolves
the driver and reports what the database actually said.

Thanks to Alishara and xet7 !
2026-08-15 22:58:57 +03:00
Lauri Ojansivu 9f5ea0f498 The waiting-for-database page never left, at a database that was answering.
Reported with a screenshot: the container logs "The database is not
answering yet" and serves the waiting page forever, while mongosh on the
host connects to the same MongoDB - replica set rs0, [direct: primary],
7.0.40 - without complaint.

The probe was wrong, not the database. db-ready.mjs forced
directConnection: true. For one host that is harmless; a replica set is
normally a SEED LIST, and the driver refuses that combination outright:

  MongoParseError: directConnection option requires exactly one host

The throw happened while the client was being CONSTRUCTED, which was
OUTSIDE the try, so the probe died with an unhandled error on every ask.
Its exit code said "not ready", the entrypoint sent its stderr to
/dev/null, and the page stayed up with nothing anywhere saying why.

The options now come from the URL and nowhere else. That fixes the crash
and is the more correct question besides: WeKan connects with the URL as
written, so a probe that quietly connects DIFFERENTLY can report ready for
a database WeKan cannot reach - dropping the page and leaving the web port
closed, which is the exact fault this feature exists to prevent. A URL the
driver refuses is now reported rather than thrown: waiting will never fix
a malformed MONGO_URL, and saying so beats a page that spins for ten
minutes.

The reason is printed once, and put ON THE PAGE. Whoever is waiting is
looking at a browser, not at docker logs, and
"MongoServerSelectionError: connect ECONNREFUSED wekan-db:27017" names the
host that could not be reached. The first probe reports; the three-second
poll stays quiet; the last one reports again if the whole window expires.

Only the container is affected, which is why the snap was fine: the snap
serves its own page from wekan-control and never runs db-ready.mjs, and
its MONGO_URL is always a single local endpoint - a unix socket or
127.0.0.1:27019 - so it never had a seed list for the forced option to
collide with. The shape that breaks only occurs when MONGO_URL points at a
real MongoDB replica set, which is the Docker case.

Thanks to Alishara and xet7 !
2026-08-15 22:53:39 +03:00
Lauri Ojansivu 0e4177bb31 Prepare v10.92 release 2026-08-15 21:46:19 +03:00
Lauri Ojansivu 8cda7007e9 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-15 21:46:02 +03:00
Lauri Ojansivu 9d020e9333 CHANGELOG: v10.92 never published, so its section is Upcoming again.
The release job failed, and with it every job that needs it - snap, docker,
AppImage, the Windows and macOS bundles - so nothing was published under
that tag at all. A "# v10.92" section describes a release a reader can
download, and there is none, so the heading goes back to Upcoming and the
whole of what was written for v10.92 ships in the next release instead.
That is exactly the rename "Prepare v10.92 release" made, undone.

The workflow fix joins the release-workflow group under the bug fixes,
where the v10.91 snap-attachment fix already is: the notes outgrew the
128 KiB a single environment variable may hold, so execve refused to start
bash before a line of the step ran, and they go from file to file now.

The In short paragraph leads with it, because "this was tagged v10.92 and
never published" is the first thing a reader of this section needs.

Two rows of the binaries table were wrong and are corrected from the build
logs: amd64 and arm64 embed FerretDB v1.53.0, not v1.49.0, with the SHA256
each build verified. Those are the only two platforms that got as far as
building, so they are the only two there is evidence for; the rest keep
what the previous release recorded and refresh from provenance.tsv on the
next successful build.

Thanks to xet7 !
2026-08-15 21:46:00 +03:00
Lauri Ojansivu ac02941663 v10.92 could not start bash: the release notes outgrew an environment variable.
The release job failed before running a line of its script:

  ##[error]An error occurred trying to start process /usr/bin/bash with
  working directory /home/runner/work/wekan/wekan. Argument list too long

Nothing in the step was wrong. The notes are the whole newest CHANGELOG
section - prepare measured them at 172,458 characters - and they reached
the step as env: CHANGELOG. Linux caps a SINGLE argv/envp string at
MAX_ARG_STRLEN, 128 KiB, so execve refused to start the shell at all. Both
bundles had built and verified; the release, and every job that needs it -
snap, docker, AppImage, the Windows and macOS bundles - never happened.

The environment was itself the fix for the previous failure: interpolated
inline as ${{ }} the notes become part of the shell SOURCE, so a backtick
in any `code` span runs as a command, which is how v10.59 published nothing
and printed "Incorrect: command not found". One shape is unsafe, the other
does not scale, and this release was big enough to find the second wall.

So the notes stop being a shell value at all. releases/release-notes.sh
prints the CHANGELOG section for a version, reading the file itself and
taking its arguments from the environment inside a QUOTED heredoc; the jobs
append its stdout to release-notes.md. Only file paths are passed around,
which has no size limit and leaves the text as data no shell ever parses.

prepare keeps the validation - a missing section is still cheap to fix
there - but no longer publishes the text as a job output, because an output
nobody can safely consume is a trap for the next person. The release job
and the notes-rewrite job check out ref: main, the same ref prepare read.

The guard test is rewritten around the new shape: no changelog job output,
no CHANGELOG in an env:, no interpolation into a script, and the extraction
script is what all three consumers run. It kept passing through this
failure, because it only knew about the injection.

Thanks to xet7 !
2026-08-15 21:46:00 +03:00
github-actions[bot] 24973ea98b Bump versions for v10.92 2026-08-15 18:13:13 +00:00
Lauri Ojansivu 9bf592a5f8 Prepare v10.92 release 2026-08-15 21:12:08 +03:00
Lauri Ojansivu 7ecce0502f TODO Later says where the translation work stands, and a command that says it.
The entry had grown by accretion into five overlapping bullets, and every
number in it was a number in a changelog - true on the day it was written
and stale by the next commit. It is rewritten as something a later session
can resume from, and it opens with the command rather than the figures:

  node releases/translations/fill-translations.mjs --status

--status is new. A flat count of "strings still equal to the English
source" is several times the size of the backlog and says nothing about
where the work is, so it splits the count two ways. What the string IS: a
product name, a bare number, a symbol or a bare __placeholder__ has no
translation to give and will never stop counting. And WHICH SCRIPT the
language uses: in a Latin-script language an untranslated Status reads as a
word, and in a Greek, Arabic, Thai or Devanagari interface it is a foreign
alphabet mid-sentence. It then ranks the remaining keys by how many files
share each, which is the work order - one key missing in fifty files is one
table, not fifty visits.

Today that reads 99 words left in the 56 non-Latin near-complete files,
1,371 in the 86 Latin ones, and 193,247 in the 92 second-tier ones.

The entry keeps what a reader needs and drops what repeats: what is DONE
(both wrong-language axes, with the guard command), what is left in the
order worth doing it, the two rules that must not bend (translate directly,
never overwrite a human translation), and three things that bit and will
again - files written in a language other than their name, operator names
that cannot contain a space, and a reworded English source leaving stale
English that stops being offered.

Thanks to xet7 !
2026-08-15 21:05:14 +03:00
Lauri Ojansivu d1bd063a2c CHANGELOG: Upcoming had drifted from four rules CLAUDE.md sets.
Checked the whole section against CLAUDE.md rather than against the guard
suite, which only pins what can be pinned mechanically. Four things were
wrong, and none of them failed a test.

SUBSECTION ORDER. Developer-facing changes sat between the new features
and the bug fixes. CLAUDE.md puts it after the bug fixes, before the
documentation and the translations, so the release reads in the order a
reader cares about it.

GROUPING. Eight translation entries were under the group that happened to
be above them rather than the one they belong to: the four wrong-language
fixes had landed under "Panels added since a file was last touched", and
the four by-key fills under "The search operators". The wrong-language
four move up to "Files written in another language", and the by-key four
get a group of their own, since filling one key across a hundred files is
a different job from translating one file.

THE CLOSING LINE was missing outright. Upcoming now ends with the sentence
CLAUDE.md prescribes word for word.

THE IN SHORT PARAGRAPH was stale - it still said four files in the wrong
language and 3,984 strings, which was true for about a day. It now says
what the release actually amounts to: 8,716 values in the wrong language
found by two checks neither of which existed before, the search operators
and their shorthands, and the panels a file never had. It also loses a
one-word line left over from a bad wrap.

Also trimmed one summary from 111 characters to fit the 110 CLAUDE.md aims
for.

Thanks to xet7 !
2026-08-15 20:59:58 +03:00
Lauri Ojansivu dc1b8c3824 CHANGELOG: the import dialogue everywhere, and what the refusals measured.
Two entries, and a second measurement bullet in TODO Later beside the
by-script one. The 86 Latin-script near-complete files hold 1,371 real
words against 3,981 product names - the same split, one script over.

The way to find out how small the remainder really is turned out to be to
OFFER a key its translation and count the refusals: twenty everyday words
were filled 48 times and ignored 202, because Filter, Container, Version,
Pause, Type and Latitude are already those languages' own words. That
number is the reason the backlog reads several times larger than it is,
and it is worth writing down so the next person does not rediscover it.

Thanks to xet7 !
2026-08-15 20:52:27 +03:00
Lauri Ojansivu 8e62dd157a Twenty everyday words, and the 202 that were already right.
The next twenty keys by how many files share them - Dialog, Container,
Tests, Filter, Version, Pause, Repository, Detail, Type, Color, Error,
Roles, Latitude, Longitude, orange, Admin - were offered to the 56
Latin-script files that still had them in English. 48 were filled and 202
were IGNORED, because the value offered was the English word and the
English word is what that language uses: Filter is Filter in German, Dutch,
Danish, Frisian and Estonian; Container, Tests, Version, Pause, Repository,
Detail, Type and Latitude are themselves across most of western Europe.

What did change is where a language really has its own: Spanish Funciones
for roles, Administrator and Administrateur and Frisian Behearder for
admin, Slovak Podrobnosť for detail and Dialóg for a dialog, Italian
Contenitore and Finestra di dialogo, Vietnamese Vùng chứa, Portuguese
Pausa, and Yoruba Ìwọ̀n ìhà and Ìwọ̀n gígùn for latitude and longitude.

The 202 are the answer worth recording. They are counted as untranslated
only because the tool's test for it is "still equal to English", and no
amount of work will ever reduce them - which is why the backlog number is
four times the size of the backlog.

Thanks to xet7 !
2026-08-15 20:50:21 +03:00
Lauri Ojansivu e53e895f6b The import-mapping dialogue, in the other 85 files.
Finishes the pair the non-Latin files got two commits ago: 170 values
across the Latin-script languages, from Afrikaans to Zulu. These are the
two longest strings in the file - a paragraph explaining that mapping an
imported member to a real user moves that member's cards, comments and
activity, and that the user is added with the imported member's OWN role,
so mapping can never grant more permission than the import did. That last
clause is the one worth getting right in every language, because it is the
answer to "what am I about to give this person".

Seven files are written in a language other than the one their name claims
and get that one: ace is Malay, ast-ES Spanish, ro Italian, ve Zulu,
vl-SS Dutch, and vo, wo, zgh and wa-RR are French.

tlh is deliberately left in English. Klingon has words for a board, a card
and a rank, and this paragraph needs none of those - it needs three
subordinate clauses about permission, and inventing them would produce
something that reads as though somebody meant it. An English placeholder
says plainly that nobody has translated it yet, which is the more useful
thing for the one reader who could.

Thanks to xet7 !
2026-08-15 20:47:38 +03:00
Lauri Ojansivu c4799099d6 CHANGELOG: the operator words, the loanword split and the import dialogue.
Three entries for this round of the prose backlog, and a new first bullet
in TODO Later: read the count BY SCRIPT. An untranslated string in a
Latin-script language is a word; in a Greek, Arabic, Thai or Devanagari
interface it is a different alphabet mid-sentence. By that measure the 52
non-Latin files hold 133 real words against 1,927 product names, and 71 of
the 133 are the two act-with*Title keys, which are pure __board__
placeholders with no translation to give. That half is effectively done,
which is the thing the flat number never said.

Thanks to xet7 !
2026-08-15 20:16:53 +03:00
Lauri Ojansivu 4a950b80bb The import-mapping dialogue, in the 48 files where English is a foreign alphabet.
An untranslated string is not equally visible everywhere. In a Latin-script
language "Status" reads as a word; in a Greek, Arabic, Thai or Devanagari
interface an English paragraph is a different alphabet in the middle of the
page. Measured that way, the non-Latin files are nearly done: 235 real
words still in English across 52 of them, and 102 of those were these two
keys.

They are the strings the import dialogue shows when it asks which real user
an imported member is - the pair whose English source was reworded, so
every file kept the old English and the fill tooling stopped offering them.
96 values here, in 24 languages: Arabic, Belarusian, Bulgarian, Greek,
Persian, Gujarati, Hebrew, Hindi, Armenian, Japanese, Georgian, Khmer,
Korean, Macedonian, Mongolian, Odia, Punjabi, Russian, Serbian, Tamil,
Telugu, Thai, Uyghur, Ukrainian, Yiddish and Chinese in both scripts.

zh-TW and uz-AR already had human translations and kept them.

Serbian gets the case-file vocabulary the rest of that file uses: a board
is "списи" and a member a "сарадник", so the sentence reads as the same
document the rest of the interface describes.

Thanks to xet7 !
2026-08-15 20:14:29 +03:00
Lauri Ojansivu da8d02315a Statistics, package and the region names, where the word is not the English one.
128 values across 67 files, chosen by asking of each key whether the
language actually has a different word - because most of what the count
calls a prose backlog is not one. Status is Status in Danish, German,
Dutch, Swedish and Polish; Server is Server almost everywhere; Normal,
Ticket, Menu and Logo are themselves in most of Europe. Those are correct
as they stand and are left alone.

What is filled is where the English word is visibly foreign or the language
plainly has its own: the non-Latin scripts (Κάδος and Στατιστικά in Greek,
דלי in Hebrew, سطل in Persian, Кофа in Bulgarian, Корпа in Serbian,
كنجب in Khmer, Багц in Mongolian, and America in seven Indic and East Asian
files), the African languages that had been given English (IMelika,
Umlawuli, Iphakethe, Iphakheji, Ngwugwu, Idi, Marekani), and Statistics in
the twelve European languages that had not translated it.

uz-AR already had human translations for the two it was offered and kept
them, which is the fill rule rather than a special case.

Thanks to xet7 !
2026-08-15 20:07:51 +03:00
Lauri Ojansivu d62c15aedc The rest of the search operators, in the 107 files that had done the others.
A file that translated board:, list: and member: and left org:, title: and
customfield: in English is inconsistent with itself: half the search syntax
is in the reader's language and half is not, and nothing on screen says
which half is which. 217 operator words are filled here, only in files that
had already translated at least two of the core three - so this never
invents a search vocabulary for a language that has not chosen one.

An operator NAME is matched before the colon, so it can never contain a
space. Languages that write these as two words run them together, the way
Greek already writes προσαρμοσμένοπεδίο: Slovak vlastnepole, Hungarian
egyenimezo, Thai and Vietnamese likewise.

72 are deliberately left as they are. status, limit, team, selector,
projection, description, week and open are those languages' OWN words in
Dutch, Swedish, Spanish, French, Catalan, Czech, Polish, Turkish and Malay,
and the fill step ignores a value equal to the English source rather than
pretending a translation happened.

Five of the files are written in a different language from their name and
get that one: ace is Malay, ast-ES Spanish, ro Italian, ve Zulu, and vo, wo
and zgh are French.

Thanks to xet7 !
2026-08-15 20:04:25 +03:00
Lauri Ojansivu 0ced10941c CHANGELOG: the Latin-only detector and its four files go under Upcoming.
Four entries: the detector itself and the stale-English reset beside it,
the small files it caught first (ka Turkish, ar-DZ French, mn), Thai's 683
Vietnamese values, and Greek's 924 Italian ones.

TODO Later stops describing the wrong-language problem as four files in
another script. It was two problems: 5,542 values in another SCRIPT and
3,174 in the LATIN alphabet inside a language not written in it, which no
Unicode-block comparison can see. Both are at zero, and the paragraph says
which of the languages were done by looking the words up rather than by a
speaker, since that is what a reviewer needs to know first.

Thanks to xet7 !
2026-08-15 19:44:38 +03:00
Lauri Ojansivu 8df7376600 Greek: the last 459, and the Latin-only backlog is empty.
Finishes el and el-GR - global search with its whole instruction text, the
board and card reports, the wait spinners, the attachment storage, every
board and database migration with its confirmation dialogue, the
brute-force lockout settings, and the login and repository pages.

The search operators are Greek words, as they are now in Hindi, Tamil,
Georgian, Korean and Thai, because they are what a user TYPES: πίνακας for
a board, λίστα for a list, προθεσμία for due - and the instruction text
names those same words through its __operator_*__ placeholders.

That empties the Latin-only backlog the detector found three commits ago:
3,174 values across ka, ar-DZ, mn, th, el, el-GR and a tail of small ones,
all done. wrong-script.mjs --count reports zero for both of its checks now
- no value in another SCRIPT, and none in the Latin alphabet inside a
language that is not written in it.

Greek was written by looking the words up rather than by a native speaker;
a review is welcome and replaces these strings permanently.

Thanks to xet7 !
2026-08-15 19:42:32 +03:00
Lauri Ojansivu 186ca9ebaf Greek: 465 of 924 values that were Italian.
el and el-GR are the same file twice, and 924 of their values were written
in ITALIAN - lista rinominata in, si e unito a, Bacheca for a board, Lista
for a list, Couloir (French, for good measure) for a swimlane. Latin script
in a Greek file, invisible to a Unicode-block comparison, and 926 of them
sat behind a green count.

465 are Greek here, in both files: the activity feed, the workspace and
board menus, the archive dialogues, Planning Poker, the export fields, the
shortcuts, the rules engine and the Admin Panel.

The terms follow what the file already had right - Πίνακας, Κάρτα, Λίστα,
Ετικέτα, Μέλος, Λίστα ελέγχου, Αρχείο, Προσαρμοσμένα Πεδία - and fill the
two it did not: Διάδρομος for a swimlane and Υπεύθυνος for an assignee.

459 left.

Thanks to xet7 !
2026-08-15 19:36:52 +03:00
Lauri Ojansivu 482065d8fb Thai: 683 values that were Vietnamese.
A third of th.i18n.json was written in VIETNAMESE - Bang for a board, The
for a card, Danh sach for a list, Lan ngang for a swimlane - beside 1,594
values of real Thai. Latin script in a Thai file, so the Unicode-block scan
never saw it; the Latin-only detector added in the previous commit did.

All 683 are Thai now: the activity feed, the rules engine, the card
details, the filters, the Admin Panel, and the whole of global search. The
terms are fixed once and used throughout - บอร์ด for a board, การ์ด for a
card, รายการ for a list, สวิมเลน for a swimlane, เช็คลิสต์ for a
checklist, คลังเก็บ for the archive.

One value was not a translation at all: act-withCardTitle read
__kartu__[__Panel__], two Indonesian words in placeholder syntax, so the
notification it formats could never substitute a board or a card. It is
[__board__] __card__ again.

Thai was written by looking the words up rather than by a native speaker,
so a review is welcome - and it replaces these strings permanently when it
arrives on Transifex.

Thanks to xet7 !
2026-08-15 19:32:04 +03:00
Lauri Ojansivu b65518dca9 Georgian was Turkish, Algerian Arabic was French, Mongolian was English.
The Latin-only detector's first harvest, smallest first. ka held 43 values
of TURKISH (Klavye kisayollari etkin), ar-DZ held 508 of FRENCH, and mn
held 44 that were never translated at all but had drifted far enough from
the English source that the fill tooling no longer offered them. Plus the
tail: six in ug, two in fa, one each in or_IN, pa and uk.

ar-DZ is fixed from ar, key by key and only where ar has a translation -
the four search operators ar itself lacked are written for both, so the
variant is never ahead of its base.

The script map also grew the languages it never listed: ary, the nine
Turkic and Mongolic languages written in Cyrillic (ba, bua, cv, kk, ky, mn,
sah, tg, tt), and Chinese under every tag it ships as. Of those only mn had
anything to report, which is the answer worth having.

Left: el and el-GR hold 924 values of Italian each and th holds 683 of
Vietnamese. Those are the next backlog.

Thanks to xet7 !
2026-08-15 19:23:39 +03:00
Lauri Ojansivu ae3686d67c Latin in a non-Latin file, and English that stopped looking untranslated.
Two blind spots, both found by asking the scan a question it could not
answer before.

FIRST: a value written entirely in the LATIN alphabet inside a language
that is not written in Latin. The Unicode-block comparison could never see
it, because Latin is not one of the blocks it compares against - so el and
el-GR hold 926 values of ITALIAN each, th holds 685 of VIETNAMESE, ar-DZ
holds 510 of FRENCH and ka 45 of TURKISH, and the count read zero
throughout. wrong-script.mjs now flags them as latin:N. Product names are
Latin too, so a value is only suspect when it says something: five letters
or more, and not the English source wearing different punctuation.

SECOND: the two keys map-to-existing-user-desc and -none were REWORDED in
en.i18n.json, and every other file kept the old English. Old English is not
equal to the new source, so the fill tooling counted it as a translation and
stopped offering the key - English that has stopped looking untranslated,
in 145 files. The current source is written back, which changes nothing a
reader sees and makes 290 values countable again.

The 3,096 latin:N values are NOT fixed here; they are the next backlog and
are recorded as such.

Thanks to xet7 !
2026-08-15 19:18:57 +03:00
Lauri Ojansivu fc96d57bf2 CHANGELOG: the scan's blind spots and the search shorthands go under Upcoming.
Two more entries under the wrong-language group - the base-tag-only scan
that never looked at hi-IN or ko-KR, and the CJK exclusion that was wide
enough to pass 80 non-Korean values per Korean file - and a new group for
the 277 search shorthands derived from each language's own operator word.

The shorthand entry keeps the three orderings that had to be right, because
each of them silently produced something worse than what it replaced and
the next person to touch that derivation will hit them again.

Thanks to xet7 !
2026-08-15 19:04:51 +03:00
Lauri Ojansivu 774d7a032b Search shorthands are the language's own letter, in 89 files.
Each search operator has a one-letter shorthand beside it, and the
shorthand is the first letter of THAT LANGUAGE'S word - French couloir is
c, Russian доска is д, Georgian დაფა is დ. A file that translated the word
but kept the English letter contradicts itself: board: works, and b: stands
for nothing the reader can see.

277 shorthands, derived rather than guessed: the first grapheme of the
file's own word, extended while it collides with another shorthand in the
same file, and written only when it differs from the English letter -
Portuguese lista and German Liste both start with an l, so l is already
correct and is left alone. Every file ends with five distinct shorthands;
Welsh aelod and aseinai are a and as, Hungarian Tábla and tag are t and ta.

Three orderings had to be right or the result was worse than before it:
case-fold before the collision test (Tábla and tag are both T), seed the
taken set from the shorthands that are NOT being changed (Frisian lijst
kept l while lid was given l), and test the English letter AFTER the
collision loop rather than before it (or Welsh keeps a twice on the grounds
that a is what English uses).

Skipped where the operator WORD is itself in another language, because a
shorthand derived from it would carry that one step further and the word is
what wants fixing: tlh is German, th is Vietnamese, br is French, ve-PP is
Finnish, and one key each in mn, sk, lv, vo and zgh. Latin-script
contamination like that is invisible to wrong-script.mjs, which compares
Unicode blocks.

Thanks to xet7 !
2026-08-15 19:02:47 +03:00
Lauri Ojansivu 4be57c2ad5 Korean: 80 values per file that were Chinese or Japanese, not hanja.
The scan excluded CJK entirely for a Korean file, on the correct grounds
that hanja beside hangul is legitimate Korean. That exclusion was too wide:
it also passed a value with NO hangul in it at all, which is not Korean
under any reading - it is whatever the file was seeded from. 看板 for a
board, 拡大 and 縮小 for zoom, 担当者 for an assignee, 賛成 and 反対 for
the two sides of a vote, and the three date formats written 年-月-日.

The rule is now the narrower one it should always have been: hanja is fine
WITH hangul, and a hangul-script value that contains CJK and no hangul is
flagged. That is 80 values in ko and the same 80 in ko-KR, all translated
here, including the search operators a user types - 담당자, 마감, 조직.

Thanks to xet7 !
2026-08-15 19:01:09 +03:00
Lauri Ojansivu fb969b2745 The scan said zero and meant zero of the tags it happened to name.
wrong-script.mjs listed the script for a base tag only - hi, ko, ja, el,
ru - so hi-IN and ko-KR were never checked at all. Both are full copies of
what hi and ko used to be: hi-IN held 1,204 values in Gujarati and ko-KR
held 354 in Japanese kana, and the count reported zero the whole time the
two of them were the worst files in the tree.

Every file whose tag reduces to a known base is now checked under that
base's script, so a variant can never hide behind its parent again. The two
that surfaced are fixed from their corrected base, key by key, and only
where the base actually has a translation - the three values the base still
carries in English (ldap, excel-font, gridfs-storage) stay in Latin because
that is what they are.

node releases/translations/wrong-script.mjs --count reports zero again, and
this time it has looked at all 246 files.

Thanks to xet7 !
2026-08-15 18:58:32 +03:00
Lauri Ojansivu 6f49798840 CHANGELOG: the Home page, Board status and Board roles fills go under Upcoming.
A new group, "Panels added since a file was last touched", for the two
batches: 197 values for the All Boards Home and starred pages across 33
languages, and 307 for the board-status summary and the roles-status table
across 47 files. Both say which files are written in a different language
from their name and are completed in THAT one, and which are low
confidence and want a speaker.

TODO Later loses 505 from its count and gains what those two batches
taught: much of what looked like a prose backlog is loanwords. magenta and
indigo are magenta and indigo in nearly every language that "misses" them,
and so are Format, Ticket, Normal, Admin and Status in most European ones,
so the real remainder is nearer 2,200 than 3,400 - and the way to work it
is by KEY, since one key is missing in ten to a hundred files at once.

Thanks to xet7 !
2026-08-15 18:36:08 +03:00
Lauri Ojansivu 330b642190 Board status and Board roles, in the 47 files that still showed English.
Two panels added since those files were last touched: the board-status
summary (card loading, time spent, total, cards with time, overtime cards)
and the roles-status table (what each board role may do, and its Invite /
Sees cards / Create and edit / Board settings columns). 307 values.

Five of the files are not written in the language their name claims, and
are completed in the language they are ACTUALLY in rather than left half
English: ro is Italian, ast-ES is Spanish, ve is Zulu, wo is French, and
sr uses case-file vocabulary throughout - a board there is "Списи", so its
board status is "Стање списа" and not a literal translation of the English.

Two are low confidence and want a speaker: Klingon, where the boQwI"
lexicon has "patlh" for a rank but no idiom for a board role, and Volapuek.

Roles-status-role is deliberately left as-is in Czech, Spanish and Walloon:
"Role" and "Rol" are those languages" own words, and the fill step ignores
a value equal to the English source rather than pretending otherwise.

Thanks to xet7 !
2026-08-15 18:33:22 +03:00
Lauri Ojansivu 3eb1631d54 The Home page and starred pages, in the 33 languages that still showed English.
The All Boards Home strings - Remove from Home, its confirmation, Click to
star this page, Click to unstar this page, Starred Pages, and the empty
Home hint - were the newest keys in the file and had reached only the
languages that had been touched since. 197 values are filled here across
33 files.

Four of those files are not written in the language their name claims, and
are completed in the language they are ACTUALLY in, as CLAUDE.md requires
rather than leaving half of each in English: ace is Malay, ast-ES is
Spanish, ve is Zulu, and every uz variant including uz-AR is Latin Uzbek.

km_KH already had human translations for all six and kept them - the fill
step writes only where the value is still the English source, which is why
that file reports "skipped 6" rather than being overwritten, and sr kept
the one it had.

Thanks to xet7 !
2026-08-15 18:27:05 +03:00
Lauri Ojansivu 86f3785c3c CHANGELOG: the four files written in another language go under Upcoming.
Korean, Georgian, Hindi and Tamil held 3,984 values in a script the reader
of that language may not read - kana, Cyrillic, Gujarati and Telugu. Two
entries under a new "Files written in another language" group say what was
wrong, why nobody had noticed (each file was mixed, with save right and
board wrong), what the search-operator decision was, and which three
values stay in Latin because they are a font family, a protocol and a
product name.

TODO Later loses the table that listed them as outstanding: they are done,
and what remains there is the prose backlog and the 92 second-tier
languages. The In short paragraph names them too, since it is the first
thing a reader of the release sees.

Thanks to xet7 !
2026-08-15 18:20:05 +03:00
Lauri Ojansivu 7a3cd208c7 Tamil: the last 517 foreign-script strings are Tamil now, and no file is left.
Finishes ta.i18n.json - the global search instructions, the reports and
wait spinners, the whole attachment storage and migration machinery, the
brute-force lockout settings, and the login and repository pages. Two
values stay in Latin because they are not words: excel-font is the font
family handed to the spreadsheet writer, and gridfs-storage is a product
name; the Telugu transliterations of both were unusable either way.

That closes the whole wrong-script backlog. Every one of the 246 language
files now holds text in the language it claims - ko, ka, hi and ta were
the four seeded from another language, and all four are done. The scan
that found them is still there:

  node releases/translations/wrong-script.mjs --count

now reports zero, and it is the guard that catches the next file seeded
from a neighbour before anyone ships it.

hi and ta were translated by looking the words up rather than by native
speakers. A speaker of either who reviews them is welcome, and the pull
merge guarantees their Transifex translation replaces these strings and is
never reverted.

Thanks to xet7 !
2026-08-15 18:15:48 +03:00
Lauri Ojansivu b6c4fc64f8 Tamil: 1,120 of 1,637 done - the rules, the settings and the search.
Continues the pass through ta.i18n.json's Telugu- and Hindi-script values:
the whole rules engine with its triggers and actions, the Admin Panel's
authentication and custom-HTML settings, the due-date reminders, the
notification centre, My Cards and Due Cards, and all of global search.

The search operators are Tamil words, as they are now in Hindi and
Georgian, because they are what a user types: `பலகை` for board, `பட்டியல்`
for list, `கெடு` for due - and the instruction text names those same words
through its __operator_*__ placeholders, so the two can never drift apart.

Thanks to xet7 !
2026-08-15 18:07:28 +03:00
Lauri Ojansivu f0eff59b3c Tamil: the first 560 of 1,637 foreign-script strings are Tamil now.
ta.i18n.json was seeded from Telugu and Hindi, so 1,637 of its values are
written in a script a Tamil reader does not read. The first 560 are
translated here: the whole activity feed, the workspace and board menus,
the archive, the card details with its voting and planning poker, the
custom fields, the account emails, the export and import dialogues, and
every filter.

The terms are fixed once and used throughout, which is what makes the rest
mechanical: பலகை for board, அட்டை for card, பட்டியல் for list,
நீச்சல்பாதை for swimlane, சரிபார்ப்புப் பட்டியல் for checklist,
காப்பகம் for archive, தனிப்பயன் புலம் for custom field.

Translated by looking the words up, not by a native speaker; a Tamil
speaker reviewing it is welcome, and their review on Transifex replaces
these strings for good.

Thanks to xet7 !
2026-08-15 18:00:48 +03:00
Lauri Ojansivu 9ca57a8166 Hindi: the last 605 Gujarati strings are Hindi now.
hi.i18n.json was seeded from Gujarati, so 1,204 of its values were written
in another language entirely - readable to nobody who came to WeKan for
Hindi. The remaining 605 are translated here: the search operators and
their predicates, the whole globalSearch instruction text, the reports and
wait spinners, the attachment storage and S3 settings, every board and
database migration with its confirmation dialogue, the brute-force lockout
settings, and the login and repository pages.

The search operators are the words a user TYPES, so they are native words
(operator-board is बोर्ड) and the instruction text names those same words
through its __operator_*__ placeholders. Three values stay in Latin on
purpose: ldap, gridfs-storage and excel-font are a protocol, a product and
a font family Excel is handed by name, and the Gujarati transliterations of
them were wrong in a way that no reader could act on.

hi is now at zero wrong-script values. It was translated by reading the
words up rather than by a native speaker, so a Hindi speaker reviewing it
is welcome - and when that review arrives on Transifex it replaces these
strings, which the pull merge guarantees is never reversed.

Thanks to xet7 !
2026-08-15 17:52:30 +03:00
Lauri Ojansivu fcf6a80fc4 Hindi: 604 of 1,204 done, exactly half left.
Two more chunks: the Rules engine's actions and date fields, the custom head
tags and web manifest settings, LDAP and authentication, the due-date reminder
activity lines, the weekdays, assignees, organisations and teams, and the
notification list.

Written from the English source and the file's own Devanagari vocabulary; a
Hindi speaker's review is still wanted.

Thanks to xet7 !
2026-08-15 17:45:14 +03:00
Lauri Ojansivu 50e782fdd6 Hindi: 361 of 1,204 done, 843 left.
Another chunk of the Gujarati values replaced with Devanagari: the login and
account strings, multi-selection, the archive views, the shortcut list, time
tracking, the custom logo and login-page settings, SMTP and invitations,
webhooks and the version panel.

Same caveat as the last chunk: written from the English source and the file's
own correct Devanagari vocabulary, and worth a Hindi speaker's review.

Thanks to xet7 !
2026-08-15 17:41:11 +03:00
Lauri Ojansivu b36995617f Hindi: 236 of the 1,204 Gujarati values are Hindi now.
The third wrong-script file. hi.i18n.json holds 1,204 values in GUJARATI -
`board` was બોર્ડ, `save` was સાચવો - beside the Devanagari ones that were
always right. Two closely related scripts, which is probably how it went
unnoticed: it reads as Hindi at a glance and is not.

236 done: the activity feed, workspaces and All Boards settings, the archive
confirmations, Planning Poker, the colour names, the permission descriptions,
the email templates, card export, the filters and the import instructions.

Written from the English source and the file's own correct Devanagari
vocabulary. Hindi is not a language I can check as closely as the two before it,
so these are worth a native speaker's eye - the merge rules mean a human
translation arriving on Transifex replaces them and is never overwritten back.

968 left in this file.

Thanks to xet7 !
2026-08-15 17:40:02 +03:00
Lauri Ojansivu 3673177dab Georgian is Georgian again: 789 values that were Russian.
The second of the four wrong-script files is finished. ka.i18n.json held 789
values in RUSSIAN - `board` was Доска, the whole activity feed was Russian, the
Gantt view was Диаграмма Ганта - beside the Georgian ones that were always
right. Mixed, so no whole-file revert could have told them apart, and nothing
counted them: they are not equal to the English source, so `--missing` never saw
them and the merge rules would have protected them forever.

All 789 are Georgian now. The activity feed, the archive and template strings,
board backgrounds and All Boards settings, voting and Planning Poker, the colour
names, the filters, the whole Rules engine with its triggers and actions, SMTP
and webhooks, the custom logo and layout settings, card and minicard settings,
the due-date reminders, the weekdays, the search operators and predicates with
their instruction text, the reports, attachments and storage, the ticket and
request strings, and the custom-translation dialogs.

The search `operator-*` and `predicate-*` keys are the words a user TYPES into a
query, so they are translated as words rather than left in English: `დაფა` for
board, `ვადა` for due, and single Georgian letters for the abbreviations. That
is what those keys are for - a Georgian reader searching in Georgian - and the
instruction text below them now names the same words.

The last four were product names carrying Russian: Диаграмма Ганта and Покер
планирования are Georgian now too, as განტის დიაგრამა and დაგეგმვის პოკერი.

`--count ka` is 0. Remaining across the other two files: hi 1,204, ta 1,637.

Thanks to xet7 !
2026-08-15 17:37:01 +03:00
Lauri Ojansivu 88c23203a9 Georgian: 366 of 789 done, 300 left.
Two more chunks of the Russian values in ka.i18n.json replaced with Georgian:
the whole Rules engine (triggers, actions, the date fields, the email action),
the SMTP and webhook settings, the custom logo and layout settings, the card and
minicard settings, the due-date reminders and the activity feed's label and
custom-field lines, the organisation and team dialogs.

Thanks to xet7 !
2026-08-15 17:29:43 +03:00
Lauri Ojansivu 6d69bf8937 Georgian: 244 of the 789 Russian values are Georgian now.
Continuing the wrong-script work. ka.i18n.json holds 789 values in RUSSIAN -
`board` was Доска, every activity line was Russian - beside the Georgian ones
that were always right. Mixed, so no whole-file revert could tell them apart.

244 of them are Georgian now: the whole activity feed, the archive and template
strings, the board background and All Boards settings, voting and Planning
Poker, the colour names, the filters, the import instructions and the keyboard
shortcuts. They use the words the file already used for board, list, card and
swimlane, so the new lines read with the old ones.

545 left in this file, and the tool counts them exactly: `node
releases/translations/wrong-script.mjs --count ka`.

Thanks to xet7 !
2026-08-15 16:35:24 +03:00
Lauri Ojansivu f225b3245d Korean is Korean again: 354 values that were Japanese.
The scan for the TODO Later backlog turned up something worse than any missing
string, and this is the first of the four files it found. `ko.i18n.json` held
354 values in Japanese kana - `board` was ボード, every activity line was
Japanese - beside 2,000 that were correct Korean. Mixed, so no whole-file revert
could have told them apart.

That is worse than English. An untranslated string shows English, the reader
sees it is unfinished, a speaker fixes it, and `--missing` counts it. A value in
the wrong language shows confident text the reader may not be able to read,
nothing counts it, and the merge rules protect it forever because it is not
equal to the English source. It would have survived every pull and every fill.

All 354 are Korean now, written from the English source and the file's own
correct vocabulary so the new lines use the same words for board, list, card and
swimlane that the rest of the file already used. `--count ko` is 0.

releases/translations/wrong-script.mjs is the tool: --count says how many values
each language carries in a script that is not its own, --list dumps those keys
with their English source, and --apply writes translations back. It overwrites,
which fill-translations.mjs deliberately never does - filling only ever touches
English placeholders - and that is exactly why it is a separate tool: replacing
a wrong-language value is the one case where overwriting a non-English value is
right, and --apply refuses any key the scan did not flag.

The shared Indic danda is not counted as evidence of Devanagari, and Korean
hanja is not counted as evidence of Chinese, so neither shows up as a false
positive.

Still to do, recorded in TODO Later: ta 1,637, hi 1,204, ka 789.

The README said WeKan is translated to 154 languages. It has been 234 for a
while - 142 of them essentially complete - and the number is checked against the
data files now rather than remembered, so it cannot drift again.

Thanks to xet7 !
2026-08-15 16:25:51 +03:00
Lauri Ojansivu 394bd12b74 CHANGELOG: TODO Later says what the translation backlog really is.
The near-complete half was recorded as 9,072 strings. Half of that is values
that are the same in every language - product names, bare numbers, symbols,
unit abbreviations - which count as untranslated only because they equal the
English source and will never stop counting. The real prose backlog is about
3,400 values over 251 keys, median 23 per language, and the entry says so.

It also records what the scan for this turned up, which is worse than anything
missing: four files carry text in ANOTHER language. Tamil holds 1,174 Telugu
values and 463 Devanagari ones, Hindi holds 1,201 Gujarati ones, Georgian 789
Russian ones and Korean 354 Japanese ones - each file mixed, with some keys
right and some not. A missing string shows English and invites a correction;
these show confident text the reader may not be able to read, and the merge
rules protect them forever because they are not equal to English.

Thanks to xet7 !
2026-08-15 16:12:42 +03:00
Lauri Ojansivu a1477d0c93 Five strings the Admin Panel shows, in 126 languages.
Continuing the TODO Later translation backlog, starting where one decision buys
the most: the keys missing from the MOST languages. Five of them, 252 values,
across the 126 near-complete languages that lacked them:

  event-ip             IP address
  event-attempts       Attempts
  databaseReportTitle  Database problems
  color-magenta        magenta
  normal               Normal

The first three are the Admin Panel / Problems page - the page the security
events from this release are listed on - so they were English in every language
that had not translated them, on a page an operator reads when something is
wrong. They are complete now: 0 of 142 near-complete languages still lack them.

`color-magenta` and `normal` are filled where the language genuinely differs and
left alone where it does not: magenta is the same word in most Latin-script
languages, so 57 files keep it and are not "missing" it in any meaningful sense.
Same for `normal` in 37.

Written per base language and expanded to that language's variants, so the eight
Spanish files, the four German ones and the five Chinese ones agree with each
other by construction rather than by luck.

The backlog for the near-complete half goes from 9,066 to 8,808 - but the number
that matters is smaller than either. Of those 9,066, about 4,700 are values that
are the same in every language: product names (Meteor, MongoDB, S3/MinIO,
OAuth2), bare numbers (the Planning Poker values), symbols (`/`, `#`, `@`), unit
abbreviations and one empty string. They count as "untranslated" only because
the value equals the English source, which for a proper noun is the correct
translation. The real prose backlog is about 3,400 values over 251 keys, median
23 per language.

Thanks to xet7 !
2026-08-15 16:12:10 +03:00
Lauri Ojansivu 9ee8deae3a Conformance cases for $meta, and a test run that stops the databases it started.
Two things.

$META HAS CONFORMANCE CASES NOW. The catalogue gained $slice and $elemMatch when
they were implemented; $meta went in afterwards and had none, so the one part of
the projection work that a live database could check was the part nothing
checked. Two cases, compared differently on purpose:

  projection/$meta recordId   COMPARE.SHAPE
  projection/$meta textScore  full value comparison

recordId is the STORAGE identity of a document, so its value is each backend's
own and comparing values would report a difference that is not one. What has to
agree is that every backend ANSWERS, and answers with the same shape - a number
on every row - which is what SHAPE checks. Before the fix they all errored, so
that is exactly the regression worth guarding.

textScore is computed by the handler ABOVE the backends, from the document's own
strings, so every backend must produce the same number and the values are
compared in full. It is also the only case that exercises $text: only `alpha`
(_id 1) carries the term, and carries it once. Sorted by _id so the comparison
does not depend on the order a backend returns rows in.

A TEST RUN STOPS ITS DATABASES ON EVERY WAY OUT. build.sh stopped the test server
and the mongod at the END of the run - reached when the run finishes, and not
reached when it is interrupted or when one of the early failures returns. Then
the mongod on :3001 and the bundle server on :3000 outlive the run.

That is not untidy so much as expensive, and the cost lands on the NEXT run. The
harness reuses a database only when it ANSWERS - a port held by something else
moves it aside - so a leftover mongod IS reused, holding a database this run
never seeded, and the failures land somewhere else entirely.

So the stopping is a shell function called from a trap on EXIT INT TERM, and the
normal path calls the same function. It is idempotent - each half clears its PID -
so the trap firing afterwards stops nothing twice, and a database this run did
NOT start is still left alone, because its PID was never recorded. Conformance
containers go with it: that stage names each engine's container
wekan-conformance-db-<timestamp> and removes it when the engine is done, but an
interrupted run leaves the one it was on, and a container still holding 5432 or
3306 fails the next run's engine before it starts.

cmd has no trap, so build.bat does the same clearing on the way IN as well as on
the way out: one :stop_test_databases label, called at the start and the end of
both all-tests flows. Starting from a clean slate is the batch answer to a trap
it cannot have. The two copies of the stopping that were already there are now
that one label, so they cannot drift.

tests/buildScriptParity.test.cjs pins both: the trap and the idempotence in
build.sh, the four calls in build.bat, and that both stop the same two servers
and the conformance containers.

Thanks to xet7 !
2026-08-15 14:07:05 +03:00
Lauri Ojansivu 0201c1fb89 Custom Fields form: the checkboxes are the animated one, beside their labels.
The markup was already the Admin Panel / Announcement checkbox - `a.flex >
.materialCheckBox + span`, a grey square that morphs into a green tick over
0.2s. What it lacked is where it is drawn.

The Custom Fields create and edit form is a POP-OVER, and a pop-over renders in
the global popup container, not inside `.sidebar`. So these rows never got the
`align-items: center` that #6465 added for exactly this - `.flex` on its own is
only `display: flex`, with no alignment and no gap, so the box sits on one line
and its label on another. The morph was there the whole time; it played on a box
that was not next to the words it belongs to, which beside the announcement's
neat row is what "not the animated one" looks like.

The rows are aligned now, and both halves of the box are spelled out for this
popup: the 13px square with its 0.2s transition, and the green tick it becomes.
The checked half already existed for the board Card Settings and Subtask
Settings popups, which are pop-overs for the same reason; the create/edit form
is added to that list, and the unchecked half is written out beside it - without
it the transition would run between a box some other rule happened to size and
the tick, which is the animation looking wrong rather than missing.

tests/customFieldsSectionMenu.test.cjs pins that the form's six checkboxes are
`.materialCheckBox` and not a native one the browser would draw, that both popup
names carry the alignment and both box states, and that the transition is there
- it is the transition that makes it animate rather than jump.

Thanks to xet7 !
2026-08-15 11:23:21 +03:00
Lauri Ojansivu 2b30439ced push-all-translations pushed 146 of 245 languages, and said nothing about it.
`tx push -t` uploads translations for the languages the PROJECT has as targets.
A language that exists only here - every language added to imports/i18n/data/
since the project was last reconciled - is not a target there, so the CLI skips
its file without a word. WeKan ships 245 translated languages; the project had
146, so ninety-nine were pushed nowhere and the run looked like it had worked.

Pushing harder cannot fix that: the language has to exist on the project first.
So the script reconciles before it pushes.

sync-transifex-languages.mjs enumerates the language files, resolves each to the
code Transifex knows it by (reversing .tx/config's lang_map, so `ar-DZ.i18n.json`
is asked for as `ar_DZ`), reads the project's current target languages over the
API - following pagination, since a project with hundreds of languages does not
answer in one page - and adds the missing ones. English is the source, not a
target, so it is left out: 245, not 246.

They are added ONE AT A TIME on purpose. Transifex refuses a language code it
does not know, and in a batch that one refusal takes the whole batch with it. A
code WeKan invented should cost only itself and should be NAMED, so it can be
mapped in lang_map the way `vec: ve-CC` and `vls: vl-SS` already are.

The count is printed at both ends of the run, so "146 of 245" would be visible
rather than something noticed months later when a language is still English on
Transifex. --dry-run says what it would add and changes nothing; --list prints
the codes and needs neither a token nor the network, so the enumeration can be
checked offline. The token comes from TX_TOKEN or ~/.transifexrc, the file the tx
CLI itself writes; without one the reconcile fails loudly and the push stops,
because pushing anyway is exactly the silent partial push this fixes. --skip-sync
is there for a push known to add no language.

Checked here: the enumeration returns 245 with lang_map reversed correctly, and
the API paths were exercised against a stub server - pagination followed across
two pages, --dry-run posting nothing, and the live run adding each missing
language and skipping the two the project already had.

One thing the enumeration turned up: lang_map still carries `es_TX: es-TX`, for
which there is no file. It is left alone here - it is a config question, not a
push bug - but it is why the map has 63 entries and only 62 are used.

Thanks to xet7 !
2026-08-15 11:16:16 +03:00
Lauri Ojansivu 763a3827d1 Every attempt at the five REST API holes is shown in Admin Panel / Problems.
A guard that refuses in silence tells the operator nothing. Four of the five
findings have an ATTEMPT that can be told apart from ordinary use, so each
refusal is now recorded through server/lib/securityLog into the `security`
stream - which is what Admin Panel / Problems counts and lists - with a catalog
entry naming it after its Hall of Fame page:

  authz.card-delete  PurgeBleed   critical  CWE-639
  authz.card-member  GuestBleed   high      CWE-639
  authz.board-list   StaleBleed   medium    CWE-863
  spoofing.author    AuthorBleed  medium    CWE-345

What counts as an attempt is the part worth being careful about, because a log
that fires on ordinary use is a log nobody reads:

- PurgeBleed logs when the card id names a card that exists but is NOT on the
  authorised board. A card id that names nothing is an ordinary 404 and writes
  nothing. Telling those apart needs the bare lookup back, so it is there once
  more - after the constrained one has already failed, named `elsewhere` for
  what it is, and never handed to cardRemover.
- GuestBleed logs the ids dropped from a members/assignees array. An array whose
  ids are all fine writes nothing.
- StaleBleed logs when the caller HAS revoked memberships that the listing
  withheld - counted, not named, because the point is that somebody removed is
  still asking and the titles are the thing being withheld. A caller with no
  revoked membership writes nothing.
- AuthorBleed logs when the body still carries an authorId naming somebody
  other than the session. Naming yourself is what an old client does and is not
  an attempt.

HashBleed deliberately has NO key, and the catalog says why: every call to
GET/PUT /api/users/{userId} is a legitimate admin call. The fault was in what
the answer carried, not in who asked, so a log line there would fire on ordinary
use and say nothing about an attacker.

`req.body.authorId` is still READ - that is how the spoof is noticed - but never
used as an identity, and the guard pins that: not stored as userId, not passed
to cardCreation, cardRemover or customFieldCreation, and read exactly once in
cards.js, inside noteAuthorSpoof.

tests/restApiIdorBatch.test.cjs is at 14 checks, five of them negative: that
ordinary use writes nothing on all four, that HashBleed has no key and neither
endpoint logs, and that every securityLog call sits in a try/catch - a log that
throws would turn a refusal into a 500, which makes the logging its own denial
of service.

Thanks to ybsun0215 and xet7 !
2026-08-15 11:16:16 +03:00
Lauri Ojansivu 28086fe7d7 CHANGELOG: the five CRITICAL REST API fixes go first under Upcoming.
A CRITICAL subsection comes before everything else, so it keeps the "This
release " prefix and the container-scanning section below it becomes "and fixes
the following SECURITY ISSUES found by container scanning:".

One group, **The REST API** - what an endpoint authorises, and what it then acts
on - because that is the one sentence all five share. Each entry links its Hall
of Fame page and says what the endpoint authorised, what it then reached, and
what the fix ties together.

The **In short:** paragraph leads with them now, and names the worst: any user
with write access to one board could destroy the comments, checklists and
history of every card on every board in the instance.

The guard names each of the five in its header, which is what
fixedVulnerabilityClasses.test.cjs compares the Hall of Fame against - a
published vulnerability with no test that names it is a fix the next refactor
can undo quietly.

Thanks to ybsun0215 and xet7 !
2026-08-15 10:57:19 +03:00
Lauri Ojansivu 9cc4139699 PurgeBleed, HashBleed, GuestBleed, StaleBleed, AuthorBleed: five REST API fixes.
Five findings reported by ybsun0215 against v10.91, all the same family - an
endpoint authorises ONE id and then acts on, or answers with, something the
caller was never authorised for. All need WITH_API=true.

PurgeBleed (GHSA-8cqr-x6m5-v4w6) is the severe one. The single-card DELETE
authorises the caller on the board in the URL and then fetched the card by its
id ALONE. cardRemover runs BEFORE the removal, so it erased the checklists,
checklist items, comments, activity history and subcard tree of whatever card
that bare lookup returned - any card in the instance, on boards the caller
cannot read, irreversibly. The removal after it uses a triple key that a foreign
card never matches, so the card shell survived and the endpoint answered 200:
nothing said the contents of somebody's card had just been destroyed. Any user
with write access to one board - their own suffices - could reach every board.
The bulk endpoint had always constrained its lookup with {_id, boardId}; this
one is the sibling that was missed, the same shape as PassBleed. It uses the
constrained lookup now.

HashBleed (GHSA-6qpx-x7vr-p9w6): GET and PUT /api/users/{userId} serialised the
whole Meteor user document, so every answer carried services.password.bcrypt -
an offline-crackable password hash - and services.resume.loginTokens, the hash
of every live session with the time it began. The authorisation was never the
problem; the payload was. Walking the ids from GET /api/users harvested the
credential material of the instance. The sibling endpoints in the same file show
what was meant: the list projects to _id and username, and the self view deletes
services. One helper strips services and sessionData now and both endpoints
answer through it.

GuestBleed (GHSA-whxm-pxgj-7wqv): the members and assignees ARRAYS on card
create and update were stored as given, and GET /api/user/cards answered by card
membership without re-checking board access. Together: a member of a private
board could put an outsider on a card, and that outsider's own feed then
returned the private board's card titles, board/list/swimlane ids, dates and
co-members - continuously, while their direct read of the board stayed
Forbidden. The rule already existed on the merge endpoint since #5998; it covers
the array shapes now, on single create, bulk create and update, and the listing
is filtered by board visibility as well so a card placed before this release
stops answering too.

StaleBleed (GHSA-r8r3-23vr-8jh6): the board listing matched 'members.userId'
with a dotted path, which ignores isActive - and removing a member does not
delete their entry, it sets isActive: false and keeps it. A removed member's own
listing went on showing the board's id and title. Reading the board was already
refused, so this is the id and the title, but the id is what the rest of the API
is addressed by. Same class as GHSA-gwc4-fw7p-gw58, whose fix wrote "a share
entry counts only while it is active, everywhere"; this endpoint predates that
consolidation. It uses $elemMatch now.

AuthorBleed (GHSA-6jr3-42jf-vhm5): six paths - single, bulk and linked card
create, single and bulk card delete, custom-field create - took the actor from
req.body.authorId, checking only that such a user exists. So a board member could
write "victim created this card" and "victim deleted this card" into a board's
history, and the card document recorded the forged userId as its creator. The
same class was fixed for comments in 8.19 and for the card PUT handler, whose
note still says "use req.userId consistently (it previously read
req.body.authorId here)". All six read req.userId now.

tests/restApiIdorBatch.test.cjs pins all five in the shape a regression would
change: the constrained lookups, the stripped subtree, the one assignable rule
across every shape, the $elemMatch, and each of the six paths by what it records.

Thanks to ybsun0215 and xet7 !
2026-08-15 10:53:13 +03:00
Lauri Ojansivu feda5fc72b CHANGELOG: the Export word and the Custom Fields popup go under Upcoming.
The missing `export` key goes under **New strings**, beside the other keys this
release added, because that is what it is: a key the release adds, and then
translates into all 245 other files. The Custom Fields popup - a field made from
a card that was never created, and the pencil that sat under the name instead of
beside it - goes under **Card details**, next to the section that was unreachable
on a card with no fields.

The **In short:** paragraph counts twelve bug fixes now instead of ten, and names
both.

Thanks to xet7 !
2026-08-15 10:21:22 +03:00
Lauri Ojansivu 67b91c8504 Custom Fields popup: a new field appears, and the pencil is beside the name.
Two reports against the popup the Custom Fields heading opens.

A new field made from it never appeared. "Add custom field" sits OUTSIDE the list
of fields, so its data context is the popup's own - which is the CARD - and the
shared create/edit form decided insert-vs-update on whether its context had an
`_id`. A card has one, so creating a field from a card ran the UPDATE branch
against a custom field whose id was a card's: no such document, nothing written,
nothing said. From the board sidebar the same form works, because there the
context has no `_id`, which is why this only ever happened from a card.

Both halves are fixed. The popup hands the create form an empty context, since a
new field is made from nothing. And the form no longer trusts a bare `_id`: it
asks whether that id NAMES a custom field, so a context arriving from anywhere
cannot make it update one that is not there.

The pencil was on a line of its own under the field's name. Every other pop-over
list has one anchor per row, so `li` is a block and the anchor fills it - and two
anchors in a block stack. This list has two by design, the name with its checkbox
and the pencil that edits the field, so the ROW is the flex container and the
name takes the space the pencil does not. Scoped to this popup, so no other list
moves.

tests/customFieldsSectionMenu.test.cjs gains both: that the create form is handed
an empty context and decides by looking the id up, and that a row is a checkbox,
a name and a pencil on one line.

Thanks to xet7 !
2026-08-15 06:24:06 +03:00
Lauri Ojansivu 9417e35d0b Export is a word now, in every language, not the key "export" in lowercase.
Every menu on a board grew an Export row and an Import row, and the Export one
was written `{{_ 'export'}}` - a key that has never existed in en.i18n.json, or
in any of the other 245 files. i18next answers a key it does not know with the
key, so the row read "export": lowercase, untranslated, in every language
including English. The report came from Finnish, where every other word in the
menu is Finnish and this one was an English-looking lowercase word.

`export` is a key now, "Export" in English, and it is translated into all 245
other files. The word is each language's own: for 225 of them it is the shared
part of that file's existing "Export list" and "Export swimlane", which is the
word they already use - "Vie" in Finnish, "Exportieren" in German,
"Экспортировать" in Russian.

The rest are written out, because a shared prefix is the wrong answer for them.
Where the derivation took an article with it - Portuguese "Exportar a", Irish
"Easpórtáil an", Welsh "Allforio'r" - the article is dropped. Where the verb is a
circumfix that only appears with its object between the halves, both halves are
written: Afrikaans "Voer uit", Icelandic "Flytja út", Akan "Yi adi", Tok Pisin
"Salim i go aut", Ewe "Ɖe ɖe go". Hungarian takes the nominative "Exportálás"
rather than the possessive "Exportálása". Klingon keeps its own capitalisation,
yIngeH with the lowercase y, which a capital-first rule got wrong. And the
twenty-one languages that do not put spaces between words - the Chinese variants,
Japanese, Khmer, Thai, Burmese, Tibetan and Dzongkha - had nothing to derive from
and are written from their own phrases: the two Chinese forms by script, and
エクスポート, នាំចេញ, ส่งออก, ထုတ်ယူရန်.

`import` already existed and was translated everywhere. In seven files it was
still the bare English noun while their Export was a verb, which read as a pair
that did not match: Czech gets "Importovat" beside "Exportovat", Turkmen "Import
et" beside "Eksport et", and the four Uzbek files "Import qilish" beside "Eksport
qilish". Malay and the English variants keep "Import", which is the word those
languages use.

239 files changed, one line each. Key order is unchanged - `export` sits where
en.i18n.json puts it, ahead of `export-board`, in every file - and
verify-human-preference.mjs passes.

Thanks to xet7 !
2026-08-15 06:24:06 +03:00
Lauri Ojansivu d49d055d09 CHANGELOG: the removed heading row goes under Upcoming.
Under Card and minicard menus, beside the rest of what moved into a thing's own
menu.

Thanks to xet7 !
2026-08-15 06:15:01 +03:00
Lauri Ojansivu 02025aa6c6 Show on Card and Show on Minicard: no heading row saying the title again.
The heading row named the columns of a ROW - "Show on Card" over the checkbox and
"Description" over the setting's name - which is what they labelled, but not what
they looked like. Since the rows flow into as many columns as the window fits,
the two headings sat above the FIRST of three or four columns and read as if they
named those: "Show on Card" over one column of settings and "Description" over
another, when both columns are the same kind of row.

The first heading also said what the popup's own title says. Show on Card opens a
popup titled "Show on Card" and then repeated it a line below; the same for Show
on Minicard.

Both are gone, and the two-pixel rule under them with them - one more line across
a popup that is a list of lines. A row is a checkbox and the name of a setting,
which needs no heading over it. One template serves both popups, so both lose it
together, and the sticky positioning, background and z-index that existed only to
keep those headings visible while the rows scrolled go too.

tests/showOnCardMenus.test.cjs pinned the heading row's full-width span; it now
pins the opposite, with the reason, and adds that the popup does not print its own
title twice.

Thanks to xet7 !
2026-08-15 06:14:30 +03:00
Lauri Ojansivu c878bdb8c0 CHANGELOG: three commits since v10.91 had no entry at all.
An audit of the Upcoming section against CLAUDE.md: every entry is shaped right,
every summary carries its Thanks, none ends in punctuation before the link, and
all 70 commit links resolve to a commit in this clone and to one made after the
v10.91 bump. One summary is 111 characters against the 110 the rules aim at,
which is close enough to leave.

What it did find is 21 commits since that bump with no link in Upcoming. Seventeen
are a sibling of an entry that is there - the three Export-popup commits under one
entry, the board-background pair, the two Custom Fields moves - which is how a
feature built over several commits is written up, and they stay as they are.

Three had no trace anywhere. **Move/Copy selection and Move/Copy swimlane** became
one dialog each instead of two, which is the same deduplication as the date
markup and belongs beside it - so the group line widens from markup to markup or
component. The two **test harness** fixes get a group of their own: reusing a
database because its port was open handed the server a socket that answered and
then closed, and a port owned by something else (an "Omi Server" answering HTTP
on 3001) now moves the test database rather than failing the run at its first
query.

Thanks to xet7 !
2026-08-15 05:53:08 +03:00
Lauri Ojansivu d74a9e5391 CHANGELOG: the Custom Fields fix and the two browser-test fixes go under Upcoming.
The Custom Fields regression is a bug fix, under **Card details** with the rest
of the opened card. The two Playwright commits are developer-facing, under a new
**Browser tests** group beside the shared templates - three page objects that
still described markup as it was before it moved, and the wait for the client
bundle that four Firefox-only failures needed.

The **In short:** paragraph says ten bug fixes now instead of nine, and names
Custom Fields as the one the browser tests found.

Thanks to xet7 !
2026-08-15 05:27:31 +03:00
Lauri Ojansivu aba09a0879 Custom Fields: the section is there for every card, so the only way in always is.
Playwright failed "custom fields panel opens from card details" in all three
browsers, and it was the app, not the guard. Everything about custom fields
moved to the hamburger at the end of the card's own Custom Fields heading - the
picker of which fields are on this card, a pencil each, and "Add custom field" -
and the card menu's entry, the Board Settings row and the wrapper popup went
with it. But the heading was drawn only `if customFieldsWD`, the card's own
custom-field VALUES.

So the one way in existed only once you were already through it. A card with no
custom field had no heading and therefore no hamburger; a board that had never
used them had no way anywhere, since the sidebar view that still holds the
create/edit forms is opened by nothing. The chicken-and-egg was invisible on any
board that already had a field, which is every board a developer tests on, and
was caught by a seeded board that had none.

The heading is now drawn for every card its reader may write to - `canModifyCard`,
the same condition the rest of the editable card uses. The FIELDS below still
come from `customFieldsWD`, so a card with none shows an empty section rather
than a phantom row per board definition, and a reader who may not write sees no
heading at all.

`a.js-custom-fields` went too. The move took away its label text and left the
tag: an empty anchor that rendered nothing, could not be clicked, and had two
`'click .js-custom-fields'` handlers still bound to it in cardDetails.js. All
three are gone, and `CardPage.openCustomFields` clicks the hamburger that is the
real entry point.

tests/customFieldsSectionMenu.test.cjs gains the two that would have caught
this: the heading is gated on who may write rather than on what the card already
has, and no empty anchor or orphaned handler is left behind. Its five existing
assertions - including that the three old places stay gone - still pass.

Thanks to xet7 !
2026-08-15 05:23:35 +03:00
Lauri Ojansivu 7a08f08124 Playwright: wait for the client bundle before calling a Meteor method.
Four Firefox-only failures in the 03:34 run, all the same cause: `Error:
page.evaluate: Meteor is not defined`, and in the fourth the count came back as
the string "error: can't access property \"callAsync\", window.Meteor is
undefined". Chromium and WebKit passed all four.

`page.goto(..., { waitUntil: 'networkidle' })` says the NETWORK went quiet, not
that the client bundle has finished executing. Under the three-browser parallel
run against one shared server, Firefox reached the evaluate with `Meteor` still
undefined - the bundle is large and had been fetched but not yet run.

`helpers/auth.js` has had `waitForMeteor` for exactly this since WebKit needed
it; these two specs simply never called it. `callMethod` in the All Boards spec
now awaits it before every call, which covers all three failures there, and the
admin Files report awaits it after its goto. It is idempotent and returns at
once when Meteor is already up, so it costs the passing browsers nothing.

Thanks to xet7 !
2026-08-15 05:19:42 +03:00
Lauri Ojansivu acaaa3c03f Playwright: three page objects still described the markup as it was before it moved.
The 03:34 run failed the same four tests in Chromium and in Firefox, and all
four are the guard describing the OLD markup rather than the app being wrong.
Each moved deliberately in this release, so the guard is what changes.

`editTitle` waited for `.js-card-title.js-open-inlined-form` - both classes on
ONE element. The title splits now: `span.card-details-title-edit-zone
.js-open-inlined-form` is the leading half that opens the editor, and the rest
of the `h2` drags the window, which is why `js-card-title-drag-handle` stays on
the whole heading. The class is a DESCENDANT of the title, not a second class
on it, so the old selector matches nothing and the click waits out its timeout.
That failure is `02-cards-open-view.e2e.js:104`.

`openCustomFields` opened the hamburger and clicked `.js-pop-over
.js-custom-fields`. Custom Fields is no longer a row of the actions popup - it
is on the Custom Fields SECTION of the card itself, `a.js-custom-fields
.card-details-item`, because the settings about a thing belong on the thing
that shows it. One click now, and no hamburger. That failure is
`03-cards-operations.e2e.js:246` and `09-my-cards-filter.e2e.js:122`.

The Activities heading in the board sidebar carries TWO icons since the caret
became shared with the card's eleven sections: the caret that says whether the
section is open, and the section's own comment icon. A bare `i.fa` matches both
and Playwright fails it as a strict-mode violation rather than picking one. The
spec now asks for the caret specifically - `fa-caret-down`, `fa-caret-right` or
`fa-caret-left`, the three directions `caretClassFor` can produce - which is
also a stronger assertion than "some icon is visible", since the caret is the
part that indicates state. That failure is `08-notifications-activity.e2e.js:22`.

Each selector was read against the markup that is in the tree now:
`cardDetails.jade` lines 76-79 and 456, and `sidebar.jade` lines 62-64.

Thanks to xet7 !
2026-08-15 04:24:00 +03:00
Lauri Ojansivu dcbb219b6b CHANGELOG: the twelve language entries move up to Upcoming, and what is left goes to TODO Later.
Twelve entries had been added under `and improves the translations:` inside the
ALREADY RELEASED v10.84 section rather than under Upcoming - Klingon, the six,
the eight, the nine, the ten, the fourteen, the fifteen, the eighteen, the
twenty, the sort letters, the 31 new strings and the second tier for the newest
languages. As released, v10.84 had exactly one entry there, the three
Version-pane and checklist strings, and it keeps it. The other twelve are the
work of the release being prepared now, so that is where a reader of the next
release notes has to find them.

With thirteen entries the subsection has to be grouped, per the rule a
subsection with more than one entry follows: **Deeper translation** for the two
that take languages past the words on the board, **New languages** for the ten
that add files WeKan had none of, and **New strings** for the 31 keys this
release added.

`TODO Later` gets the number a release wants to know. The 234 language files
hold 218,148 strings still equal to the English source, and they are two
different problems: 142 long-standing languages are nearly complete (9,072
between them, avg 64, worst Zeelandic at 114), and 92 recently-added ones carry
only the first two tiers (2,273 missing each, 209,076 in total). A release can
go out on either - an untranslated key renders its English source, never the
key - but the first half is what a language advertised as complete is judged
on, and it is about a day of work.

Thanks to xet7 !
2026-08-15 04:22:13 +03:00
Lauri Ojansivu 1fcf3bb5b9 CHANGELOG: the 81 deepened languages and the one-language test go under Upcoming.
Two entries, and the developer-facing subsection becomes grouped now that it
holds more than one: **Language loading** for the guard that pins that a browser
fetches one language file rather than all 246, and **Shared templates** for the
date markup that was already there.

Thanks to xet7 !
2026-08-15 04:13:40 +03:00
Lauri Ojansivu 72fd4aa127 Eighty-one languages go past the board, into the menus and the login page.
A language file started here with the words a board is made of - board, list,
card, swimlane, member, the buttons - about 50 to 77 strings. Eleven of the
largest new ones went further last time, to roughly 112, and the difference is
what somebody meets in the first minute rather than the first second: Log Out,
Forgot password, Email Address, Create Board, Move to Archive, Restore, Copy
Card, Move Card, Delete Card?, List Actions, Board Settings, Member Settings,
Search All Boards, Custom Fields, Add Attachment, Accounts, People,
Organizations, Teams - and, for the ones that had not reached it yet,
Activities, Attachments, Checklists, Assignee, Due Date, Register, Change
Password, Admin Panel, Profile, Watch and Export list.

Every other language that was still at that first tier now carries the second
one too: Maltese, Luxembourgish, Shona, Albanian, Bosnian, Hawaiian, Latin,
Luganda, Assamese, Irish, Icelandic, Javanese, Kurmanji, Kyrgyz, Malagasy,
Maori, Marathi, Chichewa, Oromo, Pashto, Kinyarwanda, Sindhi, Sinhala, Sesotho,
Tajik, Tigrinya, Setswana, Xitsonga, Tatar, Friulian, Ladin, Neapolitan,
Romansh, Aragonese, Corsican, Sardinian, Sicilian, Aromanian, Kashubian, Upper
Sorbian, Silesian, Faroese, Greenlandic, Northern Sami, Bislama, Tok Pisin,
Fijian, Samoan, Tongan, Haitian Creole, Papiamento, Inuktitut, Aymara, Quechua,
Nahuatl, Cherokee, Manx, Cornish, Scottish Gaelic, Guarani, Bashkir, Buryat,
Chuvash, Sakha, Tibetan, Dzongkha, Bhojpuri, Maithili, Konkani, Kashmiri,
Sorani, Akan, Bambara, Ewe, Fula, Northern Ndebele, Northern Sotho, Kirundi,
Swati, Tigre and Wolaytta.

Each string was written from that file's own existing vocabulary, so the new
menu rows use the same word for board, list and card that the board already
uses. The ones written with least confidence are Cherokee, Inuktitut, Nahuatl,
Wolaytta, Tigre and Kashmiri - a speaker who sees an error there should correct
it, and the merge rules guarantee a human translation arriving on Transifex
replaces a filled one and is never overwritten by it.

That is 4,082 strings across 81 files, all of them into placeholders that were
still English: verify-human-preference.mjs passes, and nothing here is pushed
to Transifex as if it were human. Every file is still a small fraction of
2,384 keys, deliberately - the rest falls back to English exactly as before.

Thanks to xet7 !
2026-08-15 04:10:32 +03:00
Lauri Ojansivu 5ab4b74310 Update newest changes from CLAUDE.md to AGENTS.md .
Thanks to xet7 !
2026-08-15 03:58:01 +03:00
Lauri Ojansivu 59c742e6c9 The browser downloads one language, and a test says so.
imports/i18n/data/ is 37 MB across 246 files, all of them reachable from the
client. The only thing keeping them out of the initial bundle is that every
entry in languages.js loads its file through `() => import('./data/<tag>.json')`
- a call Meteor's dynamic-import package code-splits into a module fetched on
demand - and that tap.js imports exactly one of them statically, English, so
the UI stays readable when dynamic import is broken (#6503).

That was true when checked and nothing pinned it. A single
`import data from './data/xx.i18n.json'` added anywhere on the client would
quietly ship that language to every visitor, and nothing about the app would
look wrong; it would just be a heavier download, which no other test measures.

The guard checks the six things that have to hold together: every registered
language has a dynamic loader, languages.js pulls in no data itself, tap.js
statically imports English and nothing else, the loader is called once for the
single resolved tag rather than mapped over the registry, no client file
bundles a language file, and dynamic-import is still in .meteor/packages. A
negative test proves the detector really sees a static import, so the other
checks cannot pass by failing to look.

Thanks to xet7 !
2026-08-15 03:50:20 +03:00
Lauri Ojansivu af2915cb54 CHANGELOG: the second tier for the newest languages goes under Upcoming. 2026-08-15 03:21:38 +03:00
Lauri Ojansivu cd13f56c68 The newest languages go past the words on the board, into the menus.
A new language file started with the words a board is made of - board, list,
card, swimlane, member, the buttons. Enough to recognise the app, not enough to
use it: the menus, the popup titles and the login page were still English.

Eleven of the largest new ones - Bengali, Urdu, Filipino, Hausa, Amharic,
Kannada, Malayalam, Nepali, Somali, Kazakh and Burmese - now carry the second
tier as well: Log Out, Forgot password, Email Address, Create Board, Move to
Archive, Restore, Copy Card, Move Card, Delete Card?, List Actions, Board
Settings, Member Settings, Search All Boards, Custom Fields, Add Attachment,
Accounts, People, Organizations, Teams - the strings somebody meets in the first
minute rather than the first second.

They are at about 112 strings each now, from 77. Still a small fraction of
2,384, and deliberately so: these are the ones where being in English is most
visible, and the rest falls back to English exactly as before.

Thanks to xet7 !
2026-08-15 03:21:38 +03:00
Lauri Ojansivu 52ad6817c4 CHANGELOG: the twenty further languages go under Upcoming. 2026-08-15 03:07:36 +03:00
Lauri Ojansivu a7332f2336 Twenty more languages, including the ones I was least sure of - and which those are.
246 languages, from 154 when this release started.

Confident enough to write without hedging: Tibetan, Dzongkha, Kalaallisut,
Inuktitut, Cherokee, Nahuatl, Buryat, Kashubian, Silesian, Aragonese, Ladin,
Aromanian, Maithili, Bhojpuri, Konkani and Sorani Kurdish (right-to-left).

Written with less to go on, and recorded here rather than quietly: Kashmiri
(right-to-left), Pulaar, Tigre and Wolaytta. For these four the sources are thin
- a wordlist and a grammar sketch rather than a dictionary with a UI vocabulary
in it - so the terms for board, swimlane and checklist are built from the
ordinary words for a writing-board, a path and a list of things to check. They
are the strings most likely to be wrong, and the most likely to be corrected by
the first speaker who opens the language picker and finds their language in it
at all.

That is the whole argument for doing them: the alternative was not a better
translation, it was no language. Cherokee has around 2,000 fluent speakers;
Tigre, Wolaytta and Pulaar have millions between them and almost no software. A
wrong word invites a correction. An English placeholder invites nothing.

The RTL guard grew by two, Sorani Kurdish and Kashmiri, so neither can lay
itself out backwards in silence.

Thanks to xet7 !
2026-08-15 03:07:36 +03:00
Lauri Ojansivu 2859499e9f CHANGELOG: the fourteen further languages go under Upcoming. 2026-08-15 02:34:30 +03:00
Lauri Ojansivu 0cca649b7d Fourteen more: the Pacific, the Caribbean, and the languages of Italy.
226 languages, from 154 at the start of this release.

Kreyol ayisyen, Papiamentu, Tok Pisin, Bislama, Gagana Samoa, Lea faka-Tonga, Na
Vosa Vakaviti, Corsu, Sardu, Sicilianu, Napulitano, Furlan, Rumantsch and
Hornjoserbsce.

Four are creoles - Haitian, Papiamentu, Tok Pisin and Bislama - which between
them are the everyday language of about fifteen million people and are almost
never what software is written in. Five are languages of Italy that Italian does
not cover: Sardinian and Sicilian each have more than a million speakers.
Rumantsch is one of Switzerland's four national languages, and Hornjoserbsce is
one of Germany's.

Each is the same shape as the rest: the words a board is made of, the buttons,
the menus, the dates - and English underneath until somebody corrects it.

Thanks to xet7 !
2026-08-15 02:34:30 +03:00
Lauri Ojansivu 38c4cde1b8 CHANGELOG: the eighteen further languages go under Upcoming. 2026-08-15 02:22:31 +03:00
Lauri Ojansivu a3ee0262d6 Eighteen more languages: the Americas, the Sahel, Siberia and the Celtic ones.
212 languages. WeKan had 154 when this release started.

This block: Bamanankan, Akan, Evegbe, Ikirundi, isiNdebele, Sesotho sa Leboa,
siSwati, Bashkir, Chuvash, Sakha, Runasimi (Quechua), Avane'e (Guarani), Aymar
aru, Foroyskt, Davvisamegiella, Gaidhlig, Kernewek and Gaelg.

Three are indigenous languages of the Americas with millions of speakers between
them and no project-management tool in any of them. Three are Siberian and Volga
languages of the Russian Federation. Four are Celtic - and Cornish and Manx are
revived languages, which is exactly the case where the wording being imperfect
matters least and the language existing at all matters most.

Scottish Gaelic and Cornish take the Scottish and English regional flags rather
than the Union Jack, and Manx takes the Isle of Man's own: a language gets the
flag of where it is spoken, and for these three that is not the same thing as
the flag of the state.

Thanks to xet7 !
2026-08-15 02:22:31 +03:00
Lauri Ojansivu 456dfb93e4 CHANGELOG: the fifteen further languages go under Upcoming. 2026-08-15 02:12:29 +03:00
Lauri Ojansivu 2395dfd084 Fifteen more languages, most of them African.
194 languages. This block is the one WeKan was furthest from having: Tatarcha,
Hawaiian, chiShona, Ikinyarwanda, Chichewa, Sesotho, Setswana, Xitsonga,
Luganda, Afaan Oromoo, Tigrinya, Kurdi, Sindhi, Assamese and Malagasy.

Nine of the fifteen are African languages with tens of millions of speakers each
and no kanban tool in them at all. Sindhi is rtl: true; Kurdish takes the Iraqi
flag, because Kurdistan has no emoji and that is where most of its speakers are
- the same rule as any language spoken across a border.

The three edits are one script now, releases/translations/add-language.mjs: it
writes the strings file as a full copy of the English one with the translations
swapped in, inserts the registry entry in alphabetical order with the name in
the language's own script, and adds the flag only if the map does not already
anticipate it. That is why this block took three commands rather than thirty,
and why nothing was left half-wired.

Thanks to xet7 !
2026-08-15 02:09:44 +03:00
Lauri Ojansivu 584f7d6d86 CHANGELOG: the ten further languages go under Upcoming. 2026-08-15 01:58:15 +03:00
Lauri Ojansivu 0d3bb3ee75 Ten more languages, and Latin among them.
179 languages. This block adds Islenska, Gaeilge, Shqip, Bosanski, Malti,
Letzebuergesch, Kyrgyzcha, Tojiki, Maori and Latina - the same three edits each,
the name written in its own language, and a flag.

Two of them say something about the policy. Maori and Latin have nobody waiting
to translate a kanban board, and Latin has no country at all; it takes the
Vatican's flag because that is the state that still uses it, which is the same
reasoning that gives a language spoken across a border the flag of where it is
spoken. A constructed language keeps the globe instead - Esperanto, Klingon and
Volapuek are nobody's country, and borrowing one would be a claim rather than a
hint.

Latin is a real translation and not a joke: tabula, charta, index, semita,
titelli, sodales, and the imperatives a menu is written in - Adde, Dele, Serva,
Quaere.

Thanks to xet7 !
2026-08-15 01:58:15 +03:00
Lauri Ojansivu 8d36122f90 CHANGELOG: the nine further new languages go under Upcoming. 2026-08-15 01:47:58 +03:00
Lauri Ojansivu 244710e91e Nine more languages: Kannada, Malayalam, Burmese, Sinhala, Nepali, Javanese,
Somali, Pashto, Kazakh.

WeKan is at 169 languages. These nine were missing entirely, and between them
they are spoken by something like 250 million people.

Each arrives the same way as the last six: the strings file, the entry naming
the language in its own script, and the flag. Pashto is rtl: true, and the RTL
guard's list grew with it - that list is the one place the direction of a script
is written down, so a new right-to-left language that is not added to it fails
rather than laying itself out backwards in silence.

Each starts with the words a board is made of - board, list, card, swimlane,
label, member, the buttons, the menus, the dates - about eighty strings, and the
rest falls back to English until somebody who speaks it says otherwise. A
language that is 3% translated is a language somebody can start using and
correcting; 0% is not.

Thanks to xet7 !
2026-08-15 01:47:58 +03:00
Lauri Ojansivu 613ea56d9d Those two language files were symlink targets, not orphans.
The previous commit's guard reported km_KH.i18n.json and ru_RU.i18n.json as
translated files that no entry loads. They are not: km-KH.i18n.json and
ru-RU.i18n.json are SYMLINKS to them. Those two locales are the ones
.tx/config's lang_map does not rename, so Transifex writes the underscored name
and the hyphenated link is what languages.js loads - a working arrangement, and
the reason the pairs were byte-identical.

The guard resolves symlinks now, and checks the properties that actually matter:
every link points at a file that exists, and one end of each pair is loaded by
an entry. The CHANGELOG entry that repeated the wrong finding is corrected with
it.

Nothing was deleted and nothing in .tx/config changed.

Thanks to xet7 !
2026-08-15 01:34:57 +03:00
Lauri Ojansivu d57d2fb045 CHANGELOG: the six new languages go under Upcoming. 2026-08-15 01:25:10 +03:00
Lauri Ojansivu 1fedfb2625 Six languages WeKan did not have: Bengali, Urdu, Marathi, Filipino, Hausa, Amharic.
WeKan had 154 language files and not one of them was Bengali - about 270
million speakers - or Urdu, or Marathi, Filipino, Hausa or Amharic. The list of
languages WeKan supports was never a judgement about which languages matter; it
is the list somebody happened to start.

Each is three edits, and all three are needed or the language is invisible: the
strings file, the entry in languages.js naming the language in that language,
and the flag in the picker. Urdu is rtl: true, so the interface lays itself out
right-to-left for it.

A new file is a full copy of en.i18n.json with the translated values swapped in,
which is what every other language file here is: the key-order guard reads
absolute positions, so a file holding only the strings it has translated would
put every later key at the wrong index.

tests/newLanguageWiring.test.cjs holds the three edits together, and found two
things while being written: fourteen languages were showing a globe instead of a
flag (Acehnese, Moroccan Arabic, Asturian, Breton, Frisian, Norwegian Bokmal,
Occitan, Turkmen, Walloon, Wu, Yiddish, Tamazight and two Chinese variants),
which they no longer do; and two files nobody loads - km_KH.i18n.json and
ru_RU.i18n.json are translated, committed, and imported by no entry, because the
entries for those tags load the hyphenated files instead. They are pinned as a
known state, so a third one is a failure rather than a shrug.

Thanks to xet7 !
2026-08-15 01:25:09 +03:00
Lauri Ojansivu 3c6344154b CHANGELOG: the eight further languages and the sort letters go under Upcoming. 2026-08-15 01:10:53 +03:00
Lauri Ojansivu 240f8f2d4a Uyghur, Belarusian, Catalan, Estonian, Malay, Turkmen, Xhosa, Igbo, sort letters.
Another eight languages from their own vocabulary: the export and import menus,
the roles table, the board-status pane, the starred pages, the search operators,
and for Igbo the last of its report and recovery strings.

The list sort letters are translated too. A list header shows (N), (M) or (L)
for whether it is sorted by NAME, by your MANUAL order or by LAST access -
initials of the English words, so in any other language they were three letters
that stand for nothing. They are that language's own initials now: German
(N)/(R)/(Z) for Name, Reihenfolge, Zugriff; Finnish (N)/(J)/(K) for Nimi,
Jaerjestys, Kaeyttoe; Russian (И)/(П)/(Д) - twenty-five languages, and their
regional variants after them.

What is left is mostly not translatable. Of the strings still flagged in German,
French and Dutch, three quarters are a word of one or two syllables that those
languages spell exactly as English does - Filter, Team, Text, Details, Layout,
Person, Pause, Actions, Date, Description, Notifications, Type, Database, Help,
Repository. They are counted as untranslated because the only test available is
"is this still equal to the English source", and by that test a correct
translation that happens to be identical can never pass.

Thanks to xet7 !
2026-08-15 01:10:52 +03:00
Lauri Ojansivu 8674936753 CHANGELOG: the eight volunteer-less languages go under Upcoming. 2026-08-15 00:53:35 +03:00
Lauri Ojansivu da022efe62 Frisian, Igbo, Yoruba, Wolof, Breton, Walloon, Volapuek and Tamazight.
Eight languages nobody has volunteered for. Five of their files were not
untranslated at all - wo, wa, br, vo and zgh were written in French ("Tableau",
"Carte", "Liste", "Supprimer"), with Esperanto in the Volapuek file and Arabic
in the Tamazight one, seeded from whatever was to hand. As with Klingon, that is
not a translation to protect: the core vocabulary is replaced with each
language's own words - Breton Taolenn, Kartenn, Roll; Walloon Tavlea, Cate,
Djiveye; Wolof Tablo, Karta, Limu; Volapuek Bod, Kad, Lised; Tamazight
Tafelwit, Takarda, Tabdart.

On top of that each got the strings a board actually shows: the export and
import menus, the roles table, the board status pane, the starred pages, and
the sentence that explains what dragging a board onto Home does.

They are imperfect, and that is the point: a wrong string is readable, obviously
improvable and an invitation to the person who speaks the language, which an
English placeholder never is. A correction arriving through Transifex replaces
the filled one, because the merge always prefers a human translation.

What is left in the big files is mostly not translatable at all: German Filter,
Team, Text, Details, French Actions, Date, Description, Notifications and Dutch
Filter, Type are the words those languages use, and they are counted as
untranslated only because the tool's test for it is "still equal to English".

Thanks to xet7 !
2026-08-15 00:53:34 +03:00
Lauri Ojansivu ff514a5bb6 CHANGELOG: Klingon and the variant inheritance go under Upcoming. 2026-08-15 00:39:53 +03:00
Lauri Ojansivu 3b5f2b24cc Klingon from its own lexicon, and every regional variant inherits its language.
tlh.i18n.json was not untranslated - it was written in French and German:
"Tableau", "Karte", "Liste", "Löschen", "Board exportieren". The words are
looked up now, in the boQwI' lexicon (github.com/De7vID/klingon-assistant-data,
the Klingon Language Institute's own data): 'echlet is a board, 'echletHom a
card, tetlh a list, per a label, chuD a member, mI' a number, Dotlh a status,
yer a domain, mIllogh a picture. The actions are imperatives, which is what a
menu entry is in Klingon - yISuq (acquire it) for import, yIngeH (send it) for
export, yIQaw' (destroy it) for delete - so "Export list" is tetlh yIngeH and
"Import card" is 'echletHom yISuq.

A regional variant that had not translated a string carried the English source
while its base language had a translation two directories away: de-AT in
English beside German, es-MX beside Spanish, zh-Hant beside Traditional
Chinese. Each variant now inherits its base for the strings it has not
translated itself, and only for those.

Three files are in another language entirely and are completed in the language
they are actually written in rather than left half-English: vl-SS is Dutch,
ace is Malay, ve is Zulu.

CLAUDE.md now says to do this for every language: look the words up in any
dictionary on the Internet, complete a file in the language it is already
written in, and add the languages WeKan does not have yet - the strings file,
the entry in languages.js with the name written in that language, and the flag
in userHeader.js, because a language missing any of the three is invisible.

Thanks to xet7 !
2026-08-15 00:34:37 +03:00
Lauri Ojansivu f1d886c3b4 CLAUDE.md: translate every language, from any dictionary, imperfect beats absent.
WeKan cannot find a volunteer for every language, and a file left in English
stays in English for years. Klingon, Volapuek, Wolof, Venda, Tamazight and the
rest are no longer exceptions: look the words up in any dictionary, word list or
Wiktionary page on the Internet and write the translation from them, and where a
file is already written in another language, complete it in that language rather
than leaving half of it English.

A bad translation beats no translation: it is readable, it is obviously
improvable, and it invites the person who speaks the language to fix it, which
an English placeholder never does. The merge rules already guarantee that a
human translation arriving from Transifex replaces the filled one and is never
overwritten by it.

Thanks to xet7 !
2026-08-15 00:28:24 +03:00
Lauri Ojansivu 07cea1e4de CHANGELOG: the 31 newly translated strings go under Upcoming. 2026-08-15 00:17:03 +03:00
Lauri Ojansivu f12095efab Translate the 31 new strings into 127 languages.
Everything added to en.i18n.json since 10.91 - the new popup titles, the export
and import wording, the card-number search operator, the filesystem integrity
report - shipped as English placeholders in every other language file.
Translated directly, as CLAUDE.md requires: no external translation service,
API or key, and each language's own existing strings as the reference, so the
new wording matches what that file already says.

The four import titles are the four export titles with that language's own word
for importing, because in a menu that already says what it is about they are
the same sentence pointed the other way. operator-number is not a label but a
word somebody types into the search box beside operator-board and
operator-list, so it is that language's plain word for a number, in the form
those two are already in.

Nothing overwrote a human translation: the fill step writes only into keys that
are still the English source, and a translation already committed for a key was
kept. None of this is pushed to Transifex, so a filled string can never
masquerade there as a human one.

Fifteen languages are left as English placeholders rather than guessed at:
Klingon, Volapük, Acehnese, Breton, Igbo, Uyghur, Venda and its two variants,
Walloon and its variant, Wolof, Yoruba, Tamazight, and vl-SS. A wrong
translation reads as though somebody meant it; an English placeholder says
plainly that nobody has translated it yet.

Thanks to xet7 !
2026-08-15 00:17:03 +03:00
Lauri Ojansivu 8ff33a5d85 CHANGELOG: the Export and Import rows in every board menu go under Upcoming. 2026-08-14 18:51:51 +03:00
Lauri Ojansivu e948acfdd3 Every menu on a board has an Export row and an Import row.
The card, list, swimlane and board menus each had one row, and each named
itself differently: "Export card", "Export list / Import", "Export swimlane /
Import", "Export board". Two of them mentioned an import that was a section
inside the popup rather than a thing to go to, and the card and board menus did
not mention it at all - so importing into a list was discoverable and importing
into a card was not.

Each of the four now has Export and Import, in that order, named for what they
do and not for what they act on: the menu already says whether it is a card, a
list, a swimlane or the board.

Both rows open the same popup - the same panes, the same table of formats, the
same selection - with one difference: the second pane offers the formats to
write out, or the file to read in. The selection means the same thing in both
directions, which is why it is the pane they share.

Import writes, so it is offered only to somebody who may change the board. That
question is asked once, in one function registered as a helper the four menus
use, and asked again inside the popup, because a row that is merely hidden is
not a permission check.

Thanks to xet7 !
2026-08-14 18:51:51 +03:00
Lauri Ojansivu cd5571b080 CHANGELOG: the combined export popup goes under Upcoming. 2026-08-14 18:44:36 +03:00
Lauri Ojansivu df2913744e Every export popup is one popup with one scope, and looks like it.
There were two export menus. The board popup wrote nineteen formats of its own
under four subheadings, in two panes across the width of the window; the
swimlane, list, card and checklist popups wrote five in one narrow column with
no panes. So "the export popup" looked like two different features depending on
which menu opened it, and a format added to one was missing from the other.

They are one template now, drawing one table of formats. Each entry says what
it is - an icon, a label, and either a path to download or a class to click -
and which scopes it belongs to: BOARD_ONLY for the ones that only make sense
for a whole board (the HTML archive, the dependency graph, the CSV columns and
the exports written for Trello, Jira, Kanboard and the rest), and nothing at
all for the ones that work anywhere, because exportUrl already carries the
scope. The board popup is the same one-line include the other four are, with no
markup and no URL builders of its own.

All five get the board's panel: what to include in one pane and what to export
it to in the other, pinned to the viewport so the header and its X are always
reachable. The layout is written against the panes' own class rather than
against one popup's name, so it cannot be true of one menu and not another.

Which scope a popup is, is asked in one place - and every scope is named there,
because a scope left out would be read as a whole board and offered a board's
formats.

Thanks to xet7 !
2026-08-14 18:44:35 +03:00
Lauri Ojansivu 48c1c46ed3 CHANGELOG: the custom colour in all three pickers goes under Upcoming. 2026-08-14 18:28:57 +03:00
Lauri Ojansivu 024036f4ee Custom colour is offered in Member Settings and the Admin Panel too.
All three places render the same picker, so the Custom color wheel was already
written for all three - it was never reachable in two of them. It appeared only
once a named theme had been selected, and a board always has a colour whose
first entry is flat, so Board Settings always showed it; Member Settings /
Change Color and Admin Panel / Settings / Visibility open on "Default (no
override)" with nothing selected, so both looked as though they had no custom
colour at all.

The wheel is offered from that state too, as the flat category's single colour,
and choosing one applies it over the first flat theme. That fallback was
already in the code that saves; it is written into the picker's own selection
as well, because otherwise the wheel would store a theme the page did not show
as chosen and the next click would read the selection back as none.

Picking a clear theme first still gives the two wheels its colour slide needs.
One helper answers which category's custom colours the picker is offering, so
the wheel, the preview and what gets saved cannot disagree.

Thanks to xet7 !
2026-08-14 18:28:57 +03:00
Lauri Ojansivu c1e8586bfd CHANGELOG: the removed text background colour goes under Upcoming. 2026-08-14 18:20:32 +03:00
Lauri Ojansivu b77a1663d8 Remove Member Settings / Font / Text background color.
A colour painted behind the text needs elements to sit on, and neither choice
of them looks good. On the boxes - which is what it did, from <body> - it
painted the whole window: the board canvas and the empty space under the lists,
which is a page background and not a text background. On the elements that
actually carry text it striped every heading, menu row and paragraph with a
full-width band. A setting nobody can make look good is worse than no setting.

The wheel and its Unset button are gone from the popup, nothing reads
profile.uiTextBgColor, and no stylesheet rule paints it. A profile that already
has a colour is cleaned rather than left dormant: setUiColors unsets the field
on every call, whatever it is passed - and it still accepts the argument, so an
older client cannot fail against a newer server. The schema key stays declared
for exactly one reason: a modifier touching a key SimpleSchema does not know is
rejected, which would leave those profiles the only ones that could not be
cleaned.

The text colour beside it stays.

Thanks to xet7 !
2026-08-14 18:20:31 +03:00
Lauri Ojansivu b19586eb72 CHANGELOG: the two Font colour fixes go under Upcoming. 2026-08-14 18:12:42 +03:00
Lauri Ojansivu c56b556538 Text colour reaches all the text, and text background stops painting the page.
The two colour choices under Member Settings / Font had the opposite faults.

Text colour reached too little. Colour is inherited, and it was set on <body>
and the form controls, so it only ever reached text that had no colour of its
own - and WeKan gives most of its text one: the header bar's buttons, the left
menu's rows, a minicard's title, a list header. Choosing green recoloured the
page heading and the menu and left the rest as it was. It is set on every
element now. .fa is left out, because those are Font Awesome glyphs rather than
text - a red alert and a green tick mean something by being that colour - and
the icons meant to follow their label already say color: inherit, so they
follow this anyway.

Text background reached too much. It was on <body>, so choosing orange painted
the whole window orange - the board canvas, the empty space under the lists -
which is a page background and not a text background. It is a marker pen now:
it goes on the elements that carry text (span, a, p, a heading, a list item, a
table cell, a label, a viewer, and the form controls) and never on the boxes
that carry elements.

Thanks to xet7 !
2026-08-14 18:12:42 +03:00
Lauri Ojansivu acdde1e1f0 CHANGELOG: the font-size setting reaching all text goes under Upcoming. 2026-08-14 18:06:10 +03:00
Lauri Ojansivu d5b69137cc Font size setting moves all of the text, not the part written in rem.
Picking Largest grew the page heading and the minicards and left the header
bar, the left menu, the lists, the swimlane header and every popup exactly as
they were.

The preset was a percentage on the root element, and a root percentage only
reaches text whose size is written in a relative unit: rem is measured against
the root, em against its parent. WeKan writes most of its sizes in px, and px
is absolute, so exactly the rem-sized parts scaled. The base rule made it
worse: html, body, input, select, textarea, button re-stated font: 14px, so the
body took the stock size straight back off the scaled root.

The preset is published as a number as well (--wekan-ui-font-scale), and every
px font-size and line-height in the client stylesheets - 433 declarations
across 37 files - is written calc(14px * var(--wekan-ui-font-scale, 1)).
line-height scales with the size on purpose: 21px of type in an 18px line box
is the same setting half-applied. The base font rule is split into font-family
and font-size, because the shorthand's size was the one thing the setting had
to be able to move.

Nothing renders differently until a preset is chosen: the fallback is 1 and the
variable is removed for Default. tests/uiFonts.test.cjs fails on any bare px
font-size or line-height, so a new one cannot quietly opt out of the setting;
the two density guards unwrap the calc as they read the file, so each still
pins the size it means.

Thanks to xet7 !
2026-08-14 18:06:10 +03:00
Lauri Ojansivu ec62581af0 CHANGELOG: the board tile theming and the theme fixes go under Upcoming. 2026-08-14 17:52:48 +03:00
Lauri Ojansivu 05ddc83cb7 Board tiles carry the whole theme, and Clean Light is readable on it.
A board on the clearpink theme was a pink rectangle floating inside a grey
tile, while the flat-coloured board beside it filled its tile edge to edge.

Two lists decided what a tile is coloured with. boardColors.css painted
.board-list .board-color-<name> a - the link inside the tile, which sits inside
the tile's own 24px/18px padding and so can never reach its edges - and
boardsList.css carried a hand-copied list of seventeen flat background colours
on the tile itself, which is what made the flat themes look right. The five
colour slide themes added later were never copied into that list, so their
tiles kept the default grey; clearblue was in it, flattened to one hex, so it
did not show its slide either; and cleandark and cleanlight were in neither.
Each theme now paints .board-list li.board-color-<name> where it paints its
header bar and its Public Boards row, and the copy is gone.

A tile writes its title in #f6f6f6, which is invisible on a light one: Apple
Glass Pastel's tile is a pastel wash from #f6f7fb, so its name and description
were white on near-white. Both light themes write their title, description and
archive line in their own dark ink and darken the card-count pill and the
unstarred star with them, in one block named as the place a third light theme
goes.

In Clean Light's top bar, header.css paints every icon white by id. The buttons
inside a ul li escaped that through a more specific color: inherit; the house
and the notification bell are in no ul li and stayed white on #F1F1F3. Both are
dark now, and the bar's labels move from 0.5 to the same 0.85, so there is one
shade in it instead of three. The current entry, which used to be the dark one
among pale ones, is the full-black heavier one instead. An unread bell keeps
its white glyph on the red circle.

Thanks to xet7 !
2026-08-14 17:52:48 +03:00
Lauri Ojansivu b7c6855201 CHANGELOG: the import page's checkboxes, columns and buttons go under Upcoming. 2026-08-14 16:24:27 +03:00
Lauri Ojansivu 5d68d94bb7 Import page: the app's own checkboxes, two columns, and both buttons alike.
The pop-over that opens the page said "Import board (Trello, Jira, WeKan
export, CSV, Excel, ...)" - five of the fourteen sources, in a menu whose only
job is to reach the page that lists all of them. It says "Import board" now.

"Import from:" and "Select what to include:" drew a grey fa-check per row, the
same shape chosen or not. Both use .materialCheckBox, the animated green
checkbox Admin Panel / Announcement uses, and the rows sit in two columns when
the window has room for two 280px ones. The row declares no alignment of its
own: the tick is a rotated shape with negative offsets written for the plain
flow, and an align-items on top of it puts the tick on the first word of the
label. The gap to the label is spelled out for both states, since the checked
box is narrower and shifted left.

"Import without mapping members (map later)" is a primary button like the
Import beside it, instead of the one unstyled button on the page.

Thanks to xet7 !
2026-08-14 16:24:27 +03:00
Lauri Ojansivu 923f84f1d5 CHANGELOG: point the page-sidebar controls entry at its commit.
Thanks to xet7 !
2026-08-14 16:02:34 +03:00
Lauri Ojansivu 9ef05970d3 Page sidebar: its controls are rows, not three links run together.
Rules, My Cards, Due Cards and Global Search each keep their controls in the
shared page sidebar, and each draws them as `.board-header-btn` - which is what
they were when they lived in that page's own second header bar.

Every rule for that class is scoped to `#header-quick-access` (header.css), so
inside the sidebar they had no styling at all. On the Rules page that is three
bare links jammed onto one line - "← Board / Workflow view / Import export" -
with the icons run into the words and nothing to click but the text.

They are rows now: one per line, the width of the panel, padding to click
against, a hover, and the icon in a fixed 16px column so a row with a caret and
a row without one still start their words at the same x. That is the shape the
board sidebar's own rows have. Both sidebars built on this shell get it - All
Boards' as well as the page one - because both had the same problem for the
same reason.

tests/pageSidebarControls.test.cjs pins it, including that this is the class
the pages actually draw rather than a new one invented for the sidebar.

Thanks to xet7 !
2026-08-14 16:02:34 +03:00
Lauri Ojansivu d6e6e9a087 Show on Card and Show on Minicard: wide, and in columns.
Two dozen settings in one column is a list nobody sees the end of: the popup
opened and the answer to "is Description on?" was below the fold.

With one of the two checkbox columns hidden, each row is half as wide as it was
- so the width is there to be used. Both popups are min(90vw, 900px) now, and
the FORM is the grid: each row is one of its cells, keeping its own
"checkbox | name" grid inside, and the cells flow into as many columns as fit.
One on a narrow window, three or four on a wide one, without a media query per
size. The heading row spans the whole width, because it names the columns of a
ROW - "Show on Card", "Description" - not of the outer grid.

Desktop only: below 800px every popup is a full-screen sheet, where one column
is the right answer. The clamp that places a popup knows the 900 as well, or one
opened near the right edge would land half off screen.

tests/showOnCardMenus.test.cjs pins the width, the columns and the heading span.

Thanks to xet7 !
2026-08-14 15:47:20 +03:00
Lauri Ojansivu 2121be5f72 CHANGELOG: point the minicard label and sticker entries at their commit.
Thanks to xet7 !
2026-08-14 15:35:46 +03:00
Lauri Ojansivu f525b391a6 Opened card: the header row is the drag bar, and a label click stays at the label.
Dragging the opened card from the right half of its title did nothing with drag
handles off, and the reason is what a heading IS: only as wide as its own text.
So "the trailing half of the title" was a few pixels on a short title, and the
empty space beside it - the obvious place to take hold of - belonged to the
header row, which had no handler.

The row is the drag surface now, and the heading fills it, so half the title is
half the bar however long the title happens to be. The handler steps aside for
three things: the buttons in that row (they are links), the drag handle when
handles are on (it has its own handler, and both would move the window twice),
and the half that edits.

Clicking a label on a minicard opened the labels popup AND the card details
behind it - the click reached the minicard too, so one click did two things and
the one nobody asked for was underneath. It stops at the label now, and which
label was clicked is read from the event rather than from `:hover`, which
answers about the pointer and on a touch screen can still be true for whatever
was tapped last.

The stickers popup is as wide as the colour pickers, and its picker is a grid
that fits as many columns as the width allows: a hundred and fifty icons eight
to a row was a column taller than the screen.

Thanks to xet7 !
2026-08-14 15:35:46 +03:00
Lauri Ojansivu f4789e6810 Titles: the leading half edits, the trailing half drags.
With drag handles OFF a card is dragged by its own body, so a title that is
entirely an edit target leaves nowhere on that line to take hold of: a grab that
moves a few pixels is a click, and the editor opens instead of the card moving.

So the edit target is the LEADING half of the title - left in English, right in
Arabic, from one `inset-inline-start` - and the trailing half is there to drag
from. It is an overlay rather than a split of the text, because the text is a
`.viewer` and cutting it in two would cut the sentence in two; dragging still
works over the zone as well, since jQuery UI sortable's handle is the whole
minicard. What the zone decides is where a CLICK edits.

With drag handles ON the handle is the only drag source, so nothing has to be
reserved and the whole title edits again - one rule keyed off the same class the
handle is.

The opened card's title splits the same way, and its own drag handle now appears
only when drag handles are on: with them off the title bar is what moves the
window, and a handle would be a second way to do what the bar already does. The
window-drag handler steps aside for the edit zone, so the two do not fight over
one heading.

Thanks to xet7 !
2026-08-14 15:27:22 +03:00
Lauri Ojansivu 590ccf3e81 build.sh: the test database moves off a port another program owns.
The next run stopped where the previous one had died, and the port says why:
127.0.0.1:3001 on this machine is an "Omi Server" answering HTTP.

  $ printf 'GET / HTTP/1.0\r\n\r\n' >&3
  HTTP/1.1 200 OK
  <title>Omi Server - Repositories</title>

That is what the old check called "the MongoDB already listening on :3001", and
what the test server then died against with "Topology is closed". The ping added
in the previous commit catches it - but stopping is not the answer either, since
the port belongs to a service the machine is entitled to run.

So 3001 is a default, not a requirement: when it is taken by something that does
not answer a MongoDB ping, the run moves the test database to the first free
port of 3011, 3021, 3031, 3041, 3051 and TELLS the tests where it went. They
already read WEKAN_MONGO_URL (tests/playwright/helpers/db.js), which is now
exported, and the test server is started against the same URL rather than a
hard-coded one. WEKAN_TEST_DB_PORT picks the port by hand; with all the
fallbacks taken, the run says which it tried and stops.

The readiness wait for a mongod this script starts asks the same question now -
a port that opens is not a database that answers, and waiting for the first is
how a run gets to "Topology is closed" ten minutes later.

Thanks to xet7 !
2026-08-14 15:12:50 +03:00
Lauri Ojansivu 92650b782e build.sh: reuse the test database only when it answers, not when the port is open.
The 2026-08-14 run reported "WeKan tests FAILED" while the node suites (428),
mocha (518) and the import regression all passed. What failed was the test
server, on its first query:

  MongoTopologyClosedError: Topology is closed
  [An error occurred when creating an index for collection "users: Topology is closed]

There was no wekan-test-mongod.log in the run directory, which says the rest:
the script took its "reuse the MongoDB already listening on :3001" branch and
started no mongod of its own. That branch asked only whether something accepted
a TCP connection - and a mongod that is shutting down, which this same script
kills a few lines earlier on its way in, accepts for a moment longer.

It asks the database now: a `ping` through the driver already in the built
bundle, so nothing is installed for it and a dead port is refused in three
seconds rather than after a minute of server-selection. A port that is open but
does not answer is an error with the command to free it, because we cannot start
our own mongod on a taken port and starting the server against nothing is the
failure this came from.

tests/buildDevServer.test.cjs pins both halves.

Thanks to xet7 !
2026-08-14 14:58:11 +03:00
Lauri Ojansivu c31c4f9adc CHANGELOG: point the release-upload entry at its commit.
Thanks to xet7 !
2026-08-14 14:15:25 +03:00
Lauri Ojansivu 38c6ee29fd Release: one snap listed twice is why three architectures fell off v10.91.
The v10.91 release run failed in four jobs. Three of them - s390x, ppc64el and
riscv64 - had done all the work: each snap built on Launchpad and each was
published to the Snap Store. What failed was attaching it to the GitHub Release:

  HTTP 404: Not Found (https://uploads.github.com/repos/wekan/wekan/releases/
  370103352/assets?label=&name=wekan_10.91_s390x.snap)

The step collected the file with two patterns, `wekan_${VERSION}_<arch>*.snap`
and `*_<arch>.snap`, and a snap called `wekan_10.91_s390x.snap` matches both -
so the same path went to `gh release upload --clobber` twice. Asked to attach
one name twice, it deletes the asset it has just uploaded and then 404s on it.
The two other places in the same job that build this list were already piped
through `sort -u`; this one was missed.

It is deduplicated now, and the attach is confirmed from the other side the way
the native snap job already did: read the release's assets back and fail if this
snap is not among them. An upload that reports success and leaves nothing behind
is the failure nobody notices until a download 404s - and every job that lost an
architecture had already said the snap was built and published.

The fourth failure, armhf, is not this: snapcraft died with SSLEOFError while
downloading the build log from Launchpad, on all three attempts, and produced no
snap.

tests/releaseSnapArches.test.cjs now requires every snap list to be
deduplicated, and the attach step to read the assets back.

Thanks to xet7 !
2026-08-14 14:15:25 +03:00
Lauri Ojansivu d36df4c245 CHANGELOG: wrap the container wait-page paragraph at 80 columns.
Thanks to xet7 !
2026-08-14 14:06:54 +03:00
Lauri Ojansivu 01b12d8852 CHANGELOG: point the container wait-page entry at its commit.
Thanks to xet7 !
2026-08-14 14:05:37 +03:00
Lauri Ojansivu b4499b0ddc Docker: wait for the database behind a page, not behind a closed port.
"We upgraded to 10.91 ... Gateway timeout appears" - from the same reporter as
#6592, whose fix shipped in 10.91 and could not reach them: that fix is the
snap's, and this is a container.

WeKan does not open its web port until the database answers, and in a container
nothing else was listening while it waited, so a reverse proxy in front returned
a gateway timeout. That is the same symptom for two completely different faults:
WeKan is broken, or the database has simply not come up yet.

The entrypoint now asks whether the database answers - one ping, with the driver
already in the bundle (releases/ferretdb/db-ready.mjs) - and while it does not,
it serves the bridge page the recovery case already uses, taught a second reason:
"WeKan is waiting for its database", with where to look, as a 503 that refreshes
itself.

What is bounded is the PAGE, not the wait. A database can take minutes to come
up after an update and giving up on it would be worse than waiting, so when the
window ends the page stops, WeKan starts, and WeKan keeps waiting exactly as it
did before. WEKAN_DB_WAIT_PAGE=false turns it off; WEKAN_DB_WAIT_MAX_SECONDS
sets the window. The page is killed and reaped before WeKan binds the port,
because two servers cannot hold one.

tests/dockerWaitsBehindPage.test.cjs pins that, the probe writing nothing, and
that the recovery reason still says recovery.

Thanks to Alishara and xet7 !

Fixes #6595,
2026-08-14 14:05:37 +03:00
Lauri Ojansivu 0b9bca6b87 CHANGELOG: point the index entry at its commit.
Thanks to xet7 !
2026-08-14 13:58:44 +03:00
Lauri Ojansivu 569dd4af54 Index the collections a card is made of.
Reported by email against 10.91: "Still slow on loading cards. Test server with
me as only user." One user is the part that says what it is not: with nobody
else on the server there is no contention, no queue and no lock, so the time is
going into the queries themselves.

It was. cards, activities, cardComments, checklists and checklistItems - every
collection a board draws and an opened card pulls in - had NO index at all.
"The cards of this list", "the comments of this card", "the newest activities of
this board" each walked the whole collection. That is invisible on a demo board
and expensive on a real one, and worse on FerretDB, whose SQLite backend has to
walk the same documents. The activities publication even explains that it keeps
its selector flat so it can push down to FerretDB's index; there was no index
for it to push down to.

Each index matches a selector the app really makes WITH the sort it really uses
- boardId+archived, listId+sort, swimlaneId+sort, parentId, boardId+cardNumber
for cards; cardId+createdAt and boardId+createdAt for activities and comments;
cardId+sort and checklistId+sort for the checklists - because an index on the
filter alone still leaves an in-memory sort of everything it matched, which on a
board with a year of history is the slow half.

They go through the same idempotent ensureIndex every other index here uses: it
waits for the replica-set primary, does nothing when the index is already there,
and logs a backend that refuses one instead of stopping the server.

tests/cardQueryIndexes.test.cjs pins the set, and that each one carries the sort
as well as the filter.

Thanks to xet7 !
2026-08-14 13:58:44 +03:00
Lauri Ojansivu ca662c737c CHANGELOG: point the attachment and snap database entries at their commits.
Thanks to xet7 !
2026-08-14 13:50:20 +03:00
Lauri Ojansivu 804004e9e8 Snap: comparing the two databases reads the one that is already running.
`snap run wekan.database-compare` answered "MongoDB: <unreadable>" and
"FerretDB: <unreadable>" on a live instance whose wekan.sqlite was 85 MB and
whose site was up at the time.

FerretDB was unreadable BECAUSE it was up: SQLite has one writer, so the second
FerretDB the comparison started against the same directory did not get the data.
The tool then reported the database it had been talking to all along as
unreadable, and refused to choose.

The live one is asked first now - `db-eval evidence` only counts and sorts - and
a second copy is started only for a database that is not running. Both speak the
MongoDB wire protocol on the same port, so which one is answering is asked
rather than assumed: `db-eval kind` reads buildInfo, where FerretDB names itself
and a mongod does not. Reading one as the other would be a wrong answer given
with confidence, which is worse than "unreadable".

database-compare is autopick with --dry-run, so the automatic choice at startup
and the command an admin runs by hand both get it.

tests/snapCompareReadsLiveDatabase.test.cjs pins all of it.

Thanks to waltermhl and xet7 !

#6583 comment 5291730920
2026-08-14 13:50:19 +03:00
Lauri Ojansivu 2ff21ac962 Snap: MongoDB files no reader can open are not compared with FerretDB.
An instance whose MongoDB data was written by a MongoDB older than any mongod
this snap carries printed, at every start and before the site came up:

  Database selection: setting='ferretdb' ferretdb_has_data=true mongodb_has_data=true
  WeKan: BOTH databases have been written to since the migration.
  [autopick] reading both databases to see which one holds the work ...

`mongodb_has_data` answers "are the files there", not "can anything here open
them", and on that instance those are different answers - the same issue shows
mongod 7, 5.0 and 4.2 each refusing the files in turn. So the comparison ran on
a copy that cannot be served, and it is not a cheap question: it starts a mongod
that cannot open the data, and then a second FerretDB against the SQLite
directory the running one already holds.

migration-control has already tried every reader in the snap and left
.mongodb-data-too-old behind when none of them could open the files. With that
marker present the MongoDB copy is not a candidate for holding the newest work,
so both comparison branches skip it and WeKan serves the FerretDB that has the
data. The empty-FerretDB safety still asks about the files being there, because
that one is not a comparison.

Nothing is deleted and the marker stays: it is true, and it is right again on a
snap that can read those files.

tests/snapUnreadableMongoNotCompared.test.cjs pins which question each branch
asks.

Thanks to mueschel and xet7 !

#6585 comment 5290402146
2026-08-14 13:50:19 +03:00
Lauri Ojansivu 146a5184d9 Attachment migration: keep a binary two records share, and name a missing one.
Moving attachments from MongoDB CollectionFS to File-System stopped on some of
them with "FileNotFound: file 66336fc372e64200010f4832 was not found", and the
reporter had it exactly: it is the identical files.

A CollectionFS filerecord points at its binary by copies.<coll>.key, the id of a
file in the cfs_gridfs.<coll> bucket, and two filerecords can carry the SAME key
- the same file attached twice, or a board copied with its attachments. The
migration deleted the binary as soon as it had moved the first of them, so the
second read a file that was no longer there.

The binary now goes only when no other filerecord still names it, asked after
the record itself is deleted so it cannot count itself. When that question
cannot be asked at all, the binary stays: a file left behind can be removed
later, an attachment deleted out from under another record cannot be brought
back.

A binary that really is missing - metadata restored without the chunks - is no
longer a MongoDB stack trace naming a GridFS id. It names the attachment, says
where it was looked for, and is counted as SKIPPED rather than failed, because
there was nothing to move; the Admin Panel's summary line shows both counts, so
a run that leaves attachments behind cannot look like one that moved
everything.

tests/collectionFsSharedBinary.test.cjs pins the shared-binary check, the order
it is asked in, and that a skipped record is not a failure.

Thanks to sbruckmueller and xet7 !

Fixes #6596,
2026-08-14 13:49:59 +03:00
Lauri Ojansivu e612fdfee9 Custom fields: everything about them on the section that shows them.
They were spread over three places. Board Settings had the board's list of
fields behind a right-sidebar view; the card menu had an entry for that list and
another for the picker of which fields are on this card; and the picker had a
cog that jumped back to the sidebar, closing the menu and the card pane on the
way.

One place now: the hamburger at the end of the card's own Custom Fields
heading. It opens every field the board has, ticked when it is on this card,
with a pencil each and, under a rule, "Add custom field". Edit and Add open in
the same pop-over on top of that list, so the back arrow returns to it and the
card stays open behind, and they are the board's own forms rather than second
copies of them.

Gone with it: the card menu's Custom Fields entry, the Board Settings row, the
wrapper popup that nothing opened any more, and the styles that existed only to
make a sidebar list look right inside a pop-over. The sidebar view template
stays where it is, with a note that nothing opens it.

tests/customFieldsSectionMenu.test.cjs pins the popup's three parts, that the
forms are the shared ones, and that none of the three old places came back.

Thanks to xet7 !
2026-08-14 08:20:30 +03:00
Lauri Ojansivu 18076c138d Subtasks Settings: a hamburger on the Subtasks heading, and gone from Board Settings.
What subtasks do on a board - whether they are shown at all, which board and
list a new one is deposited in, and how a parent is named on a minicard - is
behind a hamburger at the end of the Subtasks heading on an opened card, where
the card and the minicard already keep theirs. It opens the same popup Board
Settings did, and Board Settings no longer has the entry.

The heading is the control that folds the section, so the hamburger's click
stops there: without that, opening the menu would close the thing it was opened
from.

The hamburger is a `menuClass` on the shared section header, so any other
section can be given one, and it is pushed to the end of the row by
`margin-inline-start: auto` - a logical margin, so a right-to-left card needs no
second rule. A subtask's own menu is about the subtask again: the settings
belong to the section, not to one subtask of it.

tests/subtaskSettingsMenu.test.cjs pins all of it, including that the click
does not reach the heading.

Thanks to xet7 !
2026-08-14 08:12:03 +03:00
Lauri Ojansivu 82db0800ef Move/Copy selection and Move/Copy swimlane: one dialog each, not two.
Both pairs duplicated their whole component, not only their markup. The
selection dialog was 152 lines twice over, and 145 of them were the same: the
four reactive selections, the four selects' helpers, the change handlers and
the sort-index maths. The seven that differed are what each does to a card once
the destination is known - move it, or copy it and move the copy. That is now
`applyToCard`, passed to one registration.

The swimlane pair is the same story with one difference instead of seven: the
method called on Done.

The markup of both is one template too - selectionDestinationPicker and
swimlaneDestinationPicker - handed the popup's own instance as `dialog`,
because a helper is looked up on the template it is written in. What the two
copies of each really did differ in was the ids their labels point at, so those
are passed in: `idSuffix` for the radios, `titleId` for the title field, which
takes it as an `id=` attribute since a literal id cannot hold a mustache.

sidebarFilters.js lost 106 lines, sidebarFilters.jade 17 and swimlanes.jade 10.
A scan for near-duplicate templates is at 7 pairs, from 74 before this work
started. tests/sharedFormTemplates.test.cjs covers both, including that move
and copy still do different things and that a label still clicks its own
control.

Thanks to xet7 !
2026-08-14 07:34:13 +03:00
Lauri Ojansivu d6df742f56 Card menu: Custom Fields is one entry, and Board Settings has none.
The menu had two: the board's LIST of custom fields and the picker for which of
them are on this card, one above the other. That puts the general question
above the particular one and makes the menu ask which you want before you have
seen either.

One entry now - the picker, with the icon and the place the picker's entry had,
named "Custom Fields". The board's list is behind that popup's own Settings
cog, which is where you look once you have seen the fields and want to add one,
and it opens as a pop-over on top of the picker rather than sending the reader
to the right sidebar: the back arrow returns to the fields, and the card stays
open behind them.

Board Settings loses its Custom Fields row with it. The fields are where the
cards are, and a third way in is a third place to look.

Thanks to xet7 !
2026-08-14 07:09:11 +03:00
Lauri Ojansivu f4b4068c8c CHANGELOG: point the shared-form entry at its commit.
Thanks to xet7 !
2026-08-14 07:04:28 +03:00
Lauri Ojansivu 53c3123de5 Three more forms written once instead of ten times over.
Three shapes were copied, and in each the JavaScript was already one piece
while the markup was not - so a behaviour change was one edit and a markup
change was four:

- where a card goes (board, swimlane, list, above or below which card, Done),
  drawn four times: Move card, Copy card, Copy checklist to many cards, Convert
  checklist item to card;
- where a list goes, drawn twice: Copy list and Move list;
- the Create Board form, drawn four times: the one on All Boards and three
  popups, one of which creates a template board and says so with a Session flag
  rather than with different markup.

Each is one template now - cardDestinationPicker, listDestinationPicker,
createBoardForm - and what it shows is passed in, because a helper is looked up
on the template it is written in and not on the one including it. The two
pickers are handed the dialog and keep it on their own INSTANCE: inside `each
boards` the data context is a board, so a helper reaching into the context for
it would find nothing there.

The events stay with the popups. An event inside an included template bubbles
to the one that includes it, which is the one holding the state - that is what
lets four popups do four different things with one form.

cardDetails.jade lost 71 lines, boardHeader.jade 58 and listHeader.jade 10, and
a scan for near-duplicate templates went from 74 pairs to 9.
tests/sharedFormTemplates.test.cjs pins all three, including that the
template-board popup still creates template boards.

Thanks to xet7 !
2026-08-14 07:04:28 +03:00
Lauri Ojansivu 1c3f72acf8 CHANGELOG: point the Subtasks Settings entry at its commit.
Thanks to xet7 !
2026-08-14 06:52:56 +03:00
Lauri Ojansivu ec8ba58753 Subtasks Settings is in the menu of the subtask it is about.
What subtasks do on a board - whether they are shown at all, which board and
list a new one is deposited in, and how a parent is named on a minicard - was
in Board Settings only: open the sidebar, open the board menu, find Subtasks
Settings. Somebody who wants to change it is usually looking at a subtask on an
opened card at that moment.

It is at the bottom of that subtask's own menu now, under a rule, below the
things the menu does to the subtask itself. A board admin's, as it is in Board
Settings, whose entry is unchanged.

The settings are their own template with their own state, helpers and handlers,
included by both places, so neither needs code of its own and the two cannot
drift. That is what the move needed: a template included in two places cannot
reach its parent's helpers, because a helper is looked up on the template it is
written in.

tests/subtaskSettingsMenu.test.cjs pins the placement, the rule above it, the
permission, and that nothing was left behind on the popup.

Thanks to xet7 !
2026-08-14 06:52:56 +03:00
Lauri Ojansivu 9faf63fced CHANGELOG: point the date-template entry at its commit.
Thanks to xet7 !
2026-08-14 06:48:48 +03:00
Lauri Ojansivu 9c288c3d4d Date templates: one edit form and one badge instead of twenty-two copies.
Two shapes were copied across three files. The edit-a-date form - date, time,
Save, Delete - existed eight times, letter for letter: the card's Received,
Start, Due and End, a vote's end date, a planning poker's end date, a date
custom field, and a ninth `datepicker` template that nothing included and no
popup could open (a popup's template name ends in `Popup`). The date badge -
the coloured date on a card and on a minicard - existed fourteen times.

The JavaScript was already shared: client/lib/datepicker.js holds the state and
the handlers, and each popup differs only in the field it stores. It was only
the markup, so a change to the form meant eight edits and a change to the badge
meant fourteen, with nothing to say so.

Each is one template now - editDateForm and dateBadgeBody. They take what they
draw as ARGUMENTS, because a helper is looked up on the template it is written
in and not on the one including it: that is what lets one piece of markup serve
them all while every popup keeps its own state, its own click and its own name.
cardDate.jade went from 289 lines to 91, and the dead datepicker.js went with
the template it wired up.

The badge's `baseClass` is the trap this had to avoid: three of the fourteen -
the custom-field dates - were never `.card-date` and must not become one, so
the class each caller carried is passed in rather than baked into the shared
markup. tests/dateTemplates.test.cjs pins that, the minicard kind classes, and
that no state moved.

Thanks to xet7 !
2026-08-14 06:48:47 +03:00
Lauri Ojansivu 1f9848920b CHANGELOG: point the popup-title entry at its commit.
Thanks to xet7 !
2026-08-14 06:37:24 +03:00
Lauri Ojansivu 47099b8baa Popups: the ten that had no header now have a title, like every other one.
A pop-over draws its header from its title, and with no title it renders as
`no-title`: no header, and so no close button and no back arrow - Escape or a
click away are the only ways out. Noticed on "Are you sure you want to delete
this background image?", which is the worst place for it.

Ten popups were in that state and are titled the way the other 151 already
were, by a `<name>Popup-title` key: Delete Background Image, Delete Duplicate
Lists, Delete Account, Add Domain, Remove Domain, Map to existing user, Export
swimlane, Export list, Export checklist, and the member popup.

The keys are in EVERY language file at the same position, as the English
placeholder a Transifex pull would produce for a key nobody has translated yet:
the files are one key order, and a key inserted in some and appended in others
makes every later diff unreadable (tests/boardItemLinks.test.cjs pins that).

The other mechanism, Popup.open(name, { titleKey }), is not a second way but
the same one pointed at a phrase the app already has - "Custom Fields", "Sort
Boards", "Show on Card" - so a word translated 147 times is not copied into a
new key that would start as English in all of them.

tests/popupTitles.test.cjs walks every popup template in the client and fails
if one resolves no title through either, so the next popup cannot ship without
a header.

Thanks to xet7 !
2026-08-14 06:37:23 +03:00
Lauri Ojansivu a3774245f5 CHANGELOG: wrap the background-upload paragraph at 80 columns.
Thanks to xet7 !
2026-08-14 06:24:21 +03:00
Lauri Ojansivu 2a50e7104c Board backgrounds: one entry, and the picture is the button.
"Board backgrounds" is gone from Board Settings. Its content - the pictures
uploaded to this board - is drawn inside Change Background Image, under the
upload that adds to them, so a second menu entry was a second place to look for
one list. The popup it opened had become a single include and went with it.

Clicking a picture, or its name, now puts that one behind the board. It was a
14px check icon in the row under an 80px picture of the thing it applies, so
the obvious click - on the picture - did nothing. That icon is gone; the row
keeps download and delete, which are the two things a picture cannot say.

Thanks to xet7 !
2026-08-14 06:23:00 +03:00
Lauri Ojansivu 4b3a37b5a2 Board background: the uploaded picture is listed, named, and applied.
An upload ran, the spinner stopped, and nothing else happened: the picture was
not in the list, and the board looked the same. Three reasons, all of them the
same shape - the upload had no way to say what it had done.

The list of what has been uploaded is its own template now and is drawn in BOTH
popups: under the upload in Change Background Image, and as the whole of Board
backgrounds. One subscription, in the template that draws it. Uploading into a
popup that then shows nothing is indistinguishable from an upload that failed.

Each picture is shown with its NAME. Two photos of the same holiday are the
same picture at 80 pixels, and after an upload nothing said which one arrived.

A finished upload puts itself behind the board. "Add background image" is asked
for by somebody who wants that picture there; an upload that only lands in a
list, with the board unchanged, reads as one that did not work. The check in
the list still switches between the pictures already uploaded, and a failed
upload sets nothing.

Thanks to xet7 !
2026-08-14 06:07:06 +03:00
Lauri Ojansivu 9035d6e829 Board background upload: a failure says so instead of vanishing.
The upload can fail before there is an uploader to listen to - a config the
collection refuses, a name its namingFunction cannot build. `insertAsync`
rejected into nothing there: the spinner stopped, no message appeared, and the
picture simply never turned up in the list. That is how the missing `fileId`
hid for as long as it did.

The insert is inside a try now: the reason lands in the popup's own warning
line and in the console, and the file input is cleared either way so the same
file can be picked again after a failure.

Thanks to xet7 !
2026-08-14 05:58:51 +03:00
Lauri Ojansivu 1386bd9ee1 CHANGELOG: point the upload and avatar entries at their commit.
Thanks to xet7 !
2026-08-14 05:47:38 +03:00
Lauri Ojansivu ac201d4b59 Upload background image works, one upload config, and an avatar with no name.
Two fixes that both came out of the same session.

"Upload background image" picked a file and nothing arrived. Two fields of an
upload's config are not optional in the way they look, and the board
background's config - written by hand - had neither:

- `fileId`, and the same id copied into `meta.fileId`. Attachments'
  namingFunction decides the name a file is STORED under, and on the client it
  reads that id out of `meta` and deletes it. Without it the name is
  `undefined`.
- `transport`. HTTP everywhere, because Meteor-Files' default DDP floods the
  WebSocket and makes Safari reconnect at ~95%; DDP on Sandstorm, whose
  http-bridge strips the x-* upload headers so every chunk comes back "Can't
  continue upload, session expired" [408].

Both come from client/lib/attachmentUploadConfig.js now, used by the card's
file picker, the card's pasted image and the board background alike - so the
next uploader gets them by asking for a config rather than by knowing two
things nothing would have told it. The two card call sites lost their
hand-written copies with it.

getInitials() ended with `this.username[0]`, so a user document that arrived
without a username threw a TypeError from a Blaze helper, mid-render. Two
helpers call it, so each avatar threw twice, and the second throw left the
SVG's viewBox half-written ("0 0  15"), which Firefox refuses outright: the
avatar drew nothing and took the rest of that render pass with it. It is total
now - initials, fullname, the first letter of a username, then an empty string -
and a fullname of only spaces no longer spells the literal word "undefined".

tests/attachmentUploadConfig.test.cjs and tests/userInitials.test.cjs pin both,
including the empty-document case that threw.

Thanks to xet7 !
2026-08-14 05:46:41 +03:00
Lauri Ojansivu a833e49174 Export board: a full-width panel, with its X always on screen.
It was anchored to the button that opens it and clamped into the viewport using
an assumed width - 1100 in popupOffset.js against `min(96vw, 1100px)` in
popup.css. The two disagreed by a few percent, so on a narrow window the
panel's trailing edge, and the pop-over's own X with it, ended up past the edge
of the screen: Escape or clicking away were the only ways to shut it.

It is a panel now, not a menu hanging off a button: pinned to the top left of
the viewport at the 10px gutter every popup is kept inside, and
`calc(100vw - 20px)` wide - the whole window, less that gutter on each side.
The header, the X and both panes are always on screen, and there is no assumed
width left to disagree with the stylesheet.

Thanks to xet7 !
2026-08-14 05:35:51 +03:00
Lauri Ojansivu 54ceb74ad7 CHANGELOG: point the board-background entry at its commit.
Thanks to xet7 !
2026-08-14 05:31:01 +03:00
Lauri Ojansivu 6a6fc25292 Board background: the upload is with the URL, and Unset sits beside Save.
Two popups of the same menu answered one question. "Change Background Image"
asked for a URL; "Board backgrounds" listed the images uploaded to this board
and carried the upload button - so the popup that reads as the place to set a
background had half of the ways to set one, and the other half was behind a
different entry.

The upload is under the URL field now: a picture from the web and a picture
from this machine, read together. "Board backgrounds" keeps what it is named
for - the images already there, to set active, download or delete. The uploader
moved as its own template with its own state and handlers, once, so there is
still one uploader; the subscription stayed with the popup that lists the
images, which is the only one that needs it.

Unset was five blank lines and a rule below the Save it belongs beside. They
are one row now, Save first, sharing the width. Unset is explicitly
type="button": inside that form a button with no type is a submit button, so
beside Save it would have saved the URL it is meant to clear.

tests/boardBackgroundPopups.test.cjs pins the layout, the type, and that
nothing about uploading is left behind or duplicated.

Thanks to xet7 !
2026-08-14 05:31:00 +03:00
Lauri Ojansivu 0461e0bed2 CHANGELOG: the header fold is its own entry, linking its own commit.
Thanks to xet7 !
2026-08-14 05:23:55 +03:00
Lauri Ojansivu 7d0396ebec Board header: the board's controls fold into one caret.
A caret leads the group, at its leading edge - left of the lock in a
left-to-right page, right of it in Arabic, because its direction comes from the
same rule every other caret here uses. Folded, the seven controls after it are
gone - Private, Muted, Sort Cards, Filter, Search, Show dependencies,
Multi-Selection - and the bar is the logo, the board's name and the caret.

The buttons a board's own Rules put in this bar are NOT folded with them:
somebody added those to this board on purpose, so they sit outside the fold and
the standard controls fold around them. It is named by the words the app
already has - Collapse and Uncollapse - so no key was added to 147 language
files to say them again, and it carries role="button" and a tabindex, so Enter
and Space work.

The sidebar's Members and Labels folds moved into the same store on the way:
`client/lib/sidebarSectionState.js` is now `client/lib/foldState.js`, with
`isFoldOpen` / `foldCaret` / `js-toggle-fold` for all three. One store, one
rule, one class - a header caret and a sidebar caret pointing different ways in
one language would be the bug this avoids.

tests/sidebarSectionCollapse.test.cjs is tests/foldSections.test.cjs and covers
both ends, including that the Rules buttons stay outside the fold.

Thanks to xet7 !
2026-08-14 05:22:31 +03:00
Lauri Ojansivu 23b15078a4 CHANGELOG: point the sidebar fold entry at its commit.
Thanks to xet7 !
2026-08-14 05:15:04 +03:00
Lauri Ojansivu 48cf269870 Board sidebar: Members and Labels fold by their headings.
The two sections at the top of the right sidebar could not be folded at all -
the People / Organizations / Teams / Domains tabs with their avatars, and every
label of the board, were always open and pushed everything under them down the
panel.

Both have a caret in front of their heading now, and the heading is the button:
the same control an opened card's sections use, and the one Activities got
below them, with its direction from the same function. Enter and Space do what
a click does.

The choice is the reader's and lasts the session: neither section decides
anything on the server, so folding one for yourself must not fold it for
everybody. Activities is the exception and stays as it is - its caret writes
board.showActivities, which also decides what the publication sends.

tests/sidebarSectionCollapse.test.cjs pins both, including that the labels are
still inside their content box rather than siblings of it, which is the shape
an indentation change like this breaks.

Thanks to xet7 !
2026-08-14 05:15:04 +03:00
Lauri Ojansivu e80009bb80 Board Settings: remove Card Settings, which the card and minicard menus now hold.
Every setting in that table is in the menu of the thing it is about: the card's
menu has "Show on Card" and a minicard's has "Show on Minicard", each with that
column of it. A board-wide entry as well is a third way to one list, and the
way furthest from what it changes.

The table itself is untouched - the two menus include it - so this removes the
row from the board menu and the handler that opened it, nothing else. The
Sandstorm and Scaling docs named the old route and now name the new one.

Thanks to xet7 !
2026-08-14 05:09:44 +03:00
Lauri Ojansivu f822d426bf Minicard: the list title is a line of its own, not another badge.
The badge row is `float: inline-start`, so the block written after it shared
its line: "Show on Minicard / List title" rendered wedged in beside the
attachment and checklist counts and read as one more badge. `clear:
inline-start` puts it where the markup already meant to put it - a small grey
line under the badges - and clears on whichever side the page starts.

Thanks to xet7 !
2026-08-14 05:05:52 +03:00
Lauri Ojansivu 6bd2816484 CHANGELOG: point the Date Format fix at its commit.
Thanks to xet7 !
2026-08-14 05:02:57 +03:00
Lauri Ojansivu 443e65b6d0 Card details: the Date Format menu is back - two collapse mechanisms folded one field.
The dropdown that chooses a card's date format disappeared and nothing brought
it back. The dates under the same heading were there; the selector above them
was not.

Two things were folding it. #1591 gave every field of an opened card a caret on
its own title and hid everything after that title when clicked, storing the
fold per card in the user's profile. The section carets that replaced it put
one handle on each section's heading - and that heading is drawn on the FIRST
field of the section, so a click on it was also a click on a field title. The
old handler ran too, folded that field, and stored it. Reopening the section
brought the other fields back but not the one the heading sits on, and the
per-field caret had been suppressed inside a group, so nothing was left to open
it.

The #1591 half is gone: the delegated handler, the caret it drew on every
title, the rule that hid the siblings, and the per-card state re-applied on
render. Every field lives in a section now and the section's heading is the
only handle. The same per-user store is still the checklists' - they key their
entries by checklist id, which is what makes a checklist folded on the card
folded on its minicard - so the store and its two model methods stay.

tests/cardSectionCollapse.test.cjs pins the select inside its section and that
no field folds on its own; tests/checklistCollapse.test.cjs keeps the half that
is still in use and says why the other half went.

Thanks to xet7 !
2026-08-14 05:02:57 +03:00
Lauri Ojansivu 1769a6bdd6 Card menu / Custom Fields opens in the menu, not in the right sidebar.
It opened the sidebar's Custom Fields view: a different part of the screen,
with the menu gone and the card pane closed behind it, and the answer to "what
fields does this board have" on the other side of the window.

It is a pop-over now, in the place the menu was, so the pop-over's own back
arrow returns to the card menu - and Create and Edit, which were popups
already, stack on top of it and come back to the list. It is the same template
the sidebar view uses, included rather than copied, so the two lists cannot
drift. What it needed was styles of its own: every rule that list has is
written for `.sidebar .sidebar-content` and none of them reach inside a
pop-over.

The board menu's Custom Fields still opens the sidebar view, unchanged.

Thanks to xet7 !
2026-08-14 04:55:59 +03:00
Lauri Ojansivu 8bdcf2a26f CHANGELOG: point the List title entry at its commit.
Thanks to xet7 !
2026-08-14 04:49:34 +03:00
Lauri Ojansivu 9d9b8ae93b Show list on minicard became the List title row of Show on Minicard.
It was a line in the card's menu reading "Show list on minicard", or "Hide list
on minicard" once it was on - a menu entry that changes its own name is the
shape a checkbox has, drawn as a sentence. It is a checkbox now, the first row
of Show on Minicard, above Received, named "List title" and unchecked by
default, which is what the field itself has always been.

It shows the name of the list the card is in on its minicard, which is worth
having where a card is read away from the column it lives in: a swimlane,
search results, My Cards. The board-wide "Show lists" row further down the same
table turns that on for every card; this one is for a single card, and either
is enough for the name to show.

It belongs to the card rather than to the board, so it is drawn only in the
minicard's own view - Board Settings has no card to act on - and only for
somebody who may change that card, which is the permission the menu entry had.
The card is passed into the popup and re-read from the collection when it is
toggled: a minicard's menu is opened from the board, where there is no current
card to fall back on.

Thanks to xet7 !
2026-08-14 04:49:33 +03:00
Lauri Ojansivu ac35762eec CHANGELOG: point the custom-fields entry at its commit.
Thanks to xet7 !
2026-08-14 04:38:55 +03:00
Lauri Ojansivu 8d0a457e68 Custom Fields and Edit custom fields are one group in the card's menu.
The board's LIST of custom fields - where a field is created, renamed or
deleted - was in Board Settings only, and the picker for which of those fields
are on this card sat down among Voting, Planning Poker and Spent time. Two
halves of one subject, three groups apart.

They are one group now, under Watch and its rule: Custom Fields first, because
a field has to exist before a card can be given it, then Edit custom fields,
then a rule like every other group in that menu. The first opens the same
sidebar view the board menu opens, asked for through the sidebar service rather
than assumed to be there, and closes the menu behind it. A board admin's, the
same as in Board Settings.

Thanks to xet7 !
2026-08-14 04:38:55 +03:00
Lauri Ojansivu 09c5764a27 CHANGELOG: point the menu-restructuring entries at their commit.
Thanks to xet7 !
2026-08-14 04:33:12 +03:00
Lauri Ojansivu f0204ae014 Show on Card and Show on Minicard in the menus of the card and the minicard.
Board Settings / Card Settings is a table of twenty-four settings in two
columns - what a card shows, and what a minicard shows - and it lived in one
place only, four clicks from the card whose fields it decides. It is now also
the first entry of the two menus it is about: the opened card's hamburger
offers Show on Card, a minicard's offers Show on Minicard, each followed by a
rule like every other group in those menus.

Both open the SAME popup with the other column hidden by a class, so there is
one list of settings and one set of handlers; a setting added to the table
appears in all three places by itself. The permission is the one Board Settings
already used - a board admin - asked in the menu and again where it acts. The
menu is opened from two places and its data context is the card either way, so
the opener records which hamburger it was in a module-level reactive value
rather than a field on the card document, which a re-render throws away.

"Hide minicard label text" left the right sidebar, where nothing else about a
minicard is, and became the "Labels text" row of the same table, directly under
Labels. It is ticked when the text is SHOWN, which is the default: the stored
flag is a "hidden" one, and reading it straight through would leave the box
unticked for the behaviour a board has out of the box. It is the one personal
row of a board-wide table, so it shows in the minicard's view only, is offered
to everybody, and a reader who is not a board admin gets that row alone.

The move found a bug: the setting was written out three times and the
minicard's own copy only ever wrote localStorage, so a logged-in user toggling
it set something nothing reads. One module reads and writes it now.

The board sidebar's Activities heading had an eye and the words "Show
activities" beside it - one setting with two controls and its own name twice.
It is a caret in front of the heading now, and the heading is the button, which
is what every collapsible section of an opened card already is. Both take the
caret's direction from one function, so it cannot point one way on a card and
another in the sidebar of the same language.

tests/showOnCardMenus.test.cjs pins all of it. The Sandstorm and Scaling docs
name the new route beside the old one, and the developer directory structure
lists the three new client/lib modules.

Thanks to xet7 !
2026-08-14 04:32:04 +03:00
Lauri Ojansivu 9c86b42eb1 CHANGELOG: point the two title-editing entries at their commit.
Thanks to xet7 !
2026-08-14 04:00:49 +03:00
Lauri Ojansivu c6ad795629 Edit a title where it is written: a card's on the board, a board's in the header bar.
A list's title has always been edited in place. A card's could only be changed
by opening the card, so fixing a typo on a board of forty cards was open, edit,
close, forty times. Clicking a minicard's title text now opens the same
inlinedForm the list heading uses - textarea, Save, X - for anybody who may
write; the complete checkbox, the linked-card icons and the card number keep
doing what they did, and the rest of the card still opens the card.

The minicard sits inside the link to the card, so a click in the open editor
would have navigated away mid-rename. That default is cancelled, except on the
Save button whose own default IS the submit. An empty save is a no-op rather
than a way to end up with a card that has nothing to click.

The board's name had a pencil beside it: two targets for one job, and the
smaller of them was the one that did it. The name itself now opens the same
boardChangeTitlePopup the pencil opened, with the board as its data context
because the header bar's context is the page. A board whose title is empty
renders no text, and a zero-width element cannot be clicked, so the clickable
title carries a minimum width and height - an empty title can still be given
one.

tests/titleClickToEdit.test.cjs pins both ends, including the two negatives:
the save button keeps its default while everything else in the editor does not,
and the empty title stays clickable.

Thanks to bonnebulle and xet7 !

Fixes #4990,
2026-08-14 03:59:38 +03:00
Lauri Ojansivu 0edbf117d3 CHANGELOG: point the restoration entry at its commit.
Thanks to xet7 !
2026-08-14 03:43:11 +03:00
Lauri Ojansivu 7a75779b2c Card details: put back what the restructuring dropped, and theme the Copy
link button.

Moving eleven sections with a script is how markup disappears without
anything failing: the file compiles, the tests pass, and a card is quietly
missing a row. Comparing the card against its state before the section work -
by counting every line of markup, not by reading - turned up four things.

Three were LOST:

- +cardButtons, the card's whole button row: a slice used it as a boundary
  and swallowed it;
- a custom field's NAME. +cardCustomField renders the value and nothing else,
  so a card with three custom fields showed three values with nothing to say
  what they were;
- the attachment count beside the Attachments heading. The section header
  takes a count now, and the board setting that governed it still does.

One was GAINED: a second, bare `+` beside the End date's add button - an icon,
not a button, from a slice that duplicated a line. That is the one the report
asked about.

Each of the four has a test now, because each was invisible to the compiler,
to the suites and to a reading of the diff.

Also: the Copy card link to clipboard button carried only `.btn`, so it fell
back to the plain grey button whose dark label is nearly unreadable on a dark
theme. It is `.primary` now - the board accent with white text - and it is
NAMED in the forms.css rules that already carry that look rather than given a
copy of them, which is what that file asks for.

Thanks to xet7 !
2026-08-14 03:43:11 +03:00
Lauri Ojansivu 25d9662fd1 CHANGELOG: point the one-row titles entry at its commit.
Thanks to xet7 !
2026-08-14 03:37:04 +03:00
Lauri Ojansivu c747d51baf Card details: the group caret is on its first field's title, one row of
titles.

Grouping the fields gave each group a header LINE of its own, and for a group
named after its first field that line repeated the word: "Labels" above
"Labels Stickers Location", "Members" above "Members Assignee Creator".

The caret belongs to the first field's TITLE now, so there is one row of
titles - Labels Stickers Location - with the caret at the start of it and the
+ buttons on the row below. Everything the caret folds is behind one switch:
that field's own content, and every field beside it. A collapsed group is
exactly one line, which is what "only caret and icon and text Members" asked
for.

The rule moved with it, from inside a field to the group, where it spans the
card - a rule inside a flex item is as wide as the item, which is the third
time that has bitten in this card and is written down where each fix lives.

The Checklists icon is a plain check again rather than a check in a box: it
was fa-check before the section header gave it fa-check-square-o.

Rebuilt one group at a time, compiling after each, because the first attempt
at this restructure produced anchors as siblings of their <li> and an orphan
`if` - both of which a running build rejected with "Can't use the built-in
'if' here" while a passing test looked fine. The file is verified through the
loader's own parse + SpacebarsCompiler.codeGen pipeline, which is what the
build runs.

Thanks to xet7 !
2026-08-14 03:37:03 +03:00
Lauri Ojansivu db4e0297b7 CHANGELOG: point the Requested By entries at their commit.
Thanks to xet7 !
2026-08-14 03:28:22 +03:00
Lauri Ojansivu 050df89f06 Requested By and Assigned By: a + to set them, and they survive a round
trip.

Both fields were set by clicking the word "Add". They have the round + that
Members and Assignee use now, opening the same editor the text opens - the
same class, so it is the same button and not a second one that looks like
it.

Checking where else they live turned up a ROUND TRIP THAT LOST THEM, which
is the worst shape this bug takes: the export looks complete and the import
reports success. They were exported by the card PDF, the card Excel and the
board CSV, and imported by nothing - a card exported and imported back had
forgotten who asked for it and who assigned it. models/wekanCreator.js and
the per-menu scoped importer both set them now.

The board's streaming Excel TABLE exported neither, so it gained two
columns: header and value in the same commit, because a header without its
value shifts every column after it.

From other trackers, where the idea has another name: Jira's REPORTER is who
asked for the work, and an issue's AUTHOR on GitHub, Gitea, Forgejo and
GitLab is the same thing. Both arrive as Requested By, as free text, so they
survive an import from a tracker nobody on this board has an account on. A
source with no such field - Trello, CSV - gains nothing, and a negative test
says so.

Also two things the grouping left crooked, both visible on an opened card:

- Members, Assignee and Creator were each a full-width row, so the last two
  fell below the first. The class that made a FIELD full width predates the
  groups; the GROUP is the full-width row now, and its fields share it.
- A field whose title the group header had taken over put its + on the first
  line, level with its neighbours' titles. Every field in a group keeps its
  own title again: the group header names the family and folds it, the
  field's title names the field, and the + starts on the line below it.

tests/requestedAssignedByRoundTrip.test.cjs walks the whole way round - the
card, Card Settings, four exports, three importers - because this was found
by looking rather than by anything failing.

Thanks to xet7 !
2026-08-14 03:28:21 +03:00
Lauri Ojansivu 8c718aa42d CHANGELOG: point the card field grouping entry at its commit.
Thanks to xet7 !
2026-08-14 03:14:52 +03:00
Lauri Ojansivu 83147dcc3e Card details: fields fold in groups, one caret each, Members before Creator.
The caret beside Labels folded the Labels field and left Stickers and
Location sitting under it. And every field already had a caret of its own on
the right - #1591 draws one on each .card-details-item - so a row could
carry two handles saying the same thing in two sizes.

The fields belong to families, and the families fold:

  Labels        Labels, Stickers, Location
  Date Format   the format, Received, Start, Due, End
  Members       Members, Assignee, Creator, Requested By, Assigned By
  Dependencies  Dependencies
  Sort          Sort, List, Spent time

One caret per group, on its title, at the reading direction's start. The
fields inside a group have none: #1591's per-item caret is turned off inside
a group body and its title stops being a handle. OUTSIDE a group it is
untouched, so nothing that folded before stopped folding - there is a
negative test for that, because turning the rule off globally would have
been the easy way to do this and would have removed a feature.

A collapsed group is one line - caret, icon, name - because everything else,
including the first field's own controls, is inside the fold.

Members reads Members, Assignee, Creator. The loose `hr` the layout drew
above the users block is gone with the grouping: the group's rule sits above
the whole group, so there is one line there rather than two.

Thanks to xet7 !
2026-08-14 03:14:52 +03:00
Lauri Ojansivu 12e91eb720 CHANGELOG: point the section rule styling entry at its commit.
Thanks to xet7 !
2026-08-14 03:05:13 +03:00
Lauri Ojansivu 365cfdc4df Card details: the section rules are the page's own line, and only where one
belongs.

Four things about those separators, all visible at a glance and none of them
visible in the source.

THE STYLE. The section rule drew a border of its own - 1px of rgba(0,0,0,
.12) - which is darker than the line the card already had above Creator.
That lighter line is the page's `hr` (layouts.css); the section rule sets
only its margin now, so a card has one line style instead of two.

ABOVE LABELS. Labels is the FIRST section: there is nothing above it to be
separated from, so it asks for no rule.

ABOVE MEMBERS. It sat immediately under the rule the layout already draws
above Creator, so a reader got two lines with a heading between them. No
rule there either. Every other section still has one, and a test checks both
halves of that.

ABOVE ACTIVITIES. The rule was to the LEFT of the heading, not above it:
`.activity-title` is `display: flex`, so a rule inside it is a flex ITEM and
lays out beside its sibling. The heading is outside that row now, where a
block-level rule spans the pane. Same class of mistake as the short rules in
the flex row of card items - a separator only separates when it is not a
flex child.

Thanks to xet7 !
2026-08-14 03:05:12 +03:00
Lauri Ojansivu acc53b8b4f CHANGELOG: point the section rule and duplicate heading entry at its commit.
Thanks to xet7 !
2026-08-14 02:46:52 +03:00
Lauri Ojansivu c4d1801022 Card details: one full-width rule per section, and each section named once.
Two things the collapsible sections got wrong, both visible the moment a
card was opened.

SHORT RULES EVERYWHERE. `.card-details-items` is a wrapping flex row and the
rule was drawn INSIDE a section, so it was as wide as that box - a stub of a
line beside a heading instead of a separator across the card. The six
sections that live in that row (Labels, Date Format, Members, Dependencies,
Sort, Custom Fields) are rows of their own now, so the rule in them spans
the card. The items laid out side by side on purpose - Stickers, Location,
the four dates, Creator, Assignees, Spent time - still share rows, with a
negative test saying they were not swept up in this.

TWO HEADINGS FOR ONE SECTION. checklists.jade and subtasks.jade draw their
own <h3>, and the section header drew it again, so a card showed
"Checklists" and "Subtasks" twice each, one above the other. Those templates
keep what belongs to the LIST - add a checklist, add a subtask - and the
section header is the only thing that names the section.

tests/templateRegistration.test.cjs failed on the second change and was
right to: it pins the LINE of a known dangling include in subtasks.jade so
the exemption cannot quietly cover a new one, and removing the <h3> moved
that line from 8 to 7. The record is updated rather than loosened, with a
note saying why it moves.

Thanks to xet7 !
2026-08-14 02:46:52 +03:00
Lauri Ojansivu d111e8633e Export board popup: HTML sits beside PDF and Excel.
HTML was its own single-entry list under the Dependencies heading, which put
it in a group it has nothing to do with - a dependency graph is a picture of
what links to what, and HTML is the board itself as a page.

It is on the first row now, beside PDF and Excel. Those three are the ones
that render a board as a document to LOOK at; everything under the headings
below is data to reimport somewhere.

The order test walks the whole menu, so it moved with it.

Thanks to xet7 !
2026-08-14 02:38:35 +03:00
Lauri Ojansivu a5a757c686 Export board popup: a rule above each subheading.
The three groups - Dependencies, CSV, JSON - were named but not separated,
so a heading floated between the list above it and the list below it with
nothing to say which one it belonged to.

An <hr> above each, which is the same thing the card sections do: the
heading names the family, the rule says where the family before it ended.
The rule is tighter than the default margin so the heading stays visually
attached to its own list rather than sitting in the middle of two gaps.

The test counts them - one rule per subheading, no stray separators - since
a menu that grows a fourth group is exactly where an extra or a missing one
appears.

Thanks to xet7 !
2026-08-14 02:36:38 +03:00
Lauri Ojansivu 0174851e64 CHANGELOG: the export menu is two panes, not one column of them.
Thanks to xet7 !
2026-08-14 02:30:57 +03:00
Lauri Ojansivu c1fe65d3ec Export board popup: two panes - what to include, and what to export to.
The menu was one column: the parts checkboxes, then twenty-odd formats under
three subheadings, and a scrollbar past most of it even on a wide screen.

With room it is now two panes side by side - the selection on the left, the
formats on the right - each filling its own width with as many columns as
fit, so the whole menu is visible at once. Width goes from 760 to 1100.

The panes are grid COLUMNS, and that is what makes the right-to-left case
free: a mirrored page puts the first column on the right, so the selection
lands on the right and the formats on the left with no second rule to write
or forget. The divider between them is a border-inline-start and a
padding-inline-start for the same reason, and a negative test checks that
nothing physical - border-left, float: right, a [dir=rtl] branch - crept in
to be mirrored by hand.

Below 800px the panes stack: popup.css already lays every popup out as a
full-screen sheet there, which is what a phone should get.

The 1100 is mirrored in client/lib/popupOffset.js, which places a popup
using its width - left at the default 380, a 1100px menu opened near the
right edge lands most of the way off the screen. Both copies name the other.

The template is rewritten rather than patched: an indentation edit had put
the anchors as siblings of their <li> instead of children, which the jade
compiler accepts and a browser renders as a flat list of links.

Thanks to xet7 !
2026-08-14 02:30:34 +03:00
Lauri Ojansivu 6adf607a0a CHANGELOG: point the export menu width and selection entry at its commit.
Thanks to xet7 !
2026-08-14 02:22:48 +03:00
Lauri Ojansivu bb6f18c469 Export board: the selection reaches every format, and the menu opens wide.
Two halves of the same menu.

THE SELECTION REACHES ALL OF IT. PDF, Excel, JSON and .zip took the parts
checkboxes; CSV, TSV, Kanboard and the eleven JSON dialects built their own
addresses and could not carry them, so ticking "no comments" changed four
downloads out of nineteen. Every board export link goes through the one url
helper now, so ?fields= rides along with all of them.

What a format can DROP depends on what it has, and that is the honest part:

- a CSV has no comments to leave out; what it has is COLUMNS, so the
  selection lands there. Unticking People removes five, Dates removes eight,
  Custom Fields removes the trailing block. One mask filters the header and
  every row, built from the untranslated column keys, so the two cannot
  drift apart;
- a Trello, Jira or GitHub export carries a title, a description, a due date
  and labels. Those three parts are gated and nothing pretends to gate what
  is not there - a negative test checks that gateItem does not mention
  comments, checklists or attachments.

AND THE MENU IS A MENU AGAIN. Twenty-odd entries under three subheadings
were one column to scroll. With room it is laid out in columns like the
language list - width: min(90vw, 760px) and repeat(auto-fill, minmax(210px,
1fr)) - so the whole thing is visible at once. Below 800px nothing changes:
popup.css already makes every popup a full-screen sheet there. The 760 is
mirrored in client/lib/popupOffset.js, which places a popup using its width;
left at the default 380, a wide menu opened near the right edge lands half
off the screen. Both copies say where the other one is.

Thanks to xet7 !
2026-08-14 02:22:48 +03:00
Lauri Ojansivu a4175a79bd CHANGELOG: rewrap the Upcoming section at 80 columns.
Thanks to xet7 !
2026-08-14 02:11:53 +03:00
Lauri Ojansivu c3a9072785 CHANGELOG: point the bundle startup fix at its commit.
Thanks to xet7 !
2026-08-14 02:11:10 +03:00
Lauri Ojansivu b77299c806 Fix the client bundle dying at startup: a component must load its own
template.

Reported from testi.wekan.fi, on the SIGN-IN page:

  Uncaught TypeError: can't access property "helpers",
    Template.exportScopeBody is undefined
  [UserAccounts] Warning no template passwordInput found!

`Template.exportScopeBody.helpers(...)` runs at module scope and throws when
the template is not defined yet. That does not break one popup: it stops the
module evaluating, so every template registered after it never registers -
which is why a sign-in form nobody had touched came up blank.

The central lists in client/features/*.js import a component's .jade before
its .js, and that is enough for a component nobody else imports. The export
popup body is imported by sidebar.js, by cardDetails.js and by the import
page, so whichever of those the graph reaches first evaluates it, long
before the feature list gets to the template. It imports its own .jade now.
The module cache makes the second import free, and the order stops being a
hope.

tests/clientBundleImports.test.cjs gains the rule - a component .js that
other components import, that registers on a template of the same basename,
must import that template - and it found a second file already in that
state: migrationProgress.js, imported by boardBody.js. Fixed the same way
rather than excepted, since it is one line and the failure it prevents takes
the whole client with it.

Thanks to xet7 !
2026-08-14 02:11:10 +03:00
Lauri Ojansivu d53aa8a832 CHANGELOG: point the export menu grouping entry at its commit.
Thanks to xet7 !
2026-08-14 02:03:02 +03:00
Lauri Ojansivu 8d716de7cc Export board popup: grouped under subheadings instead of one flat list.
Nineteen entries in one list, each spelling out its whole family: "Export /
CSV (,)", "Export / CSV (;)", "Export / TSV", then eleven lines beginning
"Export / JSON /". The part that differed - the only part worth reading -
started two thirds of the way along every line.

A subheading names the family once and the entries under it say only what
they are:

  PDF
  Excel
  Dependencies:  JSON, SVG
  HTML
  CSV:           (,), (;), TSV
  JSON:          JSON, JSON (without attachments), .zip, Kanboard, Trello,
                 Jira, NextCloud Deck, OpenProject, GitHub, GitLab, Gitea,
                 Forgejo, Asana, Zenkit

The .zip is in the JSON group because it is that document in another
container - the same bytes with the attachments beside them as files.

The parts checkboxes stay at the top, from the shared popup body: the board
passes hideFormats, because it lays the formats out itself, and borrows the
selection rather than growing a second copy of it. Its links come from the
same exportUrlFor helper every other menu uses, so a menu cannot send a
different selection than the one on the screen - which is the failure this
kind of regrouping invites.

tests/exportBoardPopupOrder.test.cjs walks the whole list in order, checks
the three subheadings, and has a negative test that every format offered
before is still offered - a reordering that loses an entry looks like a
tidy-up.

Thanks to xet7 !
2026-08-14 02:03:02 +03:00
Lauri Ojansivu 59da349fa2 CHANGELOG: point the card section collapse entry at its commit.
Thanks to xet7 !
2026-08-14 01:58:58 +03:00
Lauri Ojansivu 384f03b359 Card details: each section collapses from its own caret, one rule between
sections.

A card had ONE caret, in its header, collapsing the whole card - and
Activities had an eye beside its heading. Every other section was a heading
with no control, and the rules between sections were written by hand
wherever somebody remembered one.

Labels, Date Format, Members, Dependencies, Sort, Custom Fields,
Description, Checklists, Subtasks, Attachments, Comments and Activities each
carry a caret that collapses that section and one rule above it, both drawn
by one cardSectionHeader template - so eleven sections cannot end up with
ten separators and nine carets. The heading announces itself as a button and
answers Enter and Space, rather than being a click target only a mouse can
find. The caret points down when open and toward the reading direction's end
when closed: right in English, left in Arabic, Hebrew and Persian, read from
the language rather than guessed.

Activities starts collapsed, everything else open. An unknown section
defaults to OPEN, so a section added later shows up rather than hiding.

THE EYE WAS NOT THE SAME CONTROL AS THE CARET, and removing it needed care.
`showActivities: false` - its default - subscribes to activityType
'addComment' ONLY (server/publications/activities.js), so it chose between
"comments" and "the whole history" as much as it showed and hid. Deleting it
and nothing else would have quietly turned an opened Activities section into
a comments-only list: a feature lost by accident rather than by decision. So
an open card section asks for the whole history, and a closed one subscribes
to nothing at all - the cheaper half of what the flag was for, without a
second control to keep in step. A card with the flag explicitly false keeps
it.

Custom Fields is one section with one caret however many fields a card has;
its header was briefly inside the loop that draws them, which would have
been six sections sharing a caret on a card with six fields. Stickers,
Location, the four dates, Creator, Assignees and Spent time were not in the
list and keep the headings they had.

tests/cardSectionCollapse.test.cjs pins the twelve sections, the shared
header, the RTL caret, the keyboard, and that the history did not silently
become comments-only. tests/jadeCompiles.test.cjs - the suite that runs the
real compiler - is what made a restructure of this size checkable at all.

Thanks to xet7 !
2026-08-14 01:58:58 +03:00
Lauri Ojansivu f7eae43087 CHANGELOG: point the dependency wrap entry at its commit.
Thanks to xet7 !
2026-08-14 01:47:45 +03:00
Lauri Ojansivu 0c7b3e8df3 Card details: a dependency's title wraps instead of being cut off.
In an opened card's Dependencies section each row is an icon, the linked
card's title and its controls, and the title was `white-space: nowrap` with
an ellipsis. The card details pane is narrow and a card title is a sentence,
so most of a real title - everything right of the coloured icon - could not
be read at all.

It wraps now. Two properties, and the second is the one that is easy to
miss: `white-space: normal` alone would have changed nothing, because a flex
item's default `min-width: auto` refuses to shrink below its content, so the
text overflowed the row rather than wrapping inside it. `min-width: 0` is
what lets it wrap, and there is a negative test saying so, because removing
it would look harmless.

A title that is one long token - a URL, an id - breaks inside itself with
`overflow-wrap: anywhere` rather than pushing the type, colour and remove
controls off the row. The row aligns to flex-start now, so the icon and the
controls sit beside the FIRST line instead of floating halfway down a
three-line block.

Thanks to xet7 !
2026-08-14 01:47:45 +03:00
Lauri Ojansivu 93e1fcf821 CHANGELOG: point the streaming zip import entry at its commit.
Thanks to xet7 !
2026-08-14 01:45:59 +03:00
Lauri Ojansivu fe77b7461e Stream a .zip import through an upload endpoint instead of the browser.
Reading an archive in the browser and sending its attachments as base64 over
DDP is fine for a card and wrong for a board: 2 GB of attachments become 2.7
GB in one message, in the browser's memory and then in the server's.

A .zip now goes to POST /api/import/zip as the file itself, and nothing is
ever whole in memory on the way in:

- the request body is streamed to a temp file as it arrives;
- unzipper.Open.file reads the archive's central directory, so entries are
  opened on demand rather than inflated together - what the backup restore
  already does;
- each attachment is piped from the archive into the collection by
  addAttachmentFromStream, which writes it to a temp file and hands
  Meteor-Files the PATH rather than a Buffer.

That helper is lifted out of copyFile in models/lib/fileStoreStrategy.js,
which already streamed an attachment to a temp path and called addFile - so
there is one way to add an attachment from a stream rather than two.

Where the files end up is not decided here: addFile fires the collection's
onAfterUpload, which validates the file and moves it to the default storage
configured in the Admin Panel, exactly as an ordinary upload does.

The cap is checked as the body ARRIVES rather than after
(WEKAN_IMPORT_ZIP_MAX_BYTES, 5 GB by default), so an oversized archive never
lands, and the temp file is removed in a finally whatever happens. Importing
is a write, so the route asks for board membership, and the Admin Panel's
import switch still applies.

An entry name is data, never a path: only the attachment id before the first
dash is read from it, the temp file is named by WeKan, and the path goes
through the same safeEntryPath containment check the backup restore uses.
tests/fixedVulnerabilityClasses.test.cjs caught this file the moment it read
an archive and built a path - the guard was right to ask, and asking it for
proof is cheaper than arguing that the name was ours.

A .json still travels as a document over DDP, which is what it is.

Thanks to xet7 !
2026-08-14 01:45:59 +03:00
Lauri Ojansivu 209df66804 CHANGELOG: point the card title X and attachment import entries at their commit.
Thanks to xet7 !
2026-08-14 01:35:07 +03:00
Lauri Ojansivu bada25de4d Editing a card title keeps its X, and imported cards bring their
attachments.

Two things, one commit each would have been two lines apart in the same
files.

THE X. Clicking a card's title opens the title editor in place of the card
header - and the header is where the card's own close X lives, so the X
disappeared the moment you started editing and Save became the only visible
way out. Escape still worked; nothing on the screen said so.

The editor draws the X itself now, reusing .close-card-details, the header
button's own class, so it is the same size in the same place and does not
jump as the editor opens and closes. That class floats to inline-end - the
right in English, the left in Arabic - so the RTL mirroring needs no
direction branch, and a negative test says it must not grow one. It is an
<a> and not a <button>, because a button inside the form would submit it,
and the click reaches the shared inlinedForm handler by bubbling.

The same one-line omission was in the Requested by and Assigned by editors
below it: the close anchor was there and its icon was not, so the click
target existed and was invisible. Ten more of those exist in other files and
are left for their own change.

THE ATTACHMENTS. The scoped import was half a round trip: a card imported
from a swimlane, list or card menu arrived with its checklists and comments
and without its files.

A .json export carries each attachment as base64, and ScopedImporter writes
them the way the board import always has - the server-side Meteor-Files
writeAsync, one at a time. A .zip carries them as files named
attachments/<id>-<name>; the archive is unpacked in the browser and each
file is put back on the metadata row its id names, so the server sees the
same document either way and there is ONE import path rather than one per
container. The whole-board import on the import page now takes a .json or a
.zip through that same reader, which is the case the .zip exists for.

An attachment lands where its CARD landed - the list and swimlane it is in
now, not the ones it was exported from. One unreadable file is warned about
and skipped rather than losing the rest of the import, an empty one is not
written as a zero-byte attachment, and a `url` attachment from an older
export still goes through the downloader that validates and pins every hop,
so FollowBleed stays fixed on this path too.

The tradeoff worth knowing: the archive is unpacked in the browser and sent
as base64, so a .zip full of large attachments costs memory on the way in.
It is the shape the board import has always used, and it is written down
where the unpacking happens.

Thanks to xet7 !
2026-08-14 01:35:06 +03:00
Lauri Ojansivu c6cad5bb4a CHANGELOG: point the import page entry at its commit.
Thanks to xet7 !
2026-08-14 01:25:26 +03:00
Lauri Ojansivu f0745dec4a One import page, for every source, with the same checkboxes the exports use.
The last piece of #1173: "combine all import options to same template". They
were fourteen - a link per source in a pop-over, each going to its own
address - and the page each landed on never said which other sources
existed, so "where do I import a Jira export" was answered by a menu
somewhere else, if you knew it was there.

All Boards / + Add Board / Import is now one full-width page listing every
source it can read: Trello, Jira, CSV/TSV, Excel, Kanboard, NextCloud Deck,
OpenProject, GitHub, GitLab, Gitea, Forgejo, Asana, Zenkit, and a previous
export of this WeKan - named with the Product name this instance is branded
with rather than with a product the reader has never seen.

Under the source picker are the same "what to include" checkboxes every
export offers, from the same list (models/lib/exportFields.js) and sharing
the very same selection state, so a part unticked for an export is unticked
here too. On this side they say what comes IN.

That works for every source without teaching five creators a selection each:
models/lib/importParts.js takes the unticked parts OUT of the parsed
document before any creator sees it, at the one point every source passes
through. A creator that never sees a comment cannot import one. Three things
that pruning gets right and are covered by tests:

- an unselected section is EMPTIED, not deleted - the creators read
  board.comments directly, and undefined is a crash where an empty array is
  "there were none";
- a source's own name for a part goes with it, since Trello calls its
  comments "actions";
- a key the list does not know about is left alone, rather than silently
  dropping data nobody asked about.

/import/:source still works, under its own route name, so every existing
link, bookmark and back button lands where it did. The pop-over keeps the one
import that is not a board: card dependencies.

Thanks to xet7 !
2026-08-14 01:25:26 +03:00
Lauri Ojansivu 6ce8047fa4 Search: #12 and 12 find the label 12 and the card numbered 12.
Follows the card-number operator. #5006's title says "number/#", and the
form people actually type is the one on the card: #12, or just 12 in the
search box. Both now search for BOTH - a label called 12, and the card whose
number is 12.

Which of the two somebody means cannot be read off the text: a board calls a
card "#12", and a label can be called anything, including "12". Answering
with both is the only reading that never hides what they were looking for.

The two halves are joined with OR, and that is the property that makes this
safe rather than destructive. A search that used to find a label called
"2024" still finds it; the card numbered 2024 is added to the answer instead
of replacing it. An AND would have found nothing at all in almost every case
and would have looked like the feature working.

Only a term that is entirely digits gets the second half, so #red is the
label red exactly as before, and a label-only search takes the code path it
always took. A bare 12 adds the card number as one more alternative in the
$or that already holds title, description, custom fields, checklists,
attachments and comments - so nothing that matched before stops matching.

#12 on a board with no label called 12 no longer reports "label not found":
the card-number half is a real answer, and a not-found message beside the
card it just found contradicts the screen.

The guard that pinned the opposite decision moved rather than being deleted.
It now pins the union, the OR, that only numbers get it, that #red is
untouched, and that the not-found error still fires for a word.

Thanks to mfilser and xet7 !
2026-08-14 01:20:44 +03:00
Lauri Ojansivu 3020d5e6b9 CHANGELOG: point the All Boards tile height entry at its commit.
Thanks to xet7 !
2026-08-14 01:10:04 +03:00
Lauri Ojansivu 6c59196802 All Boards: a starred page is the same height as a board and as "+ Add
Board".

In All Boards / Starred a bookmark tile stood 8px taller than the board tile
beside it and than the grey "+ Add Board" box, and pulled its whole row up
with it.

Two of the four tiles in that grid carry `border: 4px solid #fff` - a
bookmark and the Template Container - to say they are not an ordinary board.
The tile's height was a 72px floor with 42px of padding added outside it,
which comes to the same 114px for a plain tile and 122px for a bordered one:
on a content-box element the border is added to the height rather than taken
out of it. Every height in the stylesheet said 72px, so nothing there looked
wrong.

The tile now states the height it actually renders at - 114px, padding and
border folded in with box-sizing: border-box - which is what the mobile
rules further down the same file already do, for the same reason (#6523).
Nothing moves except the two bordered tiles, which lose the 8px they were
never meant to have.

The guard compared the declared min-height and padding of two tiles that
were both content-box, so it could not see a border at all - which is how
this went unnoticed while a test watched it. It computes RENDERED heights
now, borders included, checks all four tile kinds, and has a negative test
against a tile rule opting back into content-box.

Thanks to xet7 !
2026-08-14 01:10:04 +03:00
Lauri Ojansivu 7b1cefe4e9 CHANGELOG: point the card number search entry at its commit.
Thanks to xet7 !
2026-08-14 01:06:35 +03:00
Lauri Ojansivu 530ef32c16 Search cards by their card number.
#5006, open since 2023: "A customer has just hinted us that we are not able
to search cards by its number (#)". A card carries a number, the board shows
it, people quote it to each other - and the one thing that could not be done
with it was find the card again.

`number:12` now does, in the global search and everywhere else that parses
the same query. It is an EQUALITY match on a number rather than a regular
expression on text, which is the difference between finding card 12 and
finding 12, 120 and 312. A predicate that does not parse is dropped before
it reaches the selector, and an empty list adds no clause at all rather than
an impossible one.

`number:abc` is refused with the same "expected a number" error `limit:`
already gives. A string never equals a numeric field, so the alternative is
a search that silently finds nothing, which teaches nobody anything.

The issue's title says "number/#", and `#12` is deliberately not this. `#`
is already the abbreviation for a LABEL search, and reinterpreting it when
the text after it happens to be digits would break searching for a label
called "2024" - a real label name - and would make what a query means depend
on the data rather than on what was typed. The operator is the part that can
be added without taking something else away; tests/searchCardNumber.test.cjs
pins that # still means a label.

Thanks to mfilser and xet7 !

Fixes #5006,
2026-08-14 01:06:35 +03:00
Lauri Ojansivu 066f92fe2b CHANGELOG: point the scoped import entry at its commit.
Thanks to xet7 !
2026-08-14 01:03:08 +03:00
Lauri Ojansivu d11f5ffdb7 Import into a swimlane, a list or a card, beside the one whose menu was
used.

Continues #1173, the other direction of the same popup. Importing used to
mean one thing: a whole new board, from the full-width page at All Boards. A
swimlane's menu now imports a swimlane below that swimlane, a list's menu
imports a list after it, and a card's menu imports a card below it, into the
board that is already open.

"After" is to the RIGHT in English and to the LEFT in Arabic, and that is one
rule rather than two. The page carries dir (client/components/main/
layouts.jade), so the board's row of lists mirrors itself and "after in sort
order" is already "the other side"; a direction branch in the placement
would mirror it twice and put the list back where it started. The reason is
written in models/lib/insertPosition.js, next to a negative test, because it
looks like a missing branch.

Placement is a fraction between the target's sort and the next one's, not a
renumbering of every sibling - renumbering is what a board with ten thousand
cards cannot afford and what two clients doing it at once get wrong. Several
imported items spread evenly through that gap, so a swimlane of ten lists
does not arrive with nine of them in the same position. The arithmetic is a
pure module and is tested as arithmetic, junk input included.

The file is what the export writes, at any scope, and the SAME checkboxes
decide what comes in: a document full of comments imported with comments
unticked brings the cards and leaves the comments. A .zip is unpacked in the
browser and its wekan.json handed to the same method, so there is one import
path and not two.

Nothing is merged. Everything created is new, with new ids, because an
import that half-updated a board would be an edit nobody could undo -
importing the same file twice gives two copies, which is at least a thing
you can delete. A custom field is matched by NAME, since an id from another
board matches nothing here, and a comment comes back under the importing
user, because the original author may not exist on this server.

Importing WRITES, so unlike the exports beside it in the same popup it asks
isBoardMember() rather than isVisibleBy(), the Admin Panel's import switch
still applies, the format is checked, and the deadline that bounds a board
import bounds this one. A read-only member is not offered it at all.

tests/scopedImport.test.cjs covers the placement arithmetic and pins the
selection, the write check and the single import path.

Thanks to xet7 !
2026-08-14 01:03:08 +03:00
Lauri Ojansivu e38750c54c CHANGELOG: point the JSON and .zip export entry at its commit.
Thanks to xet7 !
2026-08-14 00:52:48 +03:00
Lauri Ojansivu b0ca226099 JSON and .zip export at every menu, streamed, from the one export popup.
Continues #1173. The board's Export menu had JSON; nothing else did, and
there was no .zip anywhere. Both are now offered wherever an export is - the
board, a swimlane, a list, a card and a checklist - from the same popup,
with the same checkboxes deciding what goes in.

They are the same export in two shapes rather than two exports. The document
is written by models/exporter.js' streaming writer either way, so a .zip's
wekan.json and a .json download of the same scope are the same bytes. What
differs is where the attachments are: base64 inside the document for JSON,
or beside it under attachments/ as the files they are for the .zip. The
"without attachments" option is now offered at every menu instead of only on
the board's.

Both halves stream. The JSON writer already wrote a document at a time from
raw cursors with backpressure; ExporterZip gives it a PassThrough that
archiver compresses as it fills, and pipes every attachment from the file
store rather than reading it into a Buffer. A board with a gigabyte of
attachments costs a gigabyte of disk reads and not a gigabyte of RAM, which
is what makes the .zip the shape to use when the JSON is too large to hold
as one string.

A scoped export is the same wekan-board-1.0.0 document with fewer rows in
it, plus the lists and swimlanes its cards refer to, so what comes out can
be imported back into somewhere. A section the popup did not tick is an
empty array rather than a missing key, for the same reason. A checklist
scope exports the card that holds it, because a checklist alone has nowhere
to land, and a swimlane export carries no board-wide rules.

Less duplication, which is the other half of this:

- every export route - JSON, .zip, PDF, Excel - reads the scope through one
  parseExportScope() and the selection through one parseExportFields(),
  instead of each picking ids out of the query itself;
- the CARD export popup IS the shared body now, with the card as its scope.
  It was its own template with its own field list, its own ReactiveDict and
  its own url builder, which is how its checkboxes came to drive the Excel
  download and not the PDF;
- one url builder serves every format and every scope, and knows that a card
  has export routes of its own so there are not two ways to ask for one
  card's PDF.

tests/boardExportScope.test.cjs gains the JSON/.zip shape, the scoped
document and the single scope parser. Two guards moved rather than being
deleted: they pinned the routes parsing their own ids and the card popup
holding its own copy of the field list.

Thanks to xet7 !
2026-08-14 00:52:48 +03:00
Lauri Ojansivu 8a278937ab CHANGELOG: point the board export entries at the commit that made them.
Thanks to xet7 !
2026-08-14 00:38:55 +03:00
Lauri Ojansivu bd009cf601 Export a board, a swimlane or a list, in the card export's own layout,
with a popup for what to include.

#1173 "Add Feature: Print Board with Params" has been open since 2017, and
two things were missing.

There was no CHOICE of what to print. The board's Excel and PDF exports took
everything they knew how to render and nothing else, while the card export
already had a popup with a checkbox per section. And they did not LOOK like
the card export: the board's Excel export was a spreadsheet table, one row
per card and eighteen columns, which is a data dump rather than a printed
board.

Both now render a board as its own header followed by every card as the CARD
export's block, drawn by the card export's own code - cardBlockLines() for
the PDF, ExporterExcelCard.renderCardBlock() for the Excel one. A card looks
the same whether it was exported alone or inside its board, and the two
cannot drift into two layouts again. Each card starts on its own page in the
Excel export, because a printed board is read a card at a time.

The swimlane and list menus offer the same export, which is the board export
with one query parameter saying which cards are in scope. They sit beside
"Copy link" rather than under the permission checks, because exporting is
reading, and the file is named for what was exported - a PDF titled with the
board that holds one list is a file nobody can place afterwards.

The selection is one popup body used by all three menus, and one list:
models/lib/exportFields.js, imported by the exporters and by every popup.
The card export's list used to live twice, in the server and in
cardDetails.js under a comment reading "Must match ALL_FIELDS ..." - which
is a comment, not a mechanism, and a section added on one side is either a
checkbox that does nothing or a section nobody can turn off. The same
?fields= now gates the same sections in both formats; the card popup's
checkboxes used to say "fields to include in Excel export" and did nothing
to the PDF.

A card block cannot be streamed - it merges cells, styles them and returns
to earlier rows - and models/server/ExporterExcel.js streams on purpose,
after the in-memory version ate gigabytes on boards with thousands of cards.
That exporter is still there and still reachable: unticking "Card details"
asks for it. A checkbox, not a silent fallback.

A board's per-card data is read once per collection and grouped by card. The
card export's own per-card queries would be fifteen hundred round trips on a
board of three hundred cards.

tests/boardExportScope.test.cjs pins the shared block, the shared list, the
gating, the three menus and the streaming exporter still being reachable.
Two guards moved rather than being deleted: they pinned the board PDF
building its own labels and the server's own copy of the field list, which
is the behaviour that deliberately changed.

Thanks to xet7 !

Fixes #1173,
2026-08-14 00:38:55 +03:00
Lauri Ojansivu 2c1027e060 CHANGELOG: point the card-export entries at the commit that made them.
Thanks to xet7 !
2026-08-14 00:09:37 +03:00
Lauri Ojansivu 698e980e27 Card PDF and Excel exports say the same thing, in the reader's language,
format and time zone.

#6586 was reopened for what the first fix left, and every point of it was a
symptom of one thing: the PDF export of a card had grown separately from the
Excel export of the same card.

"Assignee, Labels, due,... these titels should be in the user set language" -
the labels were hard-coded English, and not even consistent with each other:
the card export wrote "Due: " while the board export wrote "due" with no
colon. Both exports now build every label from the same i18n keys, in the
language the request carries, through one field() helper that writes every
"Label: value" - so the two cannot drift apart again. Every key carries its
English text as the fallback, so a missing translation shows the word and
never the key.

"I think all those other things we set in a card should be also present" -
both exports now carry title, labels, creator, assignees, members, board,
swimlane, list, card number, requested by, assigned by, the six dates, spent
time, description, custom fields, checklists, subtasks, comments,
attachments, voting and Planning Poker, in that order. Custom fields, voting
and poker are new on BOTH sides; they are appended to ALL_FIELDS rather than
inserted, so a saved ?fields= link still asks for what it always asked for,
and they are checkboxes in the export popup like the rest.

"the time is not in the user set timezone (-2h wrong for Europe/Berlin)" -
it was worse than -2h: the PDF printed UTC and the Excel export printed
whatever zone the server was started in. Dates are stored in UTC and a
profile carries no zone, so the export links now send the browser's IANA
zone and the date format the OPENED CARD is showing - which for a logged-out
reader lives in localStorage, where no server-side lookup reaches it. Both
exports format through formatDateByUserPreference, which gained an optional
zone; without one it still renders in the process zone, which is what every
client-side caller wants. A server-built export given no zone renders UTC
and says UTC. Verified: 12:00Z prints 14:00 for Europe/Berlin, 00:00 on the
15th for Pacific/Auckland, and an unknown zone falls back instead of
failing.

"would it make sense to support markdown formated text in description" - a
description is now cut into runs and drawn in the four base-14 Courier
faces, so **bold**, *italic* and ***both*** are bold, italic and both.
Consecutive Tj operators continue at the current text position, so a font
switch needs no glyph measuring. Strikethrough and inline code keep their
words and lose their markers - a Type1 base-14 font has no strike and there
is no fifth face for code - and an underscore inside a word stays an
underscore.

tests/cardExportParity.test.cjs pins the shared field list, keys, formatter
and request params; tests/pdfExport.test.cjs gains the runs, the wrapping
and the four declared faces. Two of its guards moved rather than being
deleted: they pinned the hard-coded English "Labels: " and "due", which is
the behaviour that deliberately changed, and they now pin that both come
from the same keys.

Thanks to Heart1010 and xet7 !

Fixes #6586,
2026-08-14 00:09:31 +03:00
Lauri Ojansivu 490686a3f1 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-13 23:51:23 +03:00
Lauri Ojansivu 935f59a2e9 CHANGELOG: point the new entries at the commit that made them.
Thanks to xet7 !
2026-08-13 23:51:23 +03:00
Lauri Ojansivu 6157094ab8 Stop shipping npm and node-gyp, and raise the bundle's own npm packages.
A container scan of ghcr.io/wekan/wekan:v10.91 reported 80 findings against
"Node.js", three of them CRITICAL, and not one of them was in code WeKan
runs.

Three CRITICAL and most of the HIGH findings were three copies of one
package - tar 6.2.1 twice, tar 7.5.11 once - carried by two BUILD tools
left in the finished image:

- node-gyp and @mapbox/node-pre-gyp are meteor-dev-bundle dependencies that
  compile native modules during the npm install in programs/server. WeKan
  compiles nothing at run time; uWebSockets.js, bcrypt and argon2 all ship
  prebuilt .node files that node-gyp-build picks at require time. Their tree
  is 83 of the 120 packages that install leaves behind, and it brought tar
  6.2.1 (node-gyp, cacache), brace-expansion, minimatch and npm's networking
  stack with it.
- npm itself runs exactly once, for that install. The container starts bash
  /build/wekan-entrypoint.sh and never calls it, so its bundled tar 7.5.11,
  sigstore 4.1.0, @sigstore/*, ip-address 10.1.0 and brace-expansion 5.0.4
  were image content no code path could reach.

releases/prune-build-only-modules.mjs removes the first. It is a
REACHABILITY walk, not a list of 83 names - start from every dependency of
programs/server/package.json except those two, follow each package's own
dependencies, keep the closure - so it does not go stale when Meteor
changes its dev-bundle. The Dockerfile removes npm and npx in cleanup;
node stays. Verified by booting the pruned bundle: identical failure at the
database, no missing module.

The rest of the report was the npm packages Meteor's own packages bundle -
nodemailer 8.0.3, openpgp 5.11.1, svgo 2.8.2, postcss 8.5.1, nanoid 3.3.15,
qs 6.13.0, cookie 0.4.1, on-headers 1.0.2, tmp 0.2.3, lodash 4.17.21, diff
3.5.0, body-parser 1.20.3, @babel/runtime 7.20.7, underscore 1.13.7.
Npm.depends names an exact version and meteor build copies it into
programs/server/npm/node_modules/meteor/<package>/node_modules, where no
package.json or overrides entry here is consulted - so the bundle is the
only place they can be raised.
releases/bump-bundle-npm-deps.mjs does that from the minimums in
releases/bundle-npm-security-bumps.json, with --ignore-scripts so no
prebuilt native is rebuilt, replacing only copies below the minimum.

A minimum stays inside the major the package was built against, and that
was learned rather than assumed: uuid's lowest fixed release, 11.1.1, moved
its entry point to dist/cjs/index.js and the bundle - which recorded
dist/index.js at build time - died on boot with "Cannot find module
.../uuid/dist/index.js". It is in notFixable with that error, beside
lodash.template, which has no fixed release at all.

Both run in every place a bundle is made: the amd64 build, the arm64
container, three Windows legs, two macOS legs, install-node-for-arch.sh for
the emulated arches, and the Dockerfile - each reinstalls programs/server,
which is also why each bumps again (meteor-dev-bundle pins underscore
1.13.7, CVE-2026-27601, over the bumped copy).

tests/imageBuildOnlyModules.test.cjs and
tests/bundleNpmSecurityBumps.test.cjs pin both, and buildScriptParity's
skip list records why these two scripts are not menu entries. The debian
13.6 target's 176 findings are all "Fixed in: -" and no upgrade closes
them; the build/<tool> and build/ferretdb targets are fixed in
wekan/mongo-tools-patches and wekan/FerretDB.

Thanks to xet7 !
2026-08-13 23:51:23 +03:00
github-actions[bot] cb9a2c5a6f Bump versions for v10.91 2026-08-13 17:46:11 +00:00
Lauri Ojansivu a19629e858 Prepare v10.91 release 2026-08-13 20:44:56 +03:00
Lauri Ojansivu 7d73845192 CHANGELOG: the Member Settings theming goes under Upcoming.
Thanks to xet7 !
2026-08-13 20:10:26 +03:00
Lauri Ojansivu e86e7abe05 Member Settings: "All Boards" beside Default, and popup rows highlighted like the menu.
Two things in Member Settings / Change color.

ALL BOARDS. Beside "Default (no override)", on the same row, a second switch:
All Boards. Turned on, every tile in the All Boards overview takes the THEME's
lighter colour - a white veil over --theme-accent, which is one shade up from
whatever the theme is at any theme, with no second variable to keep in step and
no color-mix() - with white text on it, instead of the colour its own board was
given. Turned off, which is the default, the overview is exactly what it was.

That is #6593's observation, as a per-user choice rather than a stylesheet. The
pull request makes every tile white for everybody, and the observation behind it
is right: a wall of boards in eleven colours reads as a palette rather than a
list. The conclusion is not, because "the tile colours are noise" and "the tile
colours are how I find my board" are both true, of different people - so it goes
where the rest of that user's theme is chosen, and it is off unless asked for.

A board with a background IMAGE keeps its image: that is a picture somebody
chose, not a colour, and painting over it would be losing something rather than
calming it down.

It is profile.allBoardsThemeTiles (optional boolean, absent = off), flipped by
toggleAllBoardsThemeTiles, which writes the CALLER and refuses an anonymous one,
and it reaches the page as has-theme-board-tiles on <body>, so it follows the
user to every page that shows board tiles. Only the user's own scope of the
shared picker has the switch: a board has no overview of its own, and a site
admin does not choose this for everybody. The label is the existing `all-boards`
string, so no new key needs 135 translations to say what one already says.

THE HIGHLIGHT. The row under the pointer in a popup was filled with a fixed dark
navy (#005377) that appears nowhere else in WeKan, and stayed navy however the
user had themed it. The All Boards left menu fills its selected row with
var(--theme-accent, #2980b9) and turns the label and the icon white, and so does
the Admin Panel; the popup does now too, so there is one selected-thing look
rather than three. The colour needs !important because the base rule pins every
row's text to #000 !important, and the icon is named separately for the same
reason.

tests/allBoardsThemeTiles.test.cjs pins both, with the negatives that matter:
only the user scope has the switch, an image tile is left alone, every new rule
is under the body class so the overview is untouched with it off, and the navy
is gone.

Thanks to khuongsatou and xet7 !
2026-08-13 20:09:06 +03:00
Lauri Ojansivu fa0bfb69e9 CHANGELOG: the two Admin Panel report fixes go under Upcoming.
Thanks to xet7 !
2026-08-13 19:44:59 +03:00
Lauri Ojansivu 034a23edef A restart is not a crash, and a reverse proxy is not a spoofer.
Two Admin Panel reports from a server running WeKan 10.90 Snap, both full of
things that never happened.

FILESYSTEM INTEGRITY, over and over:

  availability  DowntimeBleed  high  failed  uptime
    the previous run STOPPED WITHOUT SHUTTING DOWN CLEANLY, and this server was
    down for about 4 minute(s)

on a snap that had been refreshed, not crashed. Two faults, and the second made
the downtime real rather than merely reported:

  1. `IntegrityKeys.update()` is not synchronous in Meteor 3. It starts a write
     and hands back a promise nobody waited for, so the clean-shutdown mark was
     never on disk when the process went, and every stop looked unclean.
  2. Registering ANY listener for SIGTERM replaces Node's default behaviour,
     which is to terminate. Nothing in that listener exited, so WeKan ignored
     SIGTERM outright: systemd/snapd waited out its stop timeout and used
     SIGKILL. That is both the minutes of "downtime" in those rows and a
     genuinely unclean kill of the app on every ordinary restart - and on a snap
     refresh it is minutes of the site being down for no reason.

So the mark is written with updateAsync and the handler exits: with 0, because a
stop that was asked for is a success and a non-zero code makes systemd mark the
service failed every time; after at most two seconds, because a shutdown must
never be held open by the bookkeeping about shutdowns; and exactly once, however
many signals arrive. The classification itself is untouched - the rows were
wrong because the input was wrong, and a "fix" that stopped calling a crash a
crash would hide the next real one.

SECURITY REPORT, over and over:

  spoofing  MetricsBleed  medium  detected  canary:spoof.forwarded-header
    127.0.0.1  denied /metrics with X-Forwarded-For present

"Nothing legitimate sends a forwarded-for header to an endpoint that does not
trust one" is what that canary assumed, and it is wrong: every reverse proxy
adds X-Forwarded-For to everything it forwards. A Prometheus scrape through a
local proxy, on a server whose METRICS_ACCEPTED_IP_ADDRESS does not cover it, is
an ordinary refusal - and it was being written up as a medium-severity spoofing
attempt from 127.0.0.1, again and again, in the report where a real one would
have to be noticed.

The spoof has a signature, so it is asked for: the header NAMES an allowlisted
address while the connection itself is not from one. That is somebody claiming
to be a whitelisted host, and only that trips the canary now. The 401 is
unchanged, and gained the sentence the admin actually needs - the address in it
is the proxy, not the scraper, unless METRICS_TRUST_PROXY says how many hops to
trust.

tests/restartIsNotACrash.test.cjs pins both, including that SIGTERM still
terminates and that the crash classification still calls a crash a crash.

Thanks to xet7 !
2026-08-13 19:44:31 +03:00
Lauri Ojansivu 55552a7203 Snap: the pages shown without a database use the product name and the dashboard's colours.
Two things about the standalone pages the snap serves when WeKan itself cannot
be served - under maintenance, recovering data, database too old, and the new
"waiting for its database" (#6592).

THE NAME. They are a node:http server with no database connection, which is the
whole point of them, so the product name comes from a file: wekan-control caches
it once per start, right after the database comes up. That leaves the case that
matters - an admin sets the product name in the Admin Panel and the snap is not
restarted before the next outage - and a rebranded WeKan then tells its users
"WeKan is waiting for its database", a word they have never seen. WeKan is the
only thing that knows the name the moment it changes, so WeKan writes it:
server/productNameCache.js writes $SNAP_COMMON/.productname.txt at startup and
whenever the setting changes, the same way customHeadRender keeps the <title> in
step. An empty name is not written (the pages fall back to WeKan on their own),
a read-only $SNAP_COMMON is a warning and nothing more, and off snap there is no
SNAP_COMMON and nothing happens at all.

THE COLOURS. The schema-upgrade dashboard (/schema-upgrade-status) and these
pages are the same thing to a reader: the product saying what it is doing while
it cannot show them the app. They now look like it - the dashboard's #111
ground, #ddd text, #7bf blue and monospace face, dark only as the dashboard is,
rather than a light card that reads as a different product one page over.

Thanks to xet7 !
2026-08-13 19:44:15 +03:00
Lauri Ojansivu ffc0b14581 CHANGELOG: the waiting-for-its-database page goes under Upcoming.
Thanks to xet7 !
2026-08-13 19:32:11 +03:00
Lauri Ojansivu 4254dfa396 Snap: a wait for the database is a page, not a timeout.
#6592: "We upgraded from 10.85 to 10.89 and later to 10.90 - a reload of wekan
got a timeout (loading forever). ... Reload with F5 or Ctrl+F5 - opening in a
new private browser tab also doesn't work."

WeKan does not open its web port until the database answers, and the two waits
in wekan-control are endless on purpose: a database can take minutes to come up
after an update, and a snap that gave up on it would be worse than one that
waits. They are not silent either - after two minutes each prints what to check.

But NOTHING was listening on the web port while they waited. A browser gets a
timeout, which is what "loading forever" is, and everything the script printed
went to `snap logs` - the last place somebody whose site is down thinks to look.
It also makes the two possible faults look identical: "WeKan does not load" is
the report either way, whether WeKan is broken or FerretDB simply did not start.

So the wait says so, on the web port. wekan-control serves the existing
maintenance page with a new reason, `waiting-for-database`, which names the
database being waited for and, in the browser, the four commands: `snap logs
wekan.<service>` for the real reason (an `exec format error` is the bundled
binary not running on this CPU), `snap run wekan.problems` for which copy of the
data is being served, `snap start --enable` for a service left stopped by a
failed migration, and `snap revert` back to the revision that worked - with the
answer to the question actually being asked, which is whether the data is gone.
It refreshes itself away when WeKan starts.

Three things make it safe rather than merely nice, and the tests pin each:

  * it appears only after a grace period (WEKAN_DB_WAIT_PAGE_SECONDS, 30s), so
    an ordinary restart - which passes through these loops in seconds - never
    shows it, and proxies are not taught to cache a 503 for a healthy site;
  * it is stopped, and waited for, before anything else binds that port. The
    page is a child process and `exec` keeps children alive, so the two re-execs
    inside the MongoDB loop had to stop it first or the next start would die on
    EADDRINUSE - a worse fault than the one being fixed;
  * it is behind its own reason, so maintenance mode, the recovery bridge and
    the data-too-old stop are untouched.

This does not say WHY the reporter's database did not come up - the issue has no
logs, and that is the next thing to ask for. It does mean the next person sees
the reason instead of a timeout.

tests/snapWaitingForDatabasePage.test.cjs drives the page and reads what a
browser would get. tests/cpuExecWiring.test.cjs counted the maintenance-page
launches and expected two; a count says nothing about the launch that was added,
so it now asks each launch line whether it goes through cpu-exec.

Thanks to Alishara and xet7 !

Refs #6592 — the reporter's own logs are still needed to say what stopped their
database, so this does not close it.
2026-08-13 19:31:47 +03:00
Lauri Ojansivu c9f301ceb0 CHANGELOG: point the status-report entry at its commit.
Thanks to xet7 !
2026-08-13 19:20:11 +03:00
Lauri Ojansivu 4ca131e3b6 CHANGELOG: an Upcoming section for the work since v10.90.
Three entries. Two are new - the minicard that would not close the card it had
opened, and the snap status report that never said which copy of the data it was
reading - and the third was written into the v10.90 section after v10.90 had
already been tagged, so it moves up here where the release it will ship in is.

Thanks to xet7 !
2026-08-13 19:20:05 +03:00
Lauri Ojansivu 504e450a11 Snap: the status report says which copy of the data is being served.
#6583, comment 5282677837:

  "sudo snap run wekan.database-compare  brings me to:
   error: cannot find app "database-compare" in "wekan"
   It seems, that database compare is not included in wekan 10.82, we have
   installed. Should I upgrade to the most recent version and then try again?"

Yes - the two recovery commands are snap APPS, so they exist only in the
revision that ships them (v10.90). That is the smaller half of this. The larger
half is what happened before it: the same instance, serving data from a
migration done in July, was told "No problems detected" by
`snap run wekan.problems`, and the admin had to find the command names in a
GitHub thread.

That report was not wrong. It reads ONE database - the one WeKan is connected
to - and every check inside it passed. The question that mattered, WHICH of the
two copies that is, was asked by nothing, while the docs already claimed this
command answered it.

So the report ends with a "Databases on this machine" section: which copy is
being served and why (`bin/database-role --why`, the one helper every part of
the snap asks, so the report cannot disagree with what the snap does), whether
MongoDB files and a FerretDB database both exist, and - when they do - the two
commands to run. It reads the files rather than a database, so it answers with
WeKan down, and it declares no fault: two copies is the normal state of a
migrated snap, and calling that a problem would make every migrated instance
look broken.

A revision without the commands says so and says to refresh, instead of leaving
snapd's "cannot find app" as the last word, and says why refreshing is safe for
exactly this case: it does not import an old MongoDB over a FerretDB already in
use (#6585), and neither copy is ever deleted.

The script ran the Node report with `exec`, which replaces the process, so this
had to stop doing that; the report's exit status is still the command's.
`wekan.problems migrations|login|cpu|broken-cards` are unchanged - they are used
in pipes - and `wekan.help` and Migration-to-FerretDB.md now list the two
commands, with the release they arrived in.

tests/snapProblemsDatabases.test.cjs pins all of it, including the two negatives:
no fault is declared, and the sub-commands print only what they were asked for.

Thanks to waltermhl and xet7 !

Refs #6583 — the reporter still has to run the two commands, so this does not
close it.
2026-08-13 19:17:41 +03:00
Lauri Ojansivu 664cc6b95b Fix #6465: clicking a minicard again really closes the card.
"you now can click outside your mini card and the popout will close. Clicking
the mini card again to close the popout is still not possible I think."
(comment 5281667054), on v10.90 - which already had the toggle, and a test
suite pinning it.

The toggle was closing the wrong thing. On a desktop-sized screen the card
details are NOT the route: clicking a minicard writes the card id into the
`openCards` session list and boardBody renders one draggable window per id in
it, leaving the URL on the board. The toggle asked whether the card was
`currentCard` and closed it with `Utils.goBoardId(...)` - which navigates to the
board route, clears `currentCard`, and leaves the window on screen, because
nothing had taken the card OUT of `openCards`. Only the window's own X button
did that. That is why closing worked from the card and not from the minicard,
and why the guard for the earlier fix passed while the reported behaviour did
not change.

So the two questions become two helpers, and both minicard click paths - the
title, which is most of a minicard, and the rest of it - use them:

  cardWindowIsOpen  asks the list that RENDERS the window on a desktop, and
                    `currentCard` for a card opened by its own URL. With "Open
                    many cards at once" on, `currentCard` is only the last card
                    clicked, so every earlier window was impossible to close by
                    clicking its minicard.
  closeCardWindow   removes the id from `openCards`, clears `currentCard` and
                    the phone popup ids, and navigates back to the board only
                    when the card IS the route - a card opened by a click is
                    not, and navigating would reset the board view for nothing.

That is the card details' own `click .js-close-card-details` logic, so both ways
of closing a card now do the same thing; tests/cardClickToggle.test.cjs pins
that they agree, and its two assertions that pinned the routing close are
replaced with the reason above rather than deleted. The phone path, where the
card is a popup and `Popup.back()` closes it, is unchanged.

Thanks to Heart1010, csonkaoszimt and xet7 !

Fixes #6465.
2026-08-13 19:14:02 +03:00
Lauri Ojansivu 3e9e02ba08 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-13 19:06:00 +03:00
Lauri Ojansivu 0fae50d97c CHANGELOG: point the verified-issues entry at its commit.
Thanks to xet7 !
2026-08-13 19:05:59 +03:00
Lauri Ojansivu b9e00dbb85 Close two open issues the current code already answers.
Both were in the CHANGELOG's TODO Later list as "needs the running app", and
both are decided by files that can be read.

#5052, "Attachments cannot be opened (.eml)": a blank page in the browser and
nothing usable in Thunderbird, after a board was copied. Three mechanisms could
produce that and each is handled now.

  - The NAME. An unknown MIME used to append .bin (#6589), and Thunderbird will
    not open a .bin. Every type a mail file arrives as - message/rfc822,
    application/octet-stream, none at all - now keeps the extension, and the
    board copy names copies with the same function the upload used.
  - The SERVING. message/rfc822 is in neither the dangerous-types nor the
    safe-inline list, so it takes the "unknown types" branch: Content-Type
    application/octet-stream, Content-Disposition attachment, under the file's
    own name. Inline is what shows a browser a blank page for a type it cannot
    render.
  - The FILE. An attachment whose recorded versions[].path and on-disk name had
    diverged is found by the same search reading uses (#6589), so a copied
    board's attachments open even when the database's idea of the path is stale.

#5081, "Correct display of owner, member and assignee on mini cards": owner on
the very left, members next, assignees on the very right, wrapping to a second
right-aligned row when they do not fit. That is what the minicard renders
today, and the reason is the float - three groups that float to the inline end
are laid out RIGHT to left in DOM order, so the markup's assignees, members,
creator renders as creator | members | assignees. Each avatar floats as well,
so a row that does not fit wraps and stays right-aligned, and an empty group is
display:none rather than a gap.

tests/openIssuesVerifiedFromCode.test.cjs pins both, and #5052 is removed from
TODO Later.

Thanks to xet7 !

Fixes #5052,
Fixes #5081.
2026-08-13 19:05:59 +03:00
github-actions[bot] 1c3068c08f Bump versions for v10.90 2026-08-13 13:16:11 +00:00
Lauri Ojansivu 75367e0e15 Prepare v10.90 release 2026-08-13 16:14:58 +03:00
Lauri Ojansivu e5506c5767 Translations: error-watch-disabled in 135 languages.
The string the watch popup shows when the server refuses - added with the fix
for the silent Change watch popup, and pulled into every language file as the
English source, which is the placeholder state.

Filled directly, as CLAUDE.md's translation policy says: each language's own
existing strings and ordinary kanban vocabulary are the reference, no external
service is involved, and only PLACEHOLDERS were written - a value that already
differed from English would have been left alone.

Fourteen of these files are written in a language other than the one their code
names: ary holds Persian, tlh German, vo and zgh French, vl-SS Dutch, ve and its
variants Zulu, ace Indonesian, cmn and wuu-Hans simplified Chinese, yue_CN
traditional. Each follows what is IN the file, because that is what a reader of
that file sees - cmn and wuu-Hans say 版块 for board, which is the word those
files already use.

English and the en-* variants keep the source string, by design. 135 files
changed; verify-human-preference.mjs still passes 10/10.

Thanks to xet7 !
2026-08-13 16:08:04 +03:00
Lauri Ojansivu 2e1f4c8824 CLAUDE.md/AGENTS.md: a commit that fixes an issue ends with Fixes #NNNN.
The format was in the file, in the 'Fix open issues' process and in the commit
template, but not as a rule with its edges: when it applies, when it must NOT,
and what a commit with no issue behind it looks like. Those edges are where it
goes wrong - #6586 was closed by the umlaut fix and REOPENED for the five
follow-ups in its next comment, so it is fixed again only when those are done,
and #6585 has a reporter who is still stuck, so a commit that improves its
diagnostics references it and does not close it.

Thanks to xet7 !
2026-08-13 16:03:12 +03:00
Lauri Ojansivu 03a99e67b1 CHANGELOG: point the recovery-commands entry at its commit.
Thanks to xet7 !
2026-08-13 15:53:30 +03:00
Lauri Ojansivu 073a327d62 Snap: two commands for a snap serving the older of its two copies.
#6583, comment 5280504937: "The migration and the update to 10.83 startet at
11.08.2026 at 6:35 pm and migration failed. Now we just see the old data from a
migration we did in july 2026. ... Which steps exactly could we do, to restore
the database with our most recent data?"

Everything needed to answer that already existed - db-eval evidence,
database-choose.mjs, database-merge-missing.mjs, database-autopick - and none of
it was a command anybody could run. `snap run wekan.problems` said "No problems
detected", which is true of the things it checks and no help here.

  sudo snap run wekan.database-compare    # what does each copy hold?
  sudo snap run wekan.database-merge      # bring the missing documents across

compare starts each database on a temporary port, counts its documents and finds
the newest moment its data carries, and prints both sides. The running WeKan is
not disturbed and nothing is written - it is database-autopick --dry-run, so
there is one implementation of "read both and compare" rather than a second one
that can drift. It also prints where both copies live and how to take a copy.

merge inserts the documents that exist in the MongoDB copy and not in the
FerretDB one: database-autopick --to-ferretdb. It overwrites nothing, deletes
nothing and reads the MongoDB files only, so it is safe to run without first
knowing which copy is "right" - WeKan's history is append-only, so merging can
only ADD to what a card shows, and the stranded work becomes readable in that
card's History. It asks for a copy of $SNAP_COMMON first, with the command, and
takes --dry-run.

Both are registered in snapcraft.yaml and snapcraft-core26.yaml. The removed
`snap run wekan.database` switch is gone from core26 too, where it had been left
behind when the database setting was removed.

Thanks to waltermhl, lukechao and xet7 !
2026-08-13 15:53:30 +03:00
Lauri Ojansivu 6655bf7565 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-13 15:51:26 +03:00
Lauri Ojansivu 69e81632c1 CHANGELOG: an Upcoming section for the five fixes since v10.89.
v10.89 was cut with the Upcoming section it had, and nothing has recorded what
came after it: the archived cards that reappeared in the top swimlane and the
watch popup that answered nothing (both reported by email), #6589's .bin
attachment, #6591's Excel export, #6590 closed as #6588's other end, #6585's
reader diagnostics, and the two release-tooling fixes from the v10.89 run.

Grouped by area as CLAUDE.md asks - Boards, Cards and attachments, Exporting a
board, The snap, then the release tooling - with the summary paragraph, the
binaries table carried over from v10.89, and each entry linking its own commit.

Thanks to xet7 !
2026-08-13 15:51:25 +03:00
Lauri Ojansivu a70e69767f Snap: a Launchpad job that runs out of time says the build is still going.
v10.89's riscv64 leg ran 5h50m - its cap - with Launchpad still printing
"Building: riscv64", and the run showed CANCELLED and nothing else. What is
true at that moment is worth saying: the Launchpad build is NOT cancelled with
the job, it keeps its name, and re-running this job reconnects to the same
build and downloads the snap rather than starting another one.

The cap goes to 360 minutes, which is the maximum a hosted runner allows, and a
cancelled job now prints that explanation plus any Launchpad URL its logs
carry.

Thanks to xet7 !
2026-08-13 15:51:25 +03:00
Lauri Ojansivu 224e2cc9c4 Tests: accept the --repo argument in the gh release guards.
The two guards matched the exact string `gh release upload "v${VERSION}"`,
which no longer exists: every call names the repository now, because a job that
flattened its history for the Launchpad push has no remote for gh to infer one
from. Both still require --clobber and the verification that follows, and they
additionally require --repo.

Thanks to xet7 !
2026-08-13 15:51:25 +03:00
Lauri Ojansivu d38ca46803 Release: gh needs to be told which repository.
v10.89's snap-launchpad armhf, ppc64el and s390x jobs published the snap and
then failed on the line that attaches it to the GitHub Release:

  Revision 3661 created for 'wekan' and released to 'beta', 'candidate',
    'edge', and 'stable'
  no git remotes found
  Error: Process completed with exit code 1

Those jobs flatten history so the Launchpad push stays small, and the git
remote goes with it - so gh had nothing to infer the repository from. The snap
was in the store and not on the release, which reads like a failed build.

Every `gh release upload|view|edit` in every workflow now passes
--repo "${GITHUB_REPOSITORY:-wekan/wekan}", so the call does not depend on what
the checkout looks like, and tests/workflowRepoScripts.test.cjs fails a bare
one.

Thanks to xet7 !
2026-08-13 15:51:25 +03:00
Lauri Ojansivu 8ec78bb14c Archived cards stay archived, and a silent watch button says why.
Three email reports, 2026-08-13.

"PREVIOUSLY ARCHIVED CARDS (SOME SEVERAL YEARS OLD) HAVE REAPPEARED. THESE
CARDS HAVE INCORRECTLY BEEN PLACED IN THE TOP SWIMLANE."

The schema upgrade's swimlane rescue treated "this card's swimlane is archived"
as breakage and moved the card to the board's first VISIBLE swimlane. But
archiving a swimlane is how a whole swimlane is put away: its cards stay where
they are, archived: false, hidden because the swimlane is hidden. So every card
anybody had ever archived that way was hoisted back into view, years later, in
the top swimlane.

A card is orphaned when its swimlane does not EXIST, or belongs to another
board - #1959, which is still rescued. loadVisibleSwimlanes now also returns
existsById, and both the check and the write use it.

#1971, the issue the sweep cited, is about cards ADDED in List view landing in
an archived swimlane, and that is fixed where cards are created:
getDefaultSwimline() picks a non-archived swimlane. It never needed a sweep
over data somebody archived on purpose. The two guards that pinned the sweep
now pin the opposite, with the reason in them.

"SILENT DOES NOT RESPOND. IF WE TRY TO CHANGE IT DOES NOT CHANGE. NOTHING
HAPPENS." (the board's Change watch popup)

Driven against a running WeKan, the popup works for a board MEMBER - level
written, check mark moved, popup closed - and does nothing at all for anybody
who reaches the board another way:

  login as non-member admin: ok
  watch -> ERROR error-board-notAMember

A board is shared four ways: membership, an organisation, a team, and (#5850)
an email domain. Only the first puts anybody in `members`, and the watch method
asked hasMember(). Everyone sharing through an org, a team or a domain could
open the board, see the button, and be refused the moment they used it. It now
asks whether the user may SEE the board, through the same
boardVisibilitySelectors the publications use, so a watch can never be granted
where the board is not visible - and a revoked share (isActive: false) still is
not.

The other half is why nobody could tell: the popup closed on success and did
NOTHING otherwise, so a refusal was indistinguishable from a dead button. It
reports the reason now - the watch feature being off in the Admin Panel, or the
board not being visible - and en.i18n.json gains error-watch-disabled, which
was thrown but never translated. The click also reads its level with String(),
since Blaze can hand back a boxed String that `typeof x === 'string'` refuses.

Thanks to xet7 !
2026-08-13 15:51:25 +03:00
Lauri Ojansivu a712db9473 Fix the board Excel export, and the attachment stuck as .bin.
#6591 - "Board Settings -> Export board -> export/Excel didn't work".
Reproduced against a running WeKan: CSV and JSON of the same board answered
200, Excel never answered at all, and the log said nothing.

  csv:   HTTP 200 4758b
  json:  HTTP 200 4491b
  excel: Operation timed out after 30002 ms with 0 bytes received

Two faults, either of which hangs the browser on its own.

The zip: exceljs 4.7.3's streaming WorkbookWriter calls archiver the way
archiver 7 was called - Archiver('zip', opts) - and WeKan moved to archiver 8
for the low-memory backup zips. archiver 8 is ESM and exports classes, so that
is "TypeError: Archiver is not a function"; the export has been broken since
the bump. Supplying the missing factory does not rescue it: archiver 8's
readable-stream then refuses the objects exceljs appends ("input source must be
valid Stream or Buffer instance"), as an error EVENT. So createWorkbookWriter
asks what archiver exports and, when it cannot stream, returns a buffered
writer with the same API that writes the whole workbook on commit() - the path
the export used before it was made streaming. Bounded memory is what is lost,
not the export.

The silence: the route called exporterExcel.build(res) without awaiting it, so
the rejection went nowhere - no 500, no log, and a response that was never
written or ended. Every sibling route awaits; this one did not. It does now,
and a failure answers 500 with the reason.

#6589 - a .drawio attachment stored and shown as ".bin", unopenable, and
impossible to rename. Also two faults:

The name: a browser sends application/octet-stream for a type it does not
know, and the upload-time "correct the extension to the type" step took that
literally - mime.extension('application/octet-stream') is 'bin', so
"sso-proconnect-keycloak.drawio" became "...drawio.bin". Every unrecognised
format went the same way. An uninformative type now yields no extension at all,
while a type that does say something still corrects the name.

The rename: versions[].path and the file on disk had diverged, and rename used
the recorded path alone -

  Error: ENOENT: no such file or directory, rename
    '/data/files/attachments/6a7d66369c6aee799e857d36.drawio' -> ...

- while the READER already searched every layout WeKan has used and found the
file. That search is a method now (candidatePaths/resolveExistingPath) and
reading, renaming and deleting all use it, so an attachment that can be read
can also be repaired. When there is genuinely no file, the error names the
attachment instead of a path nobody recognises.

Thanks to rmb82, titver968 and xet7 !

Fixes #6589,
Fixes #6591.
2026-08-13 15:51:25 +03:00
Lauri Ojansivu 1f83020b4d Snap: say WHY each reader refused the data, and where to get an old MongoDB.
#6585 comment 5276581923 is a log that says everything except the useful part:

  [migration] mongod 7 could not open the data; trying the bundled mongod 5.0 ...
  [migration] mongod 7 could not open the data; trying the bundled mongod 4.2 ...
  [migration] The database files were made by an older MongoDB
              (MongoDB 4.2 or earlier can still read them).

Every reader was tried, each of them said something, none of it was shown - and
the conclusion recommends the version that had just failed. mongod 7 names the
version that can open the FORMAT; it cannot know the files are also damaged, or
left locked by an unclean shutdown.

So each reader now prints its own last words when it does not open the data
(reader_failed, the tail of migration-mongod.log), the "trying the bundled X"
lines name the reader being TRIED instead of blaming mongod 7 for all of them,
and when mongod 7 asks for a version this snap CARRIES, the report says it was
tried too and that --repair on a COPY is the usual next step.

And the other half of the report - "If you need us to run some external tools,
like an old mongodb, it would be good to provide a source for them": the log and
the explanatory page now link mongodb.com/try/download/community and
fastdl.mongodb.org, name the docker one-liner, and spell out mongod --dbpath /
mongodump on a COPY, never the original.

Also closes #6590, which is #6588 from the other end: the same
`this.__schemas__[...].validate is not a function` out of linkify-it 6, reported
as a card whose description hung a whole BOARD for everyone rather than one card
that would not open. Fixed by 75a23b76a on 2026-08-12; the regression test now
renders the exact onenote:///path/to/file.one#section-id={GUID} from the report,
description and checklist item alike.

Thanks to mueschel, titver968 and xet7 !

Fixes #6590,
2026-08-13 15:51:25 +03:00
github-actions[bot] 11bdb0abfa Bump versions for v10.89 2026-08-13 04:38:11 +00:00
Lauri Ojansivu dd250991b4 CHANGELOG: point the re-migration entry at its commit.
Thanks to xet7 !
2026-08-13 07:30:33 +03:00
Lauri Ojansivu e63ac0e77a Snap: a migration never runs over the database it already produced.
From #6583: "the migration re-ran yesterday (even though it had already run
successfully a few weeks ago). The .migration-to-ferretdb-done file is time
stamped yesterday ... That explains why I'm seeing old data."

The instance had been migrated and had served from FerretDB for weeks. The
marker went missing, the old staleness guard put it back on database=mongodb,
and the migration ran again - importing the MongoDB copy it had been made from,
over the database holding every change since. discard_partial_ferretdb could
then delete that database, because "partial" was assumed rather than checked.

Two locks now, opened by the same fact: the importer writes
migration-progress.json as it goes and resumes from it, so a FerretDB with data
and NO checkpoint beside it is a finished database in use, never a migration to
continue.

  - migration-control checks that before it probes, reads or deletes anything.
    Finding one, it marks the migration done, starts FerretDB, stops MongoDB and
    exits without importing.
  - discard_partial_ferretdb checks it again before removing a SQLite, and says
    so when it declines.

The MongoDB data is untouched either way. migration-pending already answers the
same question through database-role, so neither branch should be reachable,
which is why they exist: the cost of being wrong here is somebody's data.

Also from the v10.89 run:

  - The push preflight I added blocked the Docker publish on a false positive.
    GHCR hands back an OPAQUE token ("v1:wekan/wekan:1786595038984745940", no
    dots, no claims), and reading no "push" in it was treated as a refusal.
    Only an explicit refusal fails now - an access claim naming the repository
    whose actions omit push, which is what Quay and Docker Hub return - and an
    unreadable token says so and lets the push be the check. Verified against
    all three registries.
  - snap-launchpad armhf, ppc64el and s390x uploaded a file that existed and
    was not a snap ("could not unsquash snap file"). A .snap is squashfs, so it
    starts with "hsqs": the build step now checks that and a 50 MB floor,
    deletes a stale file before each attempt so a truncated one cannot be picked
    up, deduplicates the two globs that matched the same file twice, and the
    upload step checks the magic again with its own message.

Thanks to lukechao and xet7 !
2026-08-13 07:30:33 +03:00
Lauri Ojansivu b32d8977e7 CHANGELOG: point the Docker-variant entry at its commit.
Thanks to xet7 !
2026-08-12 23:45:25 +03:00
Lauri Ojansivu e533829f5b Docker: publish only wekan, on all three registries. Variants commented out.
wekan-ondra and wekan-gantt-gpl are SNAP names - a snap name cannot be changed
once people have it installed - and as Docker images they were only ever a
second name for the same image. The release tagged them on all three registries
for two versions; it does not any more.

Six extra repositories across three registries, each with its own visibility and
its own push permission, is six new ways for a release to fail in order to
publish a copy of something already published, and v10.88 failed exactly that
way an hour into an emulated build:

  ERROR: failed to push quay.io/wekan/wekan-ondra:v10.88:
    unauthorized: access to the requested resource is not authorized

The wekan image still goes to all three: wekanteam/wekan, quay.io/wekan/wekan
and ghcr.io/wekan/wekan, version tag and latest.

The variant -t lines are COMMENTED OUT, not deleted, with what it would cost to
uncomment them written beside them, and the same for their names in the two
verification loops and the push preflight. docker-variant.yml and
releases/docker-publish-variant.sh stay for publishing one out of band; both are
started by hand and no release calls them.

The SNAPS are untouched: snap-variants still builds and publishes wekan-ondra
and wekan-gantt-gpl on amd64 and arm64, and SNAP_AUTH still carries all three
snap names. Nothing is deleted from any registry either - the tags that exist
keep working for whoever pinned them, they only stop gaining versions.

tests/dockerNoVariantImages.test.cjs replaces the two suites that pinned the
opposite decision: no active variant tag or check, the comments and their
reason present, the manual workflow still dispatch-only, and the snaps still
built.

Thanks to xet7 !
2026-08-12 23:45:25 +03:00
Lauri Ojansivu 905b1bb73f CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-12 23:01:57 +03:00
Lauri Ojansivu 65c5803f0a CHANGELOG: point the workflow-path entry at its commit.
Thanks to xet7 !
2026-08-12 23:01:56 +03:00
Lauri Ojansivu 18b8812620 Release: fix the path a pushd invalidates, and the last bare downloads.
The v10.89 run failed four more jobs, the same two mistakes one step further
along.

THE WINDOWS JOBS check this repository out to path: src, so the scripts were
addressed as src/releases/... - correct until the bcrypt step does
pushd "$TMP", after which a relative path resolves against a temp directory:

  bash: src/releases/npm-retry.sh: No such file or directory

The location is now fixed before anything moves - SRC="$PWD/src" at the top of
the step, then "$SRC/releases/..." - in the eighteen blocks that need it, and
the same for the ucs job's univention/.

THE DOWNLOADS THAT ARE NOT IN A WORKFLOW. snapcraft.yaml builds the snap in its
own container and sandstorm-src/build-deps.sh runs on the runner; both still
used a bare curl, and github.com's 503s took them out:

  :: curl: (56) Connection died, tried 5 times before giving up
  :: caddy: no linux/arm64 archive in Caddy 2.11.4 - nothing left to try.
  ==> [4/7] FerretDB v1 (amd64) at deps root
  curl: (56) Connection died, tried 5 times before giving up

Both go through releases/fetch.sh now - the snap parts reach it through
CRAFT_PROJECT_DIR, since snapcraft mounts the project into the build - so the
caddy, MongoDB, mongod 4.2/5.0 and OpenSSL downloads, the meteor-spk and
Node.js tarballs and the FerretDB binary all wait an outage out. The Caddy
version LOOKUP stays a plain curl: when it fails the pinned version is used,
which is what it is for. curl https://install.sandstorm.io | sudo bash became a
download and a run, because a pipe cannot be retried.

tests/workflowRepoScripts.test.cjs grew the two checks that would have caught
these, and releaseBuildJobsCheckout accepts $SRC/releases as the same statement
as src/releases, made absolute.

Thanks to xet7 !
2026-08-12 23:01:55 +03:00
github-actions[bot] cc72a84f4c Bump versions for v10.89 2026-08-12 19:37:43 +00:00
Lauri Ojansivu e508f27aec Prepare v10.89 release 2026-08-12 22:36:34 +03:00
Lauri Ojansivu dde460eec0 CHANGELOG: the entries written after v10.88 was tagged go under Upcoming.
release-all.sh renamed the Upcoming section to v10.88 at 21:22 and the release
notes were taken from it then. The snap work (21:55, 22:12) and the workflow
paths fix (22:24) came after that, so they were never part of what v10.88
shipped. They move to a new Upcoming section, grouped by area, and v10.88 keeps
what it was released with. All commit links resolve.

Thanks to xet7 !
2026-08-12 22:28:43 +03:00
Lauri Ojansivu 2626796ee2 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-12 22:25:20 +03:00
Lauri Ojansivu 09de31496c CHANGELOG: wrap the two error lines at 80.
Thanks to xet7 !
2026-08-12 22:25:18 +03:00
Lauri Ojansivu 52b5f5a6b4 CHANGELOG: point the workflow-paths entry at its commit.
Thanks to xet7 !
2026-08-12 22:25:18 +03:00
Lauri Ojansivu bdacff80e2 Release: a repo script only runs where the repo is, and check push first.
The v10.88 run lost seven jobs to two mistakes of the same kind.

THE SCRIPTS WERE NOT ON DISK YET. Putting the downloads and package installs
behind releases/fetch.sh and releases/apt-install.sh turned steps that needed
nothing into steps that need this repository:

  bash: /home/runner/work/wekan/wekan/releases/apt-install.sh: No such file or directory
  bash: D:\a\wekan\wekan/releases/npm-retry.sh: No such file or directory

build-extra-arches had "Install dependencies" as its first step, before
actions/checkout - which was fine while it was a plain apt-get. The Windows
jobs check this repo out to path: src, so $GITHUB_WORKSPACE/releases does not
exist there; they already called the other scripts as src/releases/... Same two
shapes in the Flatpak job (no checkout at all), release-all-missing's
extra-arches and charts (path: wekan), and ucs (path: univention).

tests/workflowRepoScripts.test.cjs reads every workflow for a step that runs a
repo script before its job checks out, or through a prefix that does not match
that job's checkout path, and for a script a workflow names but the repo does
not have.

AND THE PUSH THAT WAS NEVER GOING TO WORK. The docker job built every
architecture under emulation for most of an hour and threw it away on

  ERROR: failed to push quay.io/wekan/wekan-ondra:v10.88: unauthorized

The credentials were fine; the login check passed. Quay grants push per
REPOSITORY and wekan-ondra had just been created, so the account had no rights
on it. The job now asks each registry whether it would grant a push token for
each of the nine images, before building anything, and fails in seconds naming
the per-repository permission. No answer is a warning - that is the network.
Verified against the real registries: an unauthenticated request is granted
"pull" by Quay and nothing by GHCR, which is the shape this detects.

Thanks to xet7 !
2026-08-12 22:25:18 +03:00
Lauri Ojansivu 5d51f49802 CHANGELOG: point the database-setting removal entry at its commit.
Thanks to xet7 !
2026-08-12 22:25:18 +03:00
Lauri Ojansivu bb76e7bf10 Snap: remove the database setting. There is one answer, and it is FerretDB.
`snap set wekan database=mongodb|ferretdb` is gone, and so is
`snap run wekan.database`. WeKan runs on FerretDB on every platform; MongoDB is
in the amd64/arm64 snaps to be READ while a migration is owed.

A setting could say something the data did not support, and every way it could
was a report: set to mongodb it kept a site on the database the snap migrates
away from, for good, because nothing ever set it back - including instances a
failed migration or a wrong staleness guess had put there; set to ferretdb with
no FerretDB present it would have served an empty site, so the guard against
that had to exist anyway; and each script carried its own copy of "which
database is this, then".

snap-src/bin/database-role replaces it. One helper, asked by wekan-control,
mongodb-control, ferretdb-control, migration-pending, attachment-repair and the
configure hook, answering from the data: a FerretDB with something in it and a
migration that has finished means ferretdb, MongoDB data with the migration
still owed means mongodb. An interrupted migration is told from a finished one
by the importer's checkpoint, so a partial FerretDB resumes and a finished one
whose marker went missing is not migrated over again (#6585). An empty FerretDB
beside a MongoDB full of boards is never served.

The explanatory page is no longer a dead end: when this snap cannot read the
MongoDB files but a FerretDB copy is there, the copy is served - older beats
unreadable - and the page says the snap keeps trying rather than giving a
command to type. A snap that still has the old setting is told once that it is
ignored, and it is unset.

docs/Platforms/FOSS/Container/Snap/Migration-to-FerretDB.md is the design in one
page: all text data to SQLite, CollectionFS and Meteor-Files attachments to the
filesystem, the card History with it, which MongoDB versions can be read, when
it runs, what happens when it fails, and how two copies are reconciled. The
Admin Panel, Snap and CPU-platform docs point there instead of documenting a
setting that no longer exists.

Nine guards updated; each says in it which decision changed and why.

Thanks to xet7 !
2026-08-12 22:25:18 +03:00
Lauri Ojansivu 539dda7d5d CHANGELOG: point the snap-migration entry at its commit.
Thanks to xet7 !
2026-08-12 22:25:18 +03:00
Lauri Ojansivu 60b4ceda3b Snap: end up on FerretDB, whatever the snap was running before.
Three ways a snap could stay on MongoDB for good, all of them reported.

A DATABASE NOTHING COULD OPEN. A mongod starts only on data whose
featureCompatibilityVersion is at most one major behind it, so the readers
covered FCV 6.0/7.0 (mongod 7), 4.0/4.2 (mongod 4.2) and 3.x (the 3.2 tools),
and nothing covered 4.4 or 5.0 - although this snap shipped MongoDB 5 in
February 2023 and 6.0.6 only in May. Those instances got .mongodb-data-too-old
and an explanatory page. mongod 5.0 is bundled now as a fourth read-only
reader, tried between 7 and 4.2, through cpu-exec because MongoDB 5.0 requires
AVX on x86_64.

"WEKAN CHANGED TO OLD MONGODB DATA." When the migrated FerretDB copy had fallen
behind the MongoDB beside it, wekan-control switched the snap to
database=mongodb - back onto the database it is migrating away from, and when
the detector guessed wrong (#6583) onto a copy that was weeks behind. It merges
now: database-autopick --to-ferretdb copies the documents MongoDB has and
FerretDB does not into FerretDB, inserting what is missing and overwriting
nothing, and WeKan carries on there. The history is append-only, so that work
lands in the card History. Switching to MongoDB is only the fallback for when
the merge cannot run, because serving a copy that is behind is the complaint
itself.

A FAILED MIGRATION THAT NEVER TRIED AGAIN. A failure set migrate=off and
nothing ever set it back on. It is recorded now - attempts, when, and which
snap revision - and retried by itself: immediately after the next refresh,
otherwise after a wait doubling from an hour to a day. The same record replaces
migrate=off on the unreadable-database path, which is what lets this release's
5.0 reader reach the instances already given up on. snap set wekan migrate=off
still stops it: an admin saying "not now" is a decision, not a failure.

Nothing is deleted; the MongoDB data stays in $SNAP_COMMON.

tests/snapAlwaysFerretdb.test.cjs covers all three. Two existing guards pinned
the old decisions and now pin these, with the reason written in them.

Thanks to xet7 !
2026-08-12 22:25:17 +03:00
github-actions[bot] 7d645d51d0 Bump versions for v10.88 2026-08-12 18:23:48 +00:00
Lauri Ojansivu 6daaa86e30 Prepare v10.88 release 2026-08-12 21:22:44 +03:00
Lauri Ojansivu 6c067b8c2b CHANGELOG: point the browser-test session entry at its commit.
Thanks to xet7 !
2026-08-12 21:16:41 +03:00
Lauri Ojansivu 4c68a8a49a Browser tests: a login must not log the test's other tabs out.
The last test run failed one test in all three browsers, having passed for a
month:

  02-cards-open-view.e2e.js:66 copy-link ... opens the card in full-screen view
  Error: Token login failed: You've been logged out by the server.

Driven over DDP against the running server, with a token seeded the way the
fixtures seed one:

  session A: ok           tokens: [CfgBWImyytla]
  session B: ok           tokens: [CfgBWImyytla]  <- two sessions, one token
  after B logged out      tokens: []              <- logout removed it
  session C (same token): ERROR You've been logged out by the server.

A seeded user has ONE resume token and Meteor.logout() deletes it on the
server, for every session sharing it. loginWithToken called it when the page
was logged in as somebody else, so switching users in one page stranded the
test's other pages. The copy-link test is the only one that logs a second page
in, which is why it was the one that failed.

loginWithToken now ends the previous session in the client - drop the three
Accounts keys, reload - which it already does for its own first load. The
token is untouched and the page still arrives with no user. logout() stays as
its own helper: 05-admin-users logs out and back in with a password, which is
what a user really does.

Two pages are two browsers, so they get two tokens: db.addResumeToken() adds
one to an existing user and the second tab uses it. That tab also stopped
waiting for networkidle before looking for the card - a card renders when its
subscriptions land, not when the network goes quiet.

tests/e2eSessionTokens.test.cjs pins both rules and scans every spec for two
logins sharing one token. Verified against the running server: the copy-link
test passed four times in a row, and 05-admin-users, 32-org-team-feature-
toggles, 33-board-domains and 38-impersonation - the specs that switch users -
all pass.

Thanks to xet7 !
2026-08-12 21:16:40 +03:00
Lauri Ojansivu 2d90c269f1 CHANGELOG: the two fixes written after v10.87 was tagged go under Upcoming.
release-all.sh renamed the Upcoming section to v10.87 at 20:10 and the release
notes were extracted from it there and then. The download-retry and apt-install
fixes were committed after that, so they were never part of what v10.87
shipped: they move to a new Upcoming section, and v10.87 keeps the summary it
was released with - the npm install retry alone.

Thanks to xet7 !
2026-08-12 21:04:30 +03:00
Lauri Ojansivu e78709c074 CHANGELOG: point the apt-install entry at its commit.
Thanks to xet7 !
2026-08-12 20:58:09 +03:00
Lauri Ojansivu a6edf86bc3 Install packages through a script that survives a mirror mid-republish.
The bump job of the same afternoon died on a repository the release does not
use:

  E: Failed to fetch https://dl.google.com/linux/chrome-stable/deb/dists/stable/main/binary-amd64/Packages.gz
     Hash Sum mismatch
  E: Some index files failed to download.
  Error: Process completed with exit code 100.

It was installing python3 and curl. A GitHub runner ships with google-chrome,
microsoft-prod, azure-cli and docker repositories configured, and apt-get
update fails as a whole when any one of them serves an index that does not
match its own hashes.

releases/apt-install.sh retries the update, clearing the cached lists first,
because a Hash Sum mismatch is a cached index disagreeing with the server and
re-reading it reports the same thing. If it still fails, it moves the
third-party lists aside and updates from the distribution archive alone - where
every package a release job installs comes from. Both steps say so in the log,
and a mirror that never comes back still fails the job, naming the mirror.

Every apt-get update + install pair in release-all.yml, release-all-missing.yml,
sandstorm.yml, sandstorm-store.yml, meteor-spk.yml, Flatpak.yml and the emulated
build container goes through it.

tests/releaseAptInstall.test.cjs drives it with a fake apt-get that mismatches
on demand and a fake sudo that records rather than runs. cpuExecWiring's
qemu-user-static guard now looks for the package wherever it is asked for, not
only after a literal apt-get install.

Thanks to xet7 !
2026-08-12 20:58:08 +03:00
Lauri Ojansivu 9f4aa4f8d1 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-12 20:45:16 +03:00
Lauri Ojansivu bb413493cc CHANGELOG: point the download-retry entry at its commit.
Thanks to xet7 !
2026-08-12 20:45:15 +03:00
Lauri Ojansivu e771f8c661 Wait out a 503, and keep telling it apart from a 404.
github.com spent the afternoon returning 503, and it took a second release run
an hour after the first - this time in build-amd64, downloading the FerretDB
binary:

  curl: (22) The requested URL returned error: 503
  Warning: Problem : HTTP error. Will retry in 10 seconds. 5 retries left.
  ...
  curl: (56) Connection died, tried 5 times before giving up

nodejs.org was fine in the same step, seconds earlier: the bundled Node.js
downloaded and verified. --retry 5 --retry-delay 10 is fifty seconds.

releases/fetch.sh downloads a file in a release now. 5xx, 429, 408 and the
connection errors are retried on a backoff of about fifteen minutes, and every
download in release-all.yml, release-all-missing.yml, embed-verified-node.sh,
install-node-for-arch.sh and resolve-node-source.sh goes through it. The
Dockerfile copies it in beside resolve-node-source.sh, which asks it which
Node.js builds exist, and its two bare wgets get the same --retry-on-http-error
as the others.

A 404 is not an outage, and that is the half a longer --retry cannot do. The
preflight that skips an architecture with no Node.js build yet, the MongoDB
tools not built for every platform, the .sha256sum a source may not publish:
those fail at once, and quietly with --optional.

An existence check has three answers - present, absent, and the server would
not say. The third used to be the second, which is how a 503 could drop a
platform from the Docker image or skip an architecture whose binary was
published all along. check-arch-binaries.sh, require-binaries.sh and the docker
job's platform list now stop and say to re-run instead.

tests/releaseDownloads.test.cjs runs it against a local server that 503s, 404s
and 429s on demand, and reads the workflows for a download that still goes
straight to curl.

Thanks to xet7 !
2026-08-12 20:45:15 +03:00
github-actions[bot] a02959dde6 Bump versions for v10.87 2026-08-12 17:11:28 +00:00
Lauri Ojansivu 024ae3c1cd Prepare v10.87 release 2026-08-12 20:10:36 +03:00
Lauri Ojansivu e8843f51a1 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-12 20:08:32 +03:00
Lauri Ojansivu c1bed83464 CHANGELOG: point the npm-retry entry at its commit.
Thanks to xet7 !
2026-08-12 20:08:31 +03:00
Lauri Ojansivu 644e61f6a3 Retry the release's npm installs when the far end 5xxes.
The v10.86 run failed in build-amd64 with

  npm error code E503
  npm error 503 Service Unavailable - GET
  https://github.com/meteor/node-source-map-support/tarball/81bce1f9...

github.com was 503ing for a few minutes. npm's own fetch-retries is two quick
attempts and does not cover a git tarball fetch at all, so the install gave up;
every other Linux bundle is repacked from the amd64 one, so the whole release -
eleven architectures, the Docker images, the snap - was skipped and had to be
started again by hand. Every curl in that workflow has carried --retry 5 for
years; the npm installs carried nothing.

releases/npm-retry.sh runs an npm command with backoff (15s, 30s, 60s, 120s,
five attempts) and every npm install, npm pack and meteor npm install in
release-all.yml now goes through it, including the two inside docker run, which
get releases/ mounted the way the arch builds already do. Release All Missing
repacks through the same container script and inherits it.

A real npm error is NOT retried: ERESOLVE, a 404 for a package that does not
exist, a gyp compile error fail on the first attempt saying they are not a
network reason, because five attempts at an emulated arm64 install that was
never going to work is half an hour spent to print the same message. Only 5xx,
429 and the socket errors count as transient.

The Meteor installer is downloaded and then run instead of curl | sh, which
cannot be retried once sh is half way through the script.

tests/npmRetry.test.cjs pins both halves against a fake npm and reads the
workflow for a bare npm install.

Thanks to xet7 !
2026-08-12 20:08:30 +03:00
github-actions[bot] f1a569dae1 Bump versions for v10.86 2026-08-12 16:40:39 +00:00
Lauri Ojansivu 5ca9f20e03 Prepare v10.86 release 2026-08-12 19:39:48 +03:00
Lauri Ojansivu 52fe1822bf CHANGELOG: point the variant-images entry at the commit that survived the amend.
Thanks to xet7 !
2026-08-12 18:58:44 +03:00
Lauri Ojansivu 1d76e0d434 Publish the variant images on Docker Hub and Quay too, in the release build.
wekan-ondra and wekan-gantt-gpl are byte-identical to wekan/wekan apart from
the snap name:, so their images were retags of the released manifest, pushed
whenever somebody remembered to start docker-variant.yml. GHCR already got
them from the release build; Docker Hub and Quay now do as well - eighteen
tags out of the one docker buildx build --push, so a variant tag carries the
release's own digests for every architecture and cannot name a version it does
not contain.

docker-variant.yml stays for republishing one out of band, and is still
workflow_dispatch only.

A pushed tag is not a pullable tag: after the credentialed verification of all
nine images, a new step asks each one anonymously, the way a user does, and a
401/404 fails the job with the fix for that registry. Quay creates a new
repository private, so quay.io/wekan/wekan-ondra will pull for the pusher and
for nobody else until its visibility is changed - the same trap
ghcr.io/wekan/ferretdb fell into. An unreachable registry is a warning, not a
failure.

Thanks to xet7 !
2026-08-12 18:58:28 +03:00
Lauri Ojansivu b091a8ec57 CHANGELOG: the variant images on GHCR, and the chart's image going back there.
Two corrections and an addition to the Helm chart subsection: the default image
is ghcr.io/wekan/ferretdb again now that the package is public, the paragraph
about it tells the whole story (private package, org policy, what Artifact Hub
said and what a cluster would have said), and the release publishing
wekan-ondra and wekan-gantt-gpl on GHCR from its own build gets an entry of its
own - including that Release All Missing has no Docker part to change.
2026-08-12 18:45:13 +03:00
Lauri Ojansivu 4a519b6562 Release: publish the two variant images on GHCR from the release build itself.
wekan-ondra and wekan-gantt-gpl are the same WeKan as wekan - those repositories
are byte-identical to wekan/wekan apart from the snap `name:` in snapcraft.yaml,
which is why their Docker images have never been rebuilt but retagged from the
released manifest (docker-variant.yml, run by hand).

On GHCR they are now tagged in the release's own `docker buildx build --push`,
beside ghcr.io/wekan/wekan. Being in the SAME build is what makes it safe: the
variant tags carry the release's own digests for every architecture, there is no
second emulated build to go wrong, and there is no window in which a variant
image can differ from the release it names. The push verification loop asks the
registry about them like any other tag, so a variant tag that did not arrive
fails the job rather than being found months later by somebody on that CPU.

Docker Hub and Quay variants stay manual: docker-variant.yml exists so the
maintainer publishes those deliberately, and this does not change that.

Release All Missing has no Docker part to change - its jobs are plan,
extra-arches, appimage, flatpak, charts and done. It fills in artifacts for
versions whose image already exists; the charts job even refuses to publish a
chart for a version that has none.

The condition every bit of this rests on is written where the tags are, and in
docs/Design/Autoupdate/Snap-Ondra-Gantt.md: if a variant repository ever stops
being identical to wekan/wekan, these tags become a lie and that variant needs
its own build from its own Dockerfile.

The chart's default image also goes back to ghcr.io/wekan/ferretdb in the same
round, now that the package is public - the guard that briefly required it NOT to
be the GHCR one is replaced by the rule that outlives either state: the default
must be one of the three registries wekan/FerretDB pushes to, and the chart must
say which of them can be pulled.

Verified: the workflow parses, tests/chartsFerretdb.test.cjs passes (18 tests),
whole node suite 385 suites, 0 failed.

Thanks to xet7 !
2026-08-12 18:43:36 +03:00
Lauri Ojansivu d2a220a785 Pin @meteorjs/rspack to the version the Meteor rspack plugin asks for.
The build stopped before any test ran:

  => 🔧 Installing 1 dev dependency...
  => ❌ Failed to install Rspack
  For dev dependencies, run: meteor npm install -D @meteorjs/rspack@2.1.0

Nothing was missing. node_modules already had @meteorjs/rspack 2.1.0 and the
lockfile already resolved 2.1.0. What did not match was the DECLARATION:
package.json said `^2.1.0-beta.0`, and the plugin compares against the declared
string rather than the installed version, so it decided the dependency was not
there and tried to install it.

The trigger was mine: the dependency batch moved the Meteor package rspack from
1.2.0 to 1.2.1, and 1.2.1 asks for `@meteorjs/rspack@2.1.0` exactly. The
`^2.1.0-beta.0` range is older than that - it came in with "Updated dependencies."
on 2026-07-31 - and it was never really a prerelease in practice: npm resolves
that range up to the stable 2.1.0, which is what was installed all along. Only
the declaration still named a beta.

So it declares 2.1.0, which is both what the plugin wants and what is already on
disk. Nothing is downloaded and the resolved version does not change.

The package-lock.json diff also carries an unrelated npm reshuffle from the same
install - gcp-metadata re-nested under google-auth-library - which is left as npm
wrote it.

Verified by xet7: building and running WeKan works again.

Thanks to xet7 !
2026-08-12 18:40:33 +03:00
Lauri Ojansivu 67a3705cf5 Charts release: declare the images and the changes, and default to one that pulls.
From an Artifact Hub scan of the published chart:

  error scanning image ghcr.io/wekan/ferretdb:latest: image not found
  (package wekan:10.86.0)

Two separate things behind one line, and the second is the serious one.

The package 10.86.0 was a chart published out of band and already taken back off
gh-pages; the index there is clean again (230 entries, checked against the
registry and against the archives: every digest, inner chart version and
appVersion matches its entry, which is what Artifact Hub reads).

The image is the real fault, and it would have shipped in the next release.
ghcr.io/wekan/ferretdb cannot be pulled by anyone who is not logged in: a GHCR
package is private until it is made public, and that one has not been - an
anonymous manifest request answers 403 DENIED, while the same request for
ghcr.io/wekan/wekan answers 200. In a cluster that is ImagePullBackOff. The chart
now defaults to quay.io/wekan/ferretdb, which was verified to serve the full
multi-arch set (amd64, arm64, arm v5/v6/v7, 386, ppc64le, riscv64, s390x,
loong64), as does wekanteam/ferretdb on Docker Hub.

AND THE RELEASE NOW WRITES VERSION INFO INTO THE CHART, which is what a release
knows and a chart does not. releases/release-charts.sh adds two Artifact Hub
annotations to Chart.yaml alongside the version bump:

  artifacthub.io/changes   "WeKan updated to v<version>" with links to that
                           release's notes and the CHANGELOG. A chart version
                           with no changelog is one nobody can tell apart from
                           the version before it.
  artifacthub.io/images    the images this chart pulls, with exact tags, so the
                           scanner reads a list instead of inferring one - an
                           inferred list is what named an image the chart does
                           not use. The database image is READ out of values.yaml
                           as it is written, so the declaration cannot drift from
                           what is pulled.

Both are rewritten every release and were checked against the registries for the
current pair: ghcr.io/wekan/wekan:v10.85 and quay.io/wekan/ferretdb:latest exist.

The whole charts job was then run end to end against a throwaway clone of the
repository, and it does what the git history of both branches shows: main gets a
"<version>.0" commit with the bumped Chart.yaml, the archive is packaged and
carried to gh-pages, index.yaml is rebuilt from the packages, and gh-pages gets
its own "<version>.0" commit before returning to main. The archive has the same
structure and the same naming as every previous release - wekan/ with Chart.yaml,
values.yaml and templates/ - minus the Chart.lock and vendored MongoDB subchart,
which no longer exist, and plus the two FerretDB templates.

tests/chartsFerretdb.test.cjs gains three checks: the default image must be one
of the two registries that serve it publicly (never the GHCR one) with the reason
written beside it, the chart's declared images and changelog link must match its
own appVersion and its values.yaml, and the release script must build both from
the version being released rather than repeating them.

Verified by running that suite (18 tests) and the whole node suite (385 suites,
0 failed).

Thanks to xet7 !
2026-08-12 18:14:24 +03:00
Lauri Ojansivu ee7baf0b23 CHANGELOG: the Helm chart gets its own subsection, with the release path and the index.
The chart switch was sitting under "test coverage work", which is not what it is.
It now has a subsection of its own with the three entries that belong together:
the switch to FerretDB (published by this release's charts job, not before it),
the two release-path guards - a version bump cannot rewrite the database image,
and old releases are not backfilled onto a database they never ran - and the
Artifact Hub report about packages whose container image was deleted, which the
index already excluded and the backfill would have put back.

The first entry also carries what came after it: every FerretDB setting saying
what it would be on MongoDB, both compose files linked, the production notes
pointed at from three places, and the Docker Hub mongo:7 the FerretDB image
replaced named beside the three registries that carry it.
2026-08-12 18:02:17 +03:00
Lauri Ojansivu de8a293850 Backfill: rebuild the chart index with the tool that checks images.
From an Artifact Hub scan report of wekan/charts, 2026-08-11:

  error scanning image docker.io/bitnami/mongodb:7.0.14-debian-12-r3 (package wekan:7.94.0)
  error scanning image ghcr.io/wekan/wekan:v8.30: image not found (package wekan:8.30.0)
  ... v9.12, v9.14, v9.38, v9.39, v9.62

Checked against the live registry: every one of the index's 230 entries points at
an image that exists, so the index itself is already clean - releases/reindex-
charts.py asks the registry about every image a package pins and leaves out the
packages whose image is gone, which is how those seven left the index.

The hole was here. backfill-charts.sh rebuilt the index with `helm repo index .
--merge`, and helm indexes what it FINDS: 135 packages on that branch have an
image that no longer exists - six WeKan images that were never published, and 129
charts vendoring a Bitnami MongoDB image Bitnami has deleted - so one --apply run
would have put all 135 back and mailed the owner again. It now calls
reindex-charts.py, the same tool release-charts.sh uses, so "a package whose image
is gone stays out of the index" is one rule in one place rather than two tools
that disagree.

The duplicate-entry repair that followed the merge is gone with it: it existed
because `--merge` preserved the duplicates an older release-charts.sh prepended,
and reindex-charts.py writes ONE entry per package keyed on the version inside
it, so a duplicate cannot be written. The rule it implemented - keep the entry
whose digest matches the package on disk - is exercised where it now lives,
tests/reindexCharts.test.cjs, which already covers it.

tests/backfillCharts.test.cjs loses the extract-and-run of that block and gains
the two checks that matter now: no `helm repo index` in the script, and helm
still required for the one thing only helm does (packaging).
tests/chartsFerretdb.test.cjs pins the same rule from the chart's side, including
that a registry which cannot be reached is never read as "image gone" - that
would drop good entries on a network hiccup.

Six packages whose WeKan image no longer exists were deleted from the charts
gh-pages branch in the same round; the 129 Bitnami ones are left, since their
WeKan image is fine and only the old subchart's database image is missing.

Verified by running both suites and the whole node suite (385 suites, 0 failed).

Thanks to xet7 !
2026-08-12 17:44:32 +03:00
Lauri Ojansivu 2ad02cccc5 Release path: the next release publishes the FerretDB chart, and only for releases that had FerretDB.
Checking the Release All / Release All Missing charts jobs against the chart
change turned up three things worth fixing and one worth stating.

WHAT ALREADY WORKED, verified rather than assumed: a release changes exactly
three things in the chart - appVersion, the chart version, and the WeKan image
tag - and packages whatever else is on the charts repo's main branch. That branch
now holds the FerretDB chart, so the next release publishes it. Simulated with
the real substitutions for a hypothetical 10.86: appVersion 10.86, chart 10.86.0,
image ghcr.io/wekan/wekan:v10.86, no dependencies, ferretdb enabled with
ghcr.io/wekan/ferretdb:latest, both ferretdb templates in the package, and the
FerretDB MONGO_URL helper.

The tag substitution is `tag: v<digits>` and every other image in the chart is
`tag: latest` - FerretDB and the two busybox images - so a WeKan version bump
cannot rewrite the database image. The test asserts that against the pattern
itself rather than against a copy of it.

BACKFILL NEEDED A FLOOR. releases/backfill-charts.sh fills holes in the published
index by packaging TODAY'S chart with an old release's numbers on it. Today's
chart installs FerretDB, and WeKan did not default to FerretDB until v10.00
(commit 4dbf99010, released 2026-07-18) - so a backfilled chart for v6.09 would
pair that image with a database nobody ever ran it against, published under a
version number that says it is that release's chart. Versions below the floor are
now reported as "older" and left without a chart; CHART_FERRETDB_FLOOR overrides
it. Old ENTRIES are untouched either way: a version that already has a package is
"keep", and keep is never rebuilt.

The comments in release-charts.sh no longer describe a MongoDB dependency that is
gone; they say what the anchored substitutions protect now.

tests/chartsFerretdb.test.cjs gains the release-path half, which runs whether or
not the charts clone is present: three substitutions and no more, `tag: latest`
unmatched by the tag pattern, the image-existence check before publishing, and
the backfill floor.

Verified by running the suite (12 tests) and the whole node suite (385 suites,
0 failed).

Thanks to salcinad, ouvry-ems and xet7 !
2026-08-12 17:29:25 +03:00
Lauri Ojansivu 7a38213ab6 CHANGELOG: the Helm chart's FerretDB switch, guarded from here. 2026-08-12 17:15:06 +03:00
Lauri Ojansivu 5d1c9d0a3c Tests: pin the Helm chart's FerretDB switch from this side of the fence.
wekan/charts is a separate repository with no test runner, and the two things
that went wrong in it (charts#54, charts#55) are WeKan's own settings: the URL
parameter WeKan needs (#6582) and the environment WeKan's docker-compose.yml runs
FerretDB with. So the guard lives here, reads .tools/charts, and SKIPS with a
message when that clone is absent rather than passing quietly - the same shape as
the FerretDB checks in tests/fixedVulnerabilityClasses.test.cjs.

What it holds: no per-pod StatefulSet DNS may be assembled in _helpers.tpl again
(that was charts#54 - Bitnami's naming against groundhog2k's services, so WeKan
dialled a host that does not exist); the Service the URL names is the Service the
chart creates; directConnection=true is there and an external URL that already
sets it is left alone; the WeKan environment is docker-compose.yml's FerretDB one
(polling, sockjs, WRITABLE_PATH, WITH_API) with no MONGO_OPLOG_URL and no second
MONGO_URL; the image is ghcr.io/wekan/ferretdb with quay.io and Docker Hub named
as alternatives; there is one replica and the reason is written down; the data
survives a restart; and an existing MongoDB is still reachable after the upgrade.

One check is a sweep rather than an example: every setting docker-compose.yml
documents has to appear in values.yaml. It found NODE_OPTIONS missing, which the
chart now carries too - a Helm user gets the same reference a Docker user does.

Verified by running the suite against the current .tools/charts checkout (9 tests)
and the whole node suite (385 suites, 0 failed).

Thanks to salcinad, ouvry-ems and xet7 !
2026-08-12 17:13:36 +03:00
Lauri Ojansivu b07b599af8 CHANGELOG: repoint the Worker-role link after the commit was amended. 2026-08-12 16:06:41 +03:00
Lauri Ojansivu 2328b70aa9 CHANGELOG: the Worker role doing what it is for, and the card that lost its name. 2026-08-12 16:05:15 +03:00
Lauri Ojansivu f2e58945f0 Fix #3144: an archived card is still named in its own history.
"Activities for archived card displayed as undefined on board settings." Move a
card around to make some activities, archive it, open the board sidebar - and the
sentences that named that card name nothing. The reporter said what they wanted
instead: "{{ card title }} [archived]".

The feed asked `activity.card()` and rendered its title. An archived card is not
published to the client, so that lookup returns nothing and each sentence was
built around a hole: a card that still exists, still has a title, and whose
activities are right there on the page went nameless in its own history.

The activity already knew the title in most cases - createCard, moveCard and
moveCardBoard have stored `cardTitle` for years. So the answer is not to publish
archived cards to every sidebar, which would send data to clients that do not need
it; it is to read what the activity recorded. models/lib/activityCardLink.js
decides, in order: the card's CURRENT title when the card is here (a card renamed
since should read as it reads now), marked when that card says it is archived; the
title the activity recorded when the card is not here; and null when neither
exists, which the feed renders as "this card" rather than as a gap in the middle
of a sentence.

The link survives all of it. A card URL is /b/<boardId>/<slug>/<cardId>, and an
activity carries both ids, so the anchor is built without the card document
(models/lib/cardUrl.js). No card document also means no slug, and the fallback
'board' is a segment the route ignores - it does not invent "/b/undefined/...",
which is the bug that helper was written for (#6427).

A missing card is NOT called archived, and that is deliberate. It nearly always is
- a deleted card takes its activities with it (Cards.cardRemover) - but with lazy
card loading a card can simply be outside the window this client was sent, and
"archived" would then be a statement the feed cannot support. The mark is used
only where the card is here and says so itself.

The two activities that were NOT storing a title are the two about archiving:
archivedCard and restoredCard now record `cardTitle` like the others. They are
exactly the ones guaranteed to be about a card the client can no longer look up,
which is why this bug was reported about archiving and not about anything else.
Activities written before this have no title, and those are the ones that read
"this card".

Both strings are translated (`archived`, `this-card`, which already exist in every
language file), and a title out of the database is still sanitised before it
reaches the page.

tests/activityCardLink.test.cjs covers the reported case and the ones around it:
the live title winning over the recorded one, the board slug when the board
document is at hand, an activity with no cardId naming the card without inventing
a link, junk titles ('', '   ', null, numbers, objects) never becoming titles, and
a missing card never being called archived. Plus both ends of the wiring: the
archive activity recording the title, and no call site passing only the card.

Verified by running the suite and the whole node suite (384 suites, 0 failed). Not
verified in a browser: this sandbox has none.

Thanks to rptl and xet7 !

Fixes #3144.
2026-08-12 16:01:51 +03:00
Lauri Ojansivu 7b35cb5f03 Fix #3189: a Worker can move a card and assign themselves to it.
"User with Worker permission can't assignee card if it has been assigned to
someone else before." The card showed the reporter's name for a moment and then
showed the previous assignee again - an optimistic write the server threw away.
The previous assignee was a red herring: a Worker could not assign themselves at
all.

The board schema defines the role as "only allowed to move card, assign himself to
card and comment". Both of those are card UPDATES, and the capability table gives
Worker `write: false` - so the role defined by two specific writes was allowed
neither. The client had already built the UI for it: the assignee popup shows a
Worker exactly one name, their own. The server discarded what they clicked.

Widening `write` to true was never the fix - that hands a Worker every field of
every card, which is the opposite of what the role is. What was missing is the
field-level policy the "Known gaps" section of docs/Features/Members/Roles.md said
this needed, so that is what this is.

models/lib/workerCardWrite.js decides an update by WHAT IT WRITES:

  MOVE          $set of listId / swimlaneId / sort, plus the dateLastActivity and
                modifiedAt written with them
  SELF-ASSIGN   $addToSet or $pull of `assignees` with EXACTLY the caller's own id

and refuses everything else BY DEFAULT. Not "deny a list of dangerous fields" -
allow only when every operator and every field in the modifier is one of the
above. A modifier it does not recognise ($unset, $rename, $inc, a raw replacement
document, an operator MongoDB adds next year) is not a move and not a
self-assignment, so it is refused. A qualifier on the assignee write ($each, $in)
is refused rather than unpicked: a policy that starts unpicking list shapes is a
policy with a hole in it.

Deliberately not included: boardId, because "move card" here means within the
board and a cross-board move is a different act with its own guard
(GHSA-gm7v-pc38-53jr); `members`, because the role says assignee; and assigning or
unassigning anybody else, because the role says himself.

server/permissions/cards.js reads the modifier now (Meteor passes it to the allow
rule and it was being ignored) and consults the policy only for a member who has
no write access AND is a Worker on that board. Write access is still the first
question, and the vote/poker canaries still fire before either.

`moveCard` becomes its own capability in models/lib/boardRoleCapabilities.js -
true for every role that can write, plus Worker - so the client's canMoveCard()
asks for the same thing the server enforces. That is one answer added rather than
eight changed.

docs/Features/Members/Roles.md gains the column, the paragraph that used to
explain why moving a card could NOT be its own column now explains why it is, and
the Worker entry moves from "Known gaps" to "Fixed". The gaps section is empty and
says so.

tests/workerCardWrite.test.cjs covers both halves of the role and, more
importantly, the refusals: somebody else's name, a move with a passenger field, a
title, a label, members, an $unset, an $inc, a $rename, a whole-document
replacement, $each/$in qualifiers, and junk of every shape - none of which may
become permission. tests/boardRoles.test.cjs gains the column and flips its
gap test: the ⚠ that marked this role had to go with the fix.

Verified by running both suites and the whole node suite (383 suites, 0 failed).
Not verified in a browser: this sandbox has none, so the client half is verified
from the code and the popup template that has always shown a Worker their own name.

Thanks to rptl and xet7 !

Fixes #3189.
2026-08-12 15:58:17 +03:00
Lauri Ojansivu 49463d3dcb CHANGELOG: the snap reconciling its two copies, and the link that closed a card. 2026-08-12 15:40:15 +03:00
Lauri Ojansivu 75a23b76ae Fix #6588: a file:// link in a card no longer makes the card impossible to open.
"A card whose description or a comment contains a `file://` URL cannot be opened.
Clicking the minicard plays the open animation but the card details panel never
mounts. There is no visible error by default because Blaze swallows the render
exception." Captured, it was:

  TypeError: this.__schemas__[t.toLowerCase()].validate is not a function
      testSchemaAt / matchAtStart / markdown-it (tokenize -> parse -> render)

WeKan registers eight custom URL schemes so links like `thunderlink:` and
`file://` are recognised, and it registered them the linkify-it 4/5 way:

  Markdown.linkify.add(scheme + ':', 'http:')      // "behave like http:"

linkify-it 6 removed string aliases and builds the definition by SPREADING it:

  const def = { normalize: ..., ...definition };

Spreading the string 'http:' produces {0:'h',1:'t',2:'t',3:'p',4:':'} - an entry
with no `validate` - and testSchemaAt then calls `.validate(...)` on it. So every
one of those eight schemes was a landmine in any card's text, and the card holding
one could not be opened again. Reproduced here against the installed linkify-it,
not from the report: the test builds that same registration and watches it throw.

Each scheme now carries a validate of its own, which is all the alias was standing
in for: the URL after the scheme - an optional `//`, then everything up to
whitespace or a bracket - with trailing sentence punctuation handed back, so "see
file://server/share/doc.xlsm." does not swallow the full stop.

WHAT THAT DOES NOT DO, because the list promises more than it delivers.
Registering a scheme makes markdown-it RECOGNISE it; two filters then decide
whether a reader gets a clickable link, and both refuse these. markdown-it's own
validateLink blocks file: (with javascript:, vbscript: and data:), and the
viewer's DOMPurify allows only http/https/ftp/ftps/mailto/tel/callto/cid/xmpp
hrefs. So none of the eight has ever produced a clickable link - its only
observable effect was the crash. Making them clickable means relaxing both
filters for schemes that launch local applications, which is the security decision
#3218 asks for and not something to slip in while fixing a crash. It goes to TODO
Later; the tests pin today's answer so the day it changes is a decision.

And the guarantee behind the fix, which is worth more than the fix: nothing a card
contains may make that card impossible to open. The markdown render is wrapped, so
a throw from a plugin, a pathological formula or the next markdown-it upgrade
shows the text as written - escaped, sanitised, unformatted, and readable - instead
of a panel that never mounts.

tests/markdownCustomUrlSchemes.test.cjs runs the real linkify-it and markdown-it
from node_modules: the old registration throwing, every scheme having a working
validate, the reported strings rendering, punctuation staying in the sentence, a
bare `file:` and junk input returning zero rather than throwing, and the viewer's
fallback returning sanitised text rather than nothing.

Verified by running the suite and the whole node suite (382 suites, 0 failed). Not
verified in a browser: this sandbox has none, so the crash and the fix are
exercised against the same dependency the browser would load.

Thanks to rmb82 and xet7 !

Fixes #6588.
2026-08-12 15:36:53 +03:00
Lauri Ojansivu 40ff8151ea Snap: pick the database that holds the work, and merge the other one into its history.
An email report, on an instance being served the older of its two copies: "some
users are unable to log in (error: 'user not found'), and boards created after
mid-July appear to be missing." That is what serving a copy looks like from the
outside - the accounts and boards made after the copy was taken simply are not
there.

Until now the snap handled that by telling the admin to sort it out. #6583 taught
it not to switch on a guess, so when both MongoDB and the migrated FerretDB have
been written to since the migration it printed the two `snap set wekan
database=...` commands and stayed where it was. That message is in `snap logs`;
most people never see it, and their site meanwhile shows the wrong copy.

A file timestamp cannot answer "which copy holds the work" - an mtime says when a
file was touched, and starting a database touches its files. But both copies can
be READ, so ask them. bin/db-eval.mjs gains an `evidence` command (per-collection
counts, plus the newest timestamp any document carries) and bin/database-choose.mjs
decides from the two answers:

  one holds everything the other holds, and is not older -> serve it, nothing to
  merge;
  both hold work the other does not, and one is clearly newer -> serve the newer
  one and copy across what only exists in the other;
  too close to call, or nothing readable -> change NOTHING and keep the existing
  message, because that case is a decision about somebody's work.

"Clearly newer" is a margin, not a tie-break: six hours by default, so the writes
the migration itself makes on both sides cannot decide anything.

WHY THE MERGE IS SAFE, and it is WeKan's own design that makes it safe: the
history is APPEND-ONLY (docs/Features/Reports/History/History.md, which gains a
section 9a on exactly this). Activities, comments and the coming changeHistory
rows are never rewritten in place, so they can only be ADDED to. bin/database-
merge-missing.mjs therefore inserts every document whose _id is ABSENT from the
chosen database and does nothing else:

  * a document that IS there is never overwritten - a card edited on both sides
    keeps the version in the copy being served, which is the newer one;
  * nothing is deleted, on either side;
  * the copy that was not chosen stays on disk, so `snap set wekan database=<the
    other one>` is still the way back - now a choice rather than a repair;
  * the work done on the copy that is not being served becomes readable in the
    served copy's card History instead of sitting in a database nobody opens.

For the reported symptoms that means the missing users and boards are inserted
rather than left behind: a user who could not log in exists again, with their own
_id, and their boards with them.

A three-way merge - reconciling two edits of one field - is not attempted and will
not be from a script. Where the evidence cannot separate the copies, the snap
still says so and hands the decision to a person.

WEKAN_AUTOPICK=false turns the whole thing off, `database-autopick --dry-run`
shows what it would do, and AUTOPICK_MERGE_LIMIT bounds how much it copies.

tests/snapDatabaseAutopick.test.cjs covers the decision (both superset
directions, the both-diverged case, one side empty or unreadable, too-close, no
timestamps, and a BIGGER but older copy that must not win on size) and runs the
merge against an in-memory pair of databases: what is missing is inserted, what
exists is not overwritten, nothing is deleted on either side, --dry-run writes
nothing, a limit is not an error, and the merge script contains no update or
delete operation at all.

tests/ferretdbMigrationStale.test.cjs keeps its rule - no switch on a guess - with
the assertion updated to match what changed: this branch may not choose from the
timestamps it has, it delegates to the one thing that can choose, and when even
that cannot tell the copies apart the admin still gets both commands.

Verified by running both suites and the whole node suite (381 suites, 0 failed).
The snap itself cannot be built or refreshed in this sandbox, so the orchestration
that starts each database on a temporary port is verified from the code and from
the shapes migration-control already uses, not from a running snap.

Thanks to xet7 !
2026-08-12 15:30:25 +03:00
Lauri Ojansivu b7affb7a94 Update dependencies: the S3/GCS clients, bson, ws and the build toolchain.
Two Meteor packages and 63 npm ones, from a lockfile refresh - package.json's own
ranges are unchanged, so these are the versions those ranges already allowed.

The two that reach running WeKan rather than a build: bson 7.3.1 -> 7.3.2, the
codec the driver speaks to MongoDB and to FerretDB with, and ws 8.21.2 -> 8.21.3,
the WebSocket under Meteor's DDP connection. aldeed:collection2 4.2.1 -> 4.2.2 is
the schema validator - the one whose error formatter turned a list with no title
into "Cannot read properties of undefined (reading 'title')" in v10.80.

The AWS SDK (client-s3 and eighteen credential/signing/presigner packages, plus
the @smithy transport under them) and @google-cloud/storage are the optional
attachment backends: unused unless S3 or GCS is configured. The rest - rspack,
terser, browserslist and its tables, eslint and @typescript-eslint, puppeteer,
memfs and the @jsonjoy.com filesystem packages - are build- and test-time only
and ship in no WeKan bundle.

The CHANGELOG entry is the dependency batch of the upcoming release, in the
established form: what each one IS, so a reader can tell which of them could
affect their instance and which cannot.

Verified by running the whole node suite (380 suites, 0 failed) after the update.

Thanks to dependabot.
2026-08-12 15:16:52 +03:00
Lauri Ojansivu c495cb5d29 CHANGELOG: what the PDF fix could not reach, and one report with nothing to point at yet. 2026-08-12 15:13:36 +03:00
Lauri Ojansivu 35184e89bd CHANGELOG: the snap's four ways of serving a copy, and the reported UI bugs. 2026-08-12 15:11:49 +03:00
Lauri Ojansivu eb47e04657 Tests: ask whether a fixed mistake is anywhere ELSE, and close two gaps it found.
Every entry in https://wekan.fi/hall-of-fame/ has a suite pinning the place it was
found. None of them could answer the other half of the question: is the same
MISTAKE somewhere else - in WeKan, or in the FerretDB fork WeKan ships as its
default database? A per-site regression test knows one file, and the next
occurrence is written months later by somebody who never read the advisory.

tests/fixedVulnerabilityClasses.test.cjs asks it as a CLASS, over the whole tree
and over .tools/FerretDB when the clone is there (and saying it skipped those
checks when it is not, rather than passing quietly):

  ZipBleed      an archive entry naming its own destination path, without a
                containment check - and only for real archive readers, since a
                readdir entry name is one component and can escape nothing
  WhereBleed    a client-supplied selector carrying operators that execute
                JavaScript
  TransitBleed  rejectUnauthorized: false, NODE_TLS_REJECT_UNAUTHORIZED, and Go's
                InsecureSkipVerify in the FerretDB fork
  SourceBleed   eval() / new Function() on the server
  MimeBleed     an SVG served inline by a route that neither sanitizes it nor
                forces a download
  RandomBleed   a secret from Math.random(), and random bytes folded onto an
                alphabet with a bare % - CWE-1204 modulo bias

Comments and string literals are stripped before matching, or the advisory notes
explaining a fix would trip the check enforcing it. Allowed occurrences are listed
with the reason they are allowed. Each check was confirmed to FAIL on the mistake
by writing one into the tree and watching it fire; both probes were removed.

It found one straight away. models/lib/mongoSelectorSafety.js rejected `$where`
and nothing else - but `$where` stopped being the only way a FIND filter runs
JavaScript in MongoDB 4.4, which takes an aggregation expression inside a find
filter, and `$expr: { $function: { body, args, lang } }` there runs a JavaScript
body through the same client-supplied selector. `$accumulator` is the same again.
Both are rejected now, the helper is named for what it tests (hasServerSideJs,
with hasWhere as the alias every caller and the advisory use), and a cyclic
selector cannot spin it - an infinite recursion inside a security check is a
denial of service of its own. tests/mongoSelectorSafety.test.cjs gains those,
plus the negatives that keep it usable: a field literally called `function`, and
`$expr` doing ordinary comparison, are both fine.

randombleed moves from RECORDED to GUARDED in tests/securityRegressionCoverage.
test.cjs, and its gap count from 25 to 24: the rejection sampling CWE-1204 was
about is now held everywhere rather than in one file.

And one release entry had shipped with no test at all - the only one since v10.80
that is not a translation update. tests/databaseProblemsFixes.test.cjs covers it:
the default-swimlane self-heal calling the synchronous upsert Meteor 3 removed
(and the getter that starts the async one without awaiting it, which must handle
its own rejection or a self-heal failure takes the process with it), List.move()
inserting a list with no title and getting an error from the error formatter
rather than from the validator, and the two classifier rules that stop either
reading as "unknown / unclassified" in Admin Panel / Problems - on every database
WeKan runs on, and without swallowing an ordinary network error.

Coverage checked, not assumed: all 60 published vulnerabilities are named by a
suite, and every one of those suites carries negative assertions as well as
positive ones. New suites need no registration - build.sh's test run calls
tests/run-node-suites.cjs, which DISCOVERS every tests/*.test.cjs - so all three
files here are in the "Run WeKan's own tests" options already.

Verified by running the new suites and the whole node suite (380 suites, 0 failed).

Thanks to xet7 !
2026-08-12 15:06:45 +03:00
Lauri Ojansivu eb14168893 Fix #6586: the PDF export writes umlauts, not question marks.
"When using Board settings » Export board » export / PDF we get a pdf file but the
umlauts (german, ä, ü, ö,...) are corrupt", "all the text in this PDF file is
markdown formatted - this doesn't make sense in a pdf file, does it?", and "I can't
see in which swimlane a card is in that export, no tags".

The umlauts were removed on purpose, one line before anything was written:

  .replace(/[^\x20-\x7E\n]/g, '?')

so "Grüße" left the server as "Gr??e". That line was a workaround for the real
problem rather than a choice: text in a PDF is BYTES PLUS AN ENCODING, and the
exporter declared neither. A `(...)` string in a content stream is read through the
font's encoding; no /Encoding was given, so a viewer used the font's built-in one,
where the bytes above 127 are not the Latin-1 letters - and the file was assembled
as a UTF-8 string, so one "ü" would have arrived as two bytes and drawn as two
wrong glyphs. With no encoding to write into, erasing the letters was the only
thing left that did not corrupt the file.

So the font declares /WinAnsiEncoding - Windows-1252, which every viewer has for
the base-14 fonts - the text is encoded to those single bytes, and the file is
assembled as binary. The xref offsets are measured in bytes for the same reason:
measured as UTF-8, every offset after the first accented character is wrong, and
some viewers refuse the file outright.

What Windows-1252 does not have is transliterated rather than erased: a Latin
letter loses its mark (ā -> a, ź -> z, Ł -> L) and keeps its word. Only a script
the base-14 fonts cannot draw at all - Cyrillic, Greek, Hebrew, Arabic, CJK, emoji
- still falls back to '?', because the fix for those is an embedded Unicode font
and that is a font binary this repository does not carry. It is in TODO Later.

Markdown is flattened instead of printed. `**bold**` in a PDF is four asterisks,
and the board export was itself emitting markdown - "## List (3)", "- Card" - into
a file that has no markdown in it. The structure is drawn now: headings and titles
in Courier-Bold, cards as bullets, and a card's own markdown reduced to its words
(links keep their text and their URL). Emphasis underscores only count at a word
boundary, so `snake_case` and `file_name_here` are left alone - markdown treats
them the same way.

And the board export says what it was missing: swimlanes (walked swimlane by
swimlane when a board has more than the one every board is created with, and not
mentioned at all when it does not), labels, members, assignees and the dates.

The PDF itself - encoding, flattening, wrapping, pagination - moves to
models/lib/pdfDocument.js, which is pure, so tests/pdfExport.test.cjs reads the
BYTES that come out: that ü is 0xFC and ß is 0xDF, that the euro sign is 0x80,
which Latin-1 does not have at all, that every xref row points at the object it
claims, and that the bold font is selected only where a heading asks for it. Its
negative half is the part worth keeping: junk never throws, an empty board still
produces a file that opens, `2 * 3 * 4` and `a_b_c` are not emphasis, and a
Japanese title degrades to '?' without ever emitting a byte above 0xFF into the
stream.

Verified by running the suite and the whole node suite (378 suites, 0 failed). Not
verified against a PDF viewer: this sandbox has none, so the bytes are checked
against the PDF specification's own rules instead.

Thanks to Heart1010 and xet7 !

Fixes #6586.
2026-08-12 14:53:06 +03:00
Lauri Ojansivu 32b61f186f Checklists: "Show on minicard" is an override, so unchecking it hides the checklist.
Reported by email with a screenshot (Swedish): "Även om jag avmarkerar 'Visa på
minikort' (och vill dölja checklistan på minikortet)" - the switch is off and the
checklist is on the minicard anyway.

It was off for everybody. The minicard asked:

  board.allowsChecklistsOnMinicard || checklist.showChecklistAtMinicard

and the board flag defaults to TRUE. An OR cannot be argued with: while the board
setting is on, no value of the checklist's own field changes the answer. The popup
made it look like a working switch - it drew the state from the raw field, which
starts false, so it read OFF beside a checklist that was plainly showing, and
clicking it wrote true and changed nothing visible. Clicking again wrote false and
changed nothing either. Dead in both directions, which is exactly what the
screenshot shows.

The two settings are a DEFAULT and an OVERRIDE, not two ways of saying yes:

  showChecklistAtMinicard === true   -> shown, whatever the board says
  showChecklistAtMinicard === false  -> hidden, whatever the board says
  showChecklistAtMinicard undefined  -> follow the board's default

An override needs three states, so the field loses `defaultValue: false`: with
every checklist born false, "hidden" and "not chosen" are the same value and the
override cannot exist. models/lib/minicardChecklistVisibility.js is the one rule,
used by the minicard, the popup's switch and the toggle - and the toggle now flips
what is ON SCREEN rather than the raw field, because flipping the field is what
made the first click do nothing.

Checklists written under the old default carry a false that meant "follow the
board". Left alone under the new reading it would mean "hide this one" and every
checklist would vanish from every minicard - the mirror image of the bug. The
schema-upgrade step checklist-minicard-unset clears exactly those, and it is the
one step there that must run ONCE EVER rather than once per version: afterwards a
stored false is somebody's choice, and re-running would put their checklists back.
It keeps its own marker beside the schema-upgrade one for that.

tests/minicardChecklistVisibility.test.cjs covers the reported case, the opposite
one, that two clicks return to the board default, and that only true and false are
opinions - a string, a number or a null from an old REST client or a bad import
must not quietly mean "hide". tests/schemaUpgradeSteps.test.cjs gains the step,
including the negative: after it has run, a false set by a person survives the next
release.

Verified by running both suites and the whole node suite (377 suites, 0 failed).
Not verified in a browser: this sandbox has none.

Thanks to xet7 !
2026-08-12 14:45:07 +03:00
Lauri Ojansivu f7b0a4d1d2 Fix #6587: a board's type now follows Member Settings / Font size.
"The font size setting in the user menu (Settings -> Font Size) has no effect on
mini cards, while it correctly applies to other UI elements."

Two halves, and both had to be wrong for this to happen.

The preset was applied to <body>. A font-size on body is inherited by children
that ask for a relative size, and it leaves `rem` alone entirely - rem is measured
against the ROOT element. So anything sized in rem never heard about the setting.

And a board's type is sized in px: the list heading, the minicard title and the
"Add card" link were pinned to 16/14/13px in #6465, and the minicard's menu, drag
handle and mobile icons alongside them. px follows nothing at all - not the root
size, not body's - so the board was the part of the UI the setting could not reach
no matter which element carried it.

So the preset moves to <html> and the board's sizes become rem: 1rem, 0.875rem,
0.8125rem, which are the same 16/14/13px at the default root size. The type scale
#6465 set is unchanged and its guard still holds the order; what changes is that a
130% preset now scales it, and 80% shrinks it.

The size goes on ONE element only. With it on html AND body, a 130% preset
compounds to 169% for everything sized relative to body. Everything under body
still scales, because body inherits the root size - so nothing is lost by moving
it up, and uiFont.js sends only the font-size marker class to the root while the
font-family and colour rules keep matching on body, where their form-control
selectors live.

rem, not px and not vw: px cannot follow the setting, and vw is what made elements
resize with the browser window (fixed in earlier commits, deliberately). rem
follows the root font size and nothing else, which is exactly the preference the
user set.

The report also says minicards are "approximately 7-8pt at 100% browser zoom". At
the default root size a card title is 14px, and this commit does not change that
number - what it changes is that the setting can now raise it. If cards still come
out much smaller than the list heading above them, the board's own zoom control is
the other thing that scales them, and it is not affected here.

tests/listTypeScale.test.cjs reads rem as well as px (1rem = 16px at the default,
so the pinned ORDER reads the same either way) and gains two checks of its own:
that each of the three sizes is in a unit the preset can scale, and that the preset
lands on the root element and only there.

Verified by running that suite, tests/uiFonts.test.cjs and the whole node suite
(376 suites, 0 failed). Not verified in a browser: this sandbox has none.

Thanks to CCmesch and xet7 !

Fixes #6587.
2026-08-12 14:38:27 +03:00
Lauri Ojansivu 4400629066 Snap upgrade: copying the old common directory back is what destroys the restore.
From an upgrade report by email. The admin upgraded 6.09 -> 10.85 exactly by the
documented route - dump, move /var/snap/wekan/common aside, refresh, restore - and
it worked: the boards were back. Then, to get their attachments, they undid step 3
the way it reads:

  cp -pR /root/common/* /var/snap/wekan/common/

Their log, seconds later:

  mongodb-control: line 489: 351018 Aborted   ... $SNAP/bin/mongod --dbpath=...
  snap.wekan.mongodb.service: Main process exited, code=exited, status=134/n/a

"After that, Wekan was still running, but all the boards were missing."

Everything beside files/ in that directory is the raw database - *.wt, WiredTiger*,
_mdb_catalog.wt, sizeStorer.wt, storage.bson, mongod.lock, journal/ - and copying
it over a RUNNING mongod replaces the files it has open underneath it. mongod
aborts, and what comes back up is the 6.09 database it cannot read.

The documentation is where this is decided, because the mistake is made before any
code of ours runs. Step 6 already said to copy back only `files`; what it did not
say is what the obvious inversion of step 3 costs, so it says it now, in the step
itself: the exact wrong command, which files those are, what the failure looks like
in `snap logs wekan.mongodb` so somebody who has already done it recognises their
own log, and that the whole directory IS put back in one place - the "going back to
6.09" procedure - onto a STOPPED snap with the current contents removed first,
which is a different operation.

And mongodb-control now recognises the abort it produces. Exit 134 gets a case of
its own beside exit 132 (SIGILL/AVX): SIGABRT, the data directory changed underneath
a running mongod, which files may and may not be copied back, and the one way out,
`snap run wekan.database-restore <archive>`. Before this the whole story in the log
was the word "Aborted".

The same report also had a board with attachments that loads forever after a
restore. That one is not diagnosed here - the attached logs are mongod startup
only, with no errors in them - so it goes to TODO Later rather than being guessed
at.

tests/snapCopyBackDestroysDatabase.test.cjs pins both halves, including that the
warning sits in the step where the mistake is made and above the procedure it must
not be confused with.

Verified by running the new suite and the whole node suite (376 suites, 0 failed).

Thanks to xet7 !
2026-08-12 14:31:58 +03:00
Lauri Ojansivu c3811c5283 Snap: a MongoDB this snap has no reader for stops and says so, on every CPU.
"It turns out the mongodb version on my installation was even older. It was
running on mongodb 3.2, this is why your 4.2 check was not doing anything." Their
site never showed the explanatory page either: it waited for MongoDB forever,
which is the loop #6471 was opened about.

The migratemongo 3.2 tools are staged for amd64 ONLY - MongoDB published no 3.2
build for any other architecture (the migratemongo part in snapcraft.yaml). So on
arm64 and everything else, a MongoDB 3.x database has NO reader in this snap at
all, and that branch handed the case to fall_back_to_mongodb, which starts mongod
7 again. mongod 7 is the binary that has already refused these files, so it fails,
mongodb-control re-runs the migration, and the site sits on "MongoDB not ready
yet, retrying in 5 seconds..." with the reason only in `snap logs`.

A missing reader and an unreadable database are not the same thing, and only one
of them can usefully be retried. When mongod has already said "too recent to start
up on the existing data files", the tool that could have read them not being in
this snap is a final answer: stop, keep every byte, and serve the page. Data that
no mongod ever called too old still falls back to MongoDB as before, because that
case may yet come up serving. The log names the architecture, since that is the
part an admin cannot see from the outside.

The page also stops leaving the way out as an exercise. "Move the data across with
a MongoDB that can read it" was true and not enough - the reporter worked out the
steps themselves, and another admin who got this far lost their restored database
to the step that is easy to get wrong: they copied the old data files back into a
RUNNING data directory and mongod aborted with SIGABRT. So the page gives the four
steps in this snap's own paths: copy $SNAP_COMMON somewhere safe, dump it with a
MongoDB of that vintage, move the old database files OUT (naming them, and saying
that files/ - the attachments and avatars - stays), and restore with the snap's own
`snap run wekan.database-restore <archive>` rather than a mongorestore invocation
they have to assemble.

tests/snapOldMongoData.test.cjs gains the missing-reader branch and reads the
page's own HTML for the four steps. Its "newest first" test no longer checks the
order by "the first mongod_says_data_too_old comes after the 3.x probe": there are
now two ways the third reader produces no database - it is ABSENT, decided just
before the probe, or it RUNS and refuses, decided just after - and neither is
reachable until mongod 7 and mongod 4.2 have both failed, which is what that test
is really about.

Verified by running the suite and the whole node suite (375 suites, 0 failed). The
snap itself cannot be built or refreshed in this sandbox, so the arm64 path is
verified from the code and the packaging, not from a running snap.

Thanks to Philippe-Bentegeac, JDeepix and xet7 !

Fixes #6471.
2026-08-12 14:28:49 +03:00
Lauri Ojansivu 4b99fa701d Snap: the old MongoDB files are not WeKan's problem once it runs on FerretDB.
"It somehow tries to access Mongodb again instead of Ferretdb. I don't even have
an old version, but just this" - and the screenshot is the #6471 page, "Wekan
cannot open the existing database", on a snap whose data was in FerretDB and
perfectly readable.

The migration never deletes what it copied from, so a migrated snap keeps its old
MongoDB files in $SNAP_COMMON forever. Two places treated their presence as
something WeKan had to act on:

migration-pending called a migration "pending" for any $SNAP_COMMON holding
WiredTiger files without the .migration-to-ferretdb-done marker - the database
setting was never consulted. So an instance whose marker is gone (a forced
re-migration cleared it, or it was migrated by a snap old enough never to have
written one) was dragged back to those files on every start: mongodb-control
handed off to migration-control, which probed them with mongod 7, mongod 4.2 and
the 3.2 tools, and when none could open them wrote .mongodb-data-too-old.

wekan-control then served the explanatory page for that marker unconditionally.
That page is right when MongoDB is the database WeKan needs and cannot be read.
It is wrong when the snap runs on FerretDB: it replaced a working site with an
explanation of a database that site does not use.

Both now ask what the snap is actually running, and "already on FerretDB" means
the SETTING plus real data in files/db - never the setting alone, because
database=ferretdb with an empty files/db is precisely the case the migration
exists for and must still run.

The marker is left in place rather than deleted. It is true - those files really
are unreadable by this snap - and it belongs to MongoDB, so switching back with
`snap set wekan database=mongodb` brings the page back, correctly.

Nothing is switched automatically. #6583 is what choosing between two copies on
the snap's own initiative costs, and this page's own promise is that nothing
changes until an admin acts. What it does now is SAY that a FerretDB copy is
there when one is: a third way forward, the one command that serves it, the one
that undoes it, and the warning that a migrated copy is only as new as the
migration that made it. With no copy to serve it is word for word the page #6471
wrote.

tests/snapFerretdbNotBlockedByOldMongo.test.cjs RUNS migration-pending against
synthesised $SNAP/$SNAP_COMMON trees with a stub snapctl - already on FerretDB,
database=ferretdb with an empty files/db, database=mongodb with a copy beside it,
and the completed marker - and reads the page's own HTML for both shapes. It also
pins that the marker re-exec in the wait loop stays in the MongoDB branch: reached
while serving FerretDB, the new "start WeKan normally" branch would send it
straight back into the loop, exec after exec with no sleep between.

Verified by running the new suite, tests/snapOldMongoData.test.cjs (the #6471 page
it must not change) and the whole node suite (375 suites, 0 failed). The snap
itself cannot be built or refreshed in this sandbox.

Thanks to mueschel and xet7 !

Fixes #6585.
2026-08-12 14:22:24 +03:00
Lauri Ojansivu f5fee7dc01 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-12 14:05:35 +03:00
Lauri Ojansivu 894d7e87c0 CHANGELOG: the resume that copied a database three weeks out of date. 2026-08-12 14:05:34 +03:00
Lauri Ojansivu c783829823 Snap: a migration may not resume onto a database the source has outgrown.
"Our snap updated automatically from 10.81 yesterday to 10.85 this morning. Data
in the DB is from about 2 - 3 weeks ago. Many cards and work is lost."

Nothing was lost: the MongoDB data is in $SNAP_COMMON and untouched. What the
update completed was a migration that had been interrupted weeks earlier.

The MongoDB -> FerretDB migration is resumable, and has to be - it can run for
hours, and a snap refresh, a stop or a reboot part-way through is normal rather
than exceptional (migration-control's interrupted_keep_progress). So the importer
records every finished collection in $SNAP_COMMON/migration-progress.json and
skips those next time.

That checkpoint was only ever checked against the TARGET. migration-control
deletes it whenever it discards a partial SQLite (discard_partial_ferretdb), so it
can never name collections that are not there. Nothing checked it against the
SOURCE - and between an interrupted migration and its retry the snap hands WeKan
back to MongoDB and people go on using it (fail_3x_keep_progress,
resume_next_start). A migration interrupted in July and resumed in August
therefore skipped every collection it had finished in July, copied only the rest,
and reported success. finish_success then switched the snap onto that copy:
boards and cards as they stood in July, with three weeks of work still in MongoDB
and simply not being served.

A new snap revision is usually what sets the resume going again, because the
per-revision failure counter starts at zero (bin/stale-marker) - which is why
this reads as "the update lost my data". The update is when the weeks-old copy
finally got served. Same family as #6583, and the same answer: a copy made at one
time may not be used as if it were current.

Two checks, because they fail differently.

bin/migration-checkpoint-stale.mjs asks whether the checkpoint is too old to
resume from at all. mongod rewrites its WiredTiger files whenever it commits, so
the newest mtime among them is when MongoDB was last written to - the evidence
bin/ferretdb-migration-stale already uses for the finished-migration case. Later
than the checkpoint (by more than a margin, since the migration's own temporary
mongod writes on the way out) means the collections it lists are copies of an
older database, so the collection half is dropped and they are copied again. The
checkpoint's age is the OLDER of its updatedAt and the file's mtime: the two
disagreeing is a reason to doubt it, and a needless re-copy costs minutes while a
wrong skip costs weeks. The file half is kept - attachments are written once and
re-verified on disk at the recorded size, and re-extracting gigabytes is the
slowest part of a resume.

migration-control runs it as its first step, before it starts any mongod of its
own: starting one rewrites those files, and after that the question can no longer
be asked. It is the only way into a text migration - snap run wekan.migrate,
auto-migration and mongodb-control's fallback all arrive through it.

verifyCollectionCounts, in both importers, then asks the two databases themselves
once the copying is done, when both are quiet: a collection whose copy holds fewer
documents than the source is copied again from the source as it is now. It does
not care WHY a copy came up short, which is the point of a check standing behind
another one. Only a shortfall is acted on - a copy holding MORE is a resume
carrying documents deleted from MongoDB since, and deleting on that evidence is
the guess this whole family of bugs is made of - and a shortfall that survives the
second copy is reported rather than fatal, so one document FerretDB will not
accept cannot leave the snap in a migration it can never finish. It sits inside
the same "not repair mode" block as the copying it verifies, since a FILES_ONLY
repair (#6473) must never write text over a FerretDB people have been using.

tests/migrationCheckpointStale.test.cjs runs the detector against synthesised
$SNAP_COMMON directories: the reported case, an ordinary resume minutes later, the
margin that covers the migration stopping its own source mongod, a mongodb.log
that must not count as somebody using the database, and that migration-control
asks before it starts a mongod. tests/migrationCopyShortfall.test.cjs extracts
verifyCollectionCounts from BOTH importers and exercises it behaviourally.

Verified by running both suites and the whole node suite (374 suites, 0 failed).
The snap itself cannot be built or refreshed in this sandbox.

Thanks to Alishara and xet7 !

Fixes #6585.
2026-08-12 14:05:34 +03:00
github-actions[bot] c87da21587 Bump versions for v10.85 2026-08-12 04:19:57 +00:00
Lauri Ojansivu 6c8e4e88b3 Prepare v10.85 release 2026-08-12 07:18:38 +03:00
Lauri Ojansivu 2d3d6d6ca7 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-12 06:55:35 +03:00
Lauri Ojansivu 5b23954be8 CHANGELOG: the Font Awesome bounding boxes Firefox kept correcting. 2026-08-12 06:55:34 +03:00
Lauri Ojansivu f368c1a2ac Font Awesome: state the bounding box each glyph actually has.
Firefox logged one warning per glyph, on every page load, for every Font Awesome
file WeKan serves:

  downloadable font: glyf: Glyph bbox was incorrect; adjusting (glyph 19)
  (font-family: "Font Awesome 6 Free" style:normal weight:400 stretch:100
  src index:0) source: .../webfonts/fa-regular-400.woff2

819 of the 2163 glyphs in the four files were affected - 540 in fa-solid-900,
204 in fa-brands-400, 59 in fa-regular-400, 16 in fa-v4compatibility - so the
console filled with hundreds of lines and stopped being useful for spotting
anything else.

Every TrueType glyph stores its own bounding box in the `glyf` table. Font
Awesome ships boxes that are TIGHTER than the outline: they bound the on-curve
points only, while the box has to bound the control points too, because a
quadratic curve can bulge past its endpoints. Firefox's OpenType sanitiser
notices, corrects each box in memory and says so. Nothing rendered wrongly - the
warning was the whole of the damage - but the numbers in the file were wrong,
and they are wrong upstream, in Font Awesome's own build.

releases/fix-font-bboxes.py recomputes them: every glyph's box from its points,
the font-wide box in `head` from those, and the .ttf and .woff2 written from one
corrected font so the pair cannot drift. That last part also settles a smaller
oddity - fa-regular-400.woff2 does not round-trip through fontTools' woff2
reader at all, so it is rebuilt from the .ttf rather than read.

Only `head` and `hhea` differ from the files Font Awesome shipped, and both
follow from the boxes: `head` carries the font-wide box, and `hhea`'s
minRightSideBearing and xMaxExtent are derived from the glyph boxes. Every other
table - glyf outlines, cmap, hmtx, name, OS/2, post, GSUB, GPOS, loca, maxp -
compiles identically. The script verifies that itself, reading back what it
wrote and refusing to leave a font behind whose glyph order, character map,
advance widths, contour counts or point coordinates moved. The files also got
smaller (fa-solid-900.woff2 150024 -> 118388 bytes), which is fontTools'
compression rather than anything being dropped.

recalcTimestamp=False keeps Font Awesome's own head.modified, so two runs over
the same input agree byte for byte - verified - rather than churning a committed
binary with "now" on every run.

tests/fontGlyphBounds.test.cjs parses the .ttf files with its own reader, not
fontTools, so the check does not depend on the tool that wrote them. Verified it
FAILS on the original fa-regular-400.ttf with "59 of 212 glyphs state a wrong
bounding box" and passes on the corrected one.

The guard matters more than the fix here: these fonts are VENDORED, so the next
Font Awesome upgrade drops fresh upstream files straight back into the tree and
the warnings would return silently, months from anything that would explain
them. The failure message names the script to re-run.

Thanks to xet7 !
2026-08-12 06:55:33 +03:00
Lauri Ojansivu 4c02cf5585 CHANGELOG: v10.84 does rebuild its bundles, so it cannot say it does not. 2026-08-12 06:55:33 +03:00
github-actions[bot] 9a9e81f15a Bump versions for v10.84 2026-08-12 01:55:33 +00:00
Lauri Ojansivu aa76ae81b1 Prepare v10.84 release 2026-08-12 04:53:36 +03:00
Lauri Ojansivu 8d0de427ef CHANGELOG: v10.83's binaries table is v10.83's build, not v10.82's.
From the v10.83 build logs. The release itself is fine - every bundle built, the
GitHub release notes carry a correct generated table, and the only job failures
were three snap-launchpad uploads rejected by the Snap Store's own
upload-uniqueness check. What was wrong is the table in the CHANGELOG.

An Upcoming section that rebuilds nothing may say so and reuse the previous
release's table rather than invent one, and this one did: "The binaries below are
v10.82's: nothing here rebuilds them." Releasing invalidates both halves of that.
release-all.sh renamed the heading, every bundle WAS rebuilt, and the sentence
and the table came along unchanged - so v10.83 shipped claiming:

  FerretDB v1.48.0, when v1.49.0 was built and shipped;
  a loong64 row, when loong64 was SKIPPED this release - debian:trixie publishes
    no linux/loong64 image, so there is no loong64 userland to build in, and the
    job says exactly that and uploads no provenance;
  no armhf, armv6, armv7, i386 or win-arm64 rows, all of which were built.

The generated table at the top of the GitHub release notes was right all along -
releases/provenance-table.sh builds it from the provenance.tsv each build job
records, and it has 13 bundles and no loong64 - so the release notes carried two
tables that disagreed. The table here is now that same data: FerretDB v1.49.0
throughout, the five missing platforms added, loong64 gone, and s390x's FerretDB
marked *no checksum published*, which is what the build found rather than a
failed check. Full SHA256s recovered from the per-job build logs, which print
them in full where the release-notes table abbreviates.

This matters because the table is the answer to "which Node.js is in the arm64
bundle of 10.69, and was it checked" after the build log has expired. A table
carried forward unchanged answers that wrongly, which is worse than not
answering.

The Upcoming section keeps the same shape - nothing there rebuilds anything - but
its table is now v10.83's, so whatever is cut next starts from the current build
rather than one two releases back.

tests/changelogFormat.test.cjs gains a guard on the NEWEST release: it may not
contain "nothing here rebuilds them", because a release rebuilds every bundle.
Scoped to the newest deliberately. v10.73 through v10.81 carry the same sentence,
and whether each of those really rebuilt its bundles cannot be established now
that their logs have expired - rewriting eight historical tables on a guess would
put invented provenance where merely doubtful provenance is, so they are left as
they shipped.

Thanks to xet7 !
2026-08-12 04:36:00 +03:00
Lauri Ojansivu f3f6032160 CHANGELOG: the three new strings, in 133 languages. 2026-08-12 04:26:03 +03:00
Lauri Ojansivu 639214574c Translations: the three new Version-pane and checklist strings, in 133 languages.
"invalid-year":       "Invalid year. Please type all four digits, for example 2026."
  "collapse-checklist": "Collapse checklist"
  "expand-checklist":   "Expand checklist"

The keys shipped in en.i18n.json with the card-date fix and the collapsible
checklists; every other language file carried them as English placeholders.
Translated directly, as CLAUDE.md requires - no external translation service,
API or key - from each language's OWN existing strings, so the wording matches
what that file already says rather than being invented beside it.

Three anchors did most of the work. "checklist" and "collapse"/"uncollapse" give
each language its established terms, and "invalid-domain" is the same shape of
sentence as the new one - a rejection, then an instruction with an example - so
its phrasing, punctuation and politeness register carried over directly.

Where an anchor was itself wrong the correct term was used instead of copying the
mistake forward. Several files have terms that drifted in from another language:
Italian "Non collassare" in the Greek and Romanian files, Russian in the Georgian
and Mongolian ones, Vietnamese in the Thai one, Serbian in the Slovenian and
Bulgarian ones, German in the Klingon one. Others use a literal sense of
"collapse" that is not the UI one - Azerbaijani "Yıxılma", Estonian
"Kokkupõrge", Khmer "ដួលរលំ" and Chinese "崩溃" are structural collapse, a
building falling down. The new strings use the folding sense each language
actually uses for this control.

9 languages are deliberately left as English placeholders rather than guessed at:
tlh (Klingon), vo (Volapük), zgh (Tamazight), wa (Walloon), wo (Wolof), uz-AR
(Uzbek in Arabic script), and ve / ve-CC / ve-PP, whose files disagree with their
own locale tags - ve-CC reads as Venetian and ve-PP as Veps, so which language to
write is a question about the file, not about the string. A placeholder says
"nobody has translated this yet", which is true; a fabrication would say
something false in a shipped product. They are the documented
"untranslated everywhere" case and remain open to a translator.

Applied with fill-translations.mjs --apply, which writes ONLY into placeholders:
every language reported "filled 3, skipped 0 existing human translation(s)", so
no human translation was touched. Key order and the 2-space indent are preserved,
all 154 files still parse, and verify-human-preference.mjs passes 10/10.

Also carries two Traditional Chinese improvements that were already staged in the
tree from a Transifex pull - 系統運行時間 to 系統運作時間 in OS_Uptime, and the
same wording in database-migration-description. They are human translations and
the policy is that those always win.

Thanks to xet7 !
2026-08-12 04:24:23 +03:00
Lauri Ojansivu 7b84c74359 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-12 04:07:45 +03:00
Lauri Ojansivu 807fc5ea89 CHANGELOG: the staleness guard that called the live database stale. 2026-08-12 04:07:45 +03:00
Lauri Ojansivu 1993d7d3c8 Snap: a started mongod is not a used mongod, so stop calling the live copy stale.
The staleness guard added for #6583 had the opposite failure of the bug it fixed,
and this reporter ran into it:

  "A couple of weeks ago, I did a snap revert ... but then completed the
   migration successfully. Today, my database suddenly reverted to an old version
   from what looks like weeks ago. Upgrading to 10.83 did not fix the problem
   automatically."

Their FerretDB was the live database and had been for two weeks. MongoDB was the
frozen one. The guard decided otherwise because it compared exactly two things:
the newest mtime under the MongoDB data directory, and the migration marker.
STARTING mongod rewrites those files - recovery, and the checkpoint it writes on
startup - so one service start during a refresh put MongoDB's newest mtime at
today against a marker from two weeks ago. The guard called the live copy stale
and wekan-control set database=mongodb, which served data frozen at the migration
date: "reverted to an old version from weeks ago", exactly.

Upgrading could not fix it because the upgrade was the cause. wekan-control runs
this on every start, so every start re-applied it.

An mtime cannot tell "somebody used this database" from "this database was
started". Asking it of ONE copy therefore cannot answer the question. Asking it
of BOTH can, because the case the guard exists for has a shape the mistaken one
does not:

  migrated copy untouched since the migration, MongoDB moved on
    -> STALE (exit 0). Nothing has used FerretDB; MongoDB holds everything since.
  migrated copy moved on, MongoDB did not
    -> CURRENT (exit 1). The normal state after a successful switch.
  both moved on
    -> AMBIGUOUS (exit 2). Two databases have been written to since they were
       copies of each other. A timestamp says when a file was touched, not how
       much is in it, so there is no answer here - only a choice, and it is the
       admin's.

Only exit 0 may be acted on automatically. Both callers test with `if`, so that
falls out of the existing code, and the test pins it - most of all for the branch
in mongodb-control that DELETES files/db to migrate again, where ambiguity
reaching the rm -rf would destroy the very copy in doubt.

The ambiguous case switches nothing and says so, in mongodb-control and in
wekan-control, printing both databases' last-written times and the two commands
to look at each. Nothing is deleted by choosing either, and both live in
$SNAP_COMMON.

The message in wekan-control deliberately has NO database=ferretdb condition on
it. An instance the old guard already moved to database=mongodb is sitting on the
wrong copy right now and its setting persists, so a fix that only speaks up when
ferretdb is selected would leave it there silently. Whichever side is selected,
the admin hears that the other one holds writes of its own.

tests/ferretdbMigrationStale.test.cjs gains the reported regression - a
two-week-old migration, a FerretDB written to a minute ago, a mongod started an
hour ago - and pins that ambiguity can never reach the deletion. Its fixture now
stamps the SQLite at the migration time, which is what the migration itself
leaves behind; a later stamp is what "something has been using FerretDB" means.

Verified by running the detector against synthesised $SNAP_COMMON directories for
all four shapes. The snap itself cannot be built or refreshed in this sandbox.

Thanks to lukechao, markusst1982 and xet7 !

Fixes #6583,
2026-08-12 04:07:45 +03:00
github-actions[bot] ae46562308 Bump versions for v10.83 2026-08-11 16:23:54 +00:00
Lauri Ojansivu f48b2c03e2 Prepare v10.83 release 2026-08-11 19:22:45 +03:00
Lauri Ojansivu 3f9b81bcbc CHANGELOG: the public-board half of the avatar fix. 2026-08-11 19:21:54 +03:00
Lauri Ojansivu 484eae7362 Avatars: honour the ?boardId= the client has been sending all along.
Follow-up to the previous commit, found while checking why the Admin Panel
showed a picture that a board did not. The two URLs differ in one thing: the
avatarUrl helper in client/components/users/userAvatar.js appends
`?boardId=<id>`, and says why in its own comment - "so public viewers can access
avatars on public boards". The Admin Panel uses profile.avatarUrl raw.

/cdn/storage/avatars/:fileName never read that parameter. It required a signed-in
user and nothing else, so on a public board every visitor who was not logged in
got 401 and the missing-picture icon, which is the case the parameter was added
for. Fixing Meteor.userId() alone would have left that half broken.

avatarIsOnAPublicBoard() answers it: the named board must exist, be public, AND
have the avatar's owner as a member. The last part is not ceremony - without it,
naming any public board would unlock any avatar on the instance, and a public
board publishes its own members, not everybody.

The legacy redirect now keeps the query string. /cfs/files/avatars/<id> 301s to
/cdn/storage/avatars/<id>, and that is the path EVERY migrated 6.x avatar URL
takes, so dropping ?boardId= there would 401 exactly the installs the previous
commit set out to fix.

tests/requestUserAuth.test.cjs gains both: that the public path checks all three
conditions and is awaited (an unawaited Promise is truthy, which would make every
avatar public), and that the redirect carries the query and only when there is
one.

Thanks to markusst1982 and xet7 !

Fixes #6583,
2026-08-11 19:21:19 +03:00
Lauri Ojansivu 3f80161607 CHANGELOG: the avatar route that could never serve anybody. 2026-08-11 19:21:19 +03:00
Lauri Ojansivu bf047d53c8 Avatars: ask the request who it is, because Meteor.userId() cannot.
Following the same upgrade as #6583, profile pictures came back as broken
images:

  "I can see Profile Pics from the user in the Admin/User Pannel if i open a
   User. But on the boards i see only the 'missing Picture' Icon"
  "initials are OK, but there is broken avatar image for those that have image"

Nothing was lost, and the migration is not at fault. The avatar files migrate,
and the Meteor-Files record made from a CollectionFS filerecord even reuses its
_id (cfsRecordToMeteorFile), so a 6.x URL still names the right object. What
broke is the request for it.

A 6.x install stores profile.avatarUrl as /cfs/files/avatars/<id>. That route
serves the legacy CollectionFS bytes if they are still there, and otherwise
redirects to /cdn/storage/avatars/<id> - which asked who was asking like this:

  const userId = Meteor.userId();
  if (!userId) { res.writeHead(401); ... }

Meteor.userId() reads the current DDP invocation's environment. That exists
inside a method or a publication and NOT in a WebApp handler, where it does not
return "nobody" - it THROWS "Meteor.userId can only be invoked in method calls
or publications". The handler wraps its body in a try/catch that answers 500, so
the throw was swallowed into a broken image and no avatar served through that
route ever reached anybody, on any install. The upgrade did not cause it; it
moved every avatar URL onto the route where it already applied.

server/routes/legacyAttachments.js had the identical call, so legacy attachment
URLs failed the same way and for the same reason.

Initials were unaffected because they are rendered client-side from the username
and fetch nothing, which is why the report describes them as fine.

The other half of the report - the Admin Panel showing a picture that a board did
not - is NOT explained by this. Both templates end at the same route:
peopleBody.jade puts profile.avatarUrl into an img src, and a board goes through
+userAvatar, which appends ?boardId=. That parameter is the difference, and it
has a cause of its own; the commit that follows this one deals with it.

An HTTP request carries its identity in the request: a bearer token, an
X-Auth-Token header, an ?authToken= parameter, or the login cookie - and on
Sandstorm, a platform-injected user id and no Meteor token at all.
server/routes/universalFileServer.js has always resolved it that way and serves
attachments correctly today. server/lib/requestUser.js lifts that resolution out
so the two routes that were guessing share it rather than grow a third copy, and
so the next file route has something to call. It never throws: a caller deciding
whether to serve a file wants an answer, not an exception its own catch will turn
back into a 500.

universalFileServer keeps its own inline copy for now. Rewiring a working file
server is a separate change from fixing a broken one, and this commit should be
readable as the fix.

tests/requestUserAuth.test.cjs pins that neither route calls Meteor.userId(),
that both await the resolver (an unawaited Promise is truthy and would authorise
everybody), that all four token carriers and the Sandstorm path are handled, and
that the migration still reuses the id the old URL names.

Verified by reading and by the source guard; confirming the served image needs an
upgraded instance, which this sandbox has no Docker to run.

Thanks to markusst1982 and xet7 !

Fixes #6583,
2026-08-11 19:21:19 +03:00
Lauri Ojansivu c623718db1 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-11 19:02:58 +03:00
Lauri Ojansivu 4b89394087 CHANGELOG: PassBleed is the Upcoming release, not part of v10.82.
v10.82 was prepared and released while this was being written, so the PassBleed
entry had landed in a section for a release that had already gone out without the
fix - the same way the chart-index entry did after v10.81.

It moves to a new Upcoming section with its own summary and binaries table, and
v10.82 goes back to naming WhereBleed as its single CRITICAL issue, which is what
it actually shipped.

Thanks to TWPaMWang and xet7 !
2026-08-11 19:02:58 +03:00
Lauri Ojansivu 5269d0ba5c PassBleed: the Excel card export authorised one board and read a card from another.
GHSA-6p5m-f9p2-wqm5, CWE-639, CVSS 6.5.

    GET /api/boards/:boardId/lists/:listId/cards/:cardId/exportExcel

checked whether the caller could see the board in :boardId, then resolved the card
by :cardId alone. Nothing confirmed the card was on that board, so the two
identifiers came apart: one decided the authorisation, the other decided the data.
canExport() is given this._boardId and never sees this._cardId, so it can answer
"may this user see that board?" and cannot answer "may this user see that card?".

The pass is self-service. POST /api/boards takes `permission` straight from the
request body, so any authenticated user could mint their own PUBLIC board, name it
as :boardId, and pass the id of a card in somebody else's private board as
:cardId. :listId was never used in a query at all and could be any string.

What came back was the card: title, full description, members and assignees, every
comment with its author, checklists and checklist items, subtask titles,
attachment metadata - and, because image attachments are read through
getReadStream() and embedded with workbook.addImage, the attachment BYTES, which
is functionally an arbitrary private attachment download. One board could be
reused while :cardId was substituted, making it a scriptable bulk-collection
primitive rather than a single disclosure, and the REST API is on by default in
the shipped Docker configuration.

THE FIX WAS ALREADY IN THE CODEBASE, ONE FILE AWAY. The structurally identical
PDF route has always resolved getCard({ _id, boardId, listId }) and 404s a
cross-board id (ExporterCardPDF._getCardData). That control is what shows the
Excel exporter's omission was a defect rather than a decision, and it is exactly
what the Excel exporter does now.

Constraining the QUERY matters more than adding a check after it. The exporter
fans out on the same card id - checklists, checklist items, subtasks, comments,
attachments - and none of those carry a board constraint of their own. A card that
cannot resolve outside the authorised board makes every one of them safe by
construction rather than by a second guard that could be missed the same way.

The route binds the two identifiers as well, before either branch builds. That is
deliberate duplication: it is cheapest to state where both identifiers arrive
together, and it covers the public-board branch, which skips authentication
entirely and would otherwise rely on the exporter alone. A card that is not on the
named board is a 404 rather than a 403, so the difference does not tell an
unauthorised caller whether a given card id exists.

Audited the reporter's systemic point: the only two single-card export routes are
this one and the PDF one, and the attachment handlers already check
card.boardId !== boardId. tests/exportExcelCardContainment.test.cjs pins the
constrained lookup in both places, that the route checks before EITHER branch
builds, the 404, and that the PDF control it copies is still there.

Thanks to TWPaMWang and xet7 !
2026-08-11 19:02:58 +03:00
github-actions[bot] 3b6b601085 Bump versions for v10.82 2026-08-11 14:44:14 +00:00
Lauri Ojansivu fcc3951f40 Prepare v10.82 release 2026-08-11 17:43:11 +03:00
Lauri Ojansivu 418ea838bc CHANGELOG: the two developer-facing fixes, and the summary they were missing from.
Every commit since v10.81 now has an entry. The two that did not were both about
a build or a test run rather than about WeKan, so they go in a developer-facing
subsection at the end, which is where the format puts them:

  the database-conformance stage opening a debug port nothing in it uses, so an
  unrelated FerretDB on 8088 made every backend report a database problem that
  was not one;

  two more mongo42 give-up paths deleting the directory their own stage filter
  names, which on amd64 and arm64 would end the snap build the same way it ended
  all four Launchpad ones.

The subsections now read as one sentence in the order the format asks for -
CRITICAL security, then the new feature, then the bugs, then these - and the
summary paragraph accounts for them instead of stopping at the bug fixes.

Thanks to xet7 !
2026-08-11 17:42:43 +03:00
Lauri Ojansivu 09a525ae83 Conformance: do not open the debug port nothing in the run uses.
From the 2026-08-11_16-55-04 run, every backend failed before a single query was
compared:

    Failed to create debug handler ... listen tcp 127.0.0.1:8088:
      bind: address already in use
    ERROR sqlite      FerretDB did not start on this backend
    ERROR postgresql  FerretDB did not start on this backend

FerretDB opens a debug handler for metrics and profiling at 127.0.0.1:8088 by
default and EXITS when that address is taken. An unrelated FerretDB was running
on the machine, so the conformance stage reported a database problem that was
nothing of the sort - and would have reported it for any developer with anything
on 8088.

The script already takes exactly this seriously for the two ports it knows about:
it picks a free wire port, makes both overridable, and says in its own comment
that they are "chosen so this can run WHILE something else is running". The debug
port was simply never passed, so it always took the default. Nothing in the run
queries it, so the fix is not to find it a free port but not to open it:
`--debug-addr=-`, which is how FerretDB's main.go spells that
(`if addr := cli.DebugAddr; addr != "" && addr != "-"`). All four handler
launches pass it.

Verified against the built binary with 8088 deliberately held: without the flag
FerretDB does not come up and logs that error, with it it starts and logs
nothing.

tests/conformanceDebugPort.test.cjs pins it for all four backends, that the wire
port is still chosen to be free (the care that was already there), and that the
reason stays written down beside the other ports - the next person choosing one
needs to know there is a third.

Thanks to xet7 !
2026-08-11 17:35:48 +03:00
Lauri Ojansivu 68dbbd8b47 CHANGELOG: collapsible checklists and card sections.
A new-feature subsection between the CRITICAL security one and the bug fixes,
which is where the format puts features, and a line in the summary paragraph.

Thanks to czinkos, MikeRatcliffe, JannetGen and xet7 !
2026-08-11 16:43:19 +03:00
Lauri Ojansivu 1cef5ede9a Collapsible checklists and card sections, on the opened card and the minicard.
#1591, open since 2018: "It would be great to have collapsable checklists on
cards" - "Long checklists can make a card pretty cluttered, so being able to
collapse them and expand only when needed would keep the board much cleaner."

WeKan had something adjacent and it is NOT this. A checklist carries
`hideAllChecklistItems`, reachable through a toggle switch inside the checklist
actions popup - but that is a field ON THE CHECKLIST, so flipping it changes what
everyone on the board sees, and it is an edit to the card rather than a view
preference. Wrong thing, and buried where nobody folding a list would look. It is
untouched here; it has its own uses (showChecklist, isFinished).

Collapsing is per-user, which WeKan already says twice in its own models:
lists.js "collapsed state is per-user only, stored in user profile.collapsedLists"
and swimlanes.js the same for swimlanes. So this follows them:
profile.collapsedCardSections[cardId][sectionKey], beside those two.

ONE map for every foldable thing on a card, because they are the same question. A
feature group uses its own section name ('labels', 'members', 'due', ...); an
individual checklist uses 'checklist-<id>', which is why folding a checklist on
the opened card folds it on the minicard too - the same key, both places.

The control is a caret on the title, not a third entry in a menu: the whole point
is to fold at a glance while reading the card. It points down when open and right
when folded, carries aria-expanded, and answers Enter and Space, because it is an
<a> with no href and would otherwise be unreachable without a mouse. The click
stops propagation - on the opened card the title opens the inline rename form,
and a minicard is a link to the card.

For the sixteen feature groups on the opened card this is done ONCE, with a
delegated handler and CSS, rather than by editing sixteen templates - and it had
to be, because they are not uniform inside: all of them open with an
`h3.card-details-item-title` but only three wrap what follows in a
`.card-details-item-content`. So the title is the handle and folding hides every
sibling after it, which works whatever a section puts there. The caret is drawn
by CSS on the title for the same reason.

Two things this is careful about. A checklist's progress bar stays visible when
folded - it is the summary of what was folded away, and hiding it too would leave
a title that says nothing. And the fold survives reopening the card: the click
toggles the class and stores it, but a fresh render only has the stored state, so
cardDetails re-applies it in an autorun.

`float: inline-start` and logical margins on the caret, so it sits on the reading
-start side under dir=rtl - tests/rtl.test.js enforces that across component CSS
and caught a physical `float: left` here.

tests/checklistCollapse.test.cjs pins the per-user placement against its two
siblings, that the setter writes the USER and never the checklist, that "never
set" stays distinguishable from "expanded", the caret's state and keyboard
reachability, that the minicard uses the same key, and that a folded section hides
the siblings rather than a content element that most sections do not have.

Thanks to czinkos, MikeRatcliffe, JannetGen and xet7 !
2026-08-11 16:41:48 +03:00
Lauri Ojansivu 71992ef546 CHANGELOG: the queue - the snap markers, notifications, card click, dates and template containers.
Five entries and a rewritten summary paragraph, which now leads with WhereBleed
and names the snap upgrade that never retried.

Thanks to Philippe-Bentegeac, JDeepix, imlit, Nissulya, csonkaoszimt, Heart1010
and xet7 !
2026-08-11 16:31:33 +03:00
Lauri Ojansivu 80f5518724 Remove the Templates containers made for accounts that never used them.
From the cpu-monster email feedback: FerretDB at 190-350% CPU on an instance with
14490 boards, of which 13404 are template containers, for 9264 accounts of which
478 have ever logged in.

Before v10.00 every new account got a "Templates" container board at signup
whether or not the person ever saved a template. That was made lazy in v10.00
(#2339, #5850) - `ensureTemplatesBoard` creates one only when a template is
actually saved - so no NEW account creates one. But nothing ever removed the ones
already made, and they are not visible enough for anybody to delete by hand. On
that instance they are 13x the boards collection, and every query that touches
boards carries it; under polling reactivity that is every poll.

THIS DELETES BOARDS, so the rule for what may go is the whole of the risk and is
deliberately narrow: only a container nobody ever used. Anything at all having
been done to one keeps it - a template saved into it, a list, swimlane or card
added, a second member, a rename, a star, a manual archive. The rule is in
models/lib/unusedTemplateContainers.js on its own, so it can be read and tested
without the method around it, and every kept board comes back with the reason it
was kept.

A rename is judged only against the titles the app itself used, supplied by the
caller: that string is translated and has changed over time, so with no list to
compare against the title is not judged at all rather than a board being deleted
because its default name is in another language.

And it does nothing unless asked twice. The default is a dry run reporting what
WOULD go, with a sample rather than thirteen thousand rows; `apply: true` is what
deletes. For a collection this size that is the difference between a cleanup and
an accident. Admin only, checked before it reads anything. The
`profile.templatesBoardId` pointer is cleared with the board, or the next
ensureTemplatesBoard trips over a board that is not there, and one failed
deletion does not abandon the rest - the ones that failed are reported.

tests/unusedTemplateContainers.test.cjs is mostly about what must NOT be removed:
each of the four things that can be inside one, each of the four ways a person can
have claimed one, every non-container board type, junk input, and that the dry run
returns before anything is deleted.

Thanks to xet7 !
2026-08-11 16:28:38 +03:00
Lauri Ojansivu 097ad9160a Snap: a new revision is a new chance, so the 4.2 reader actually gets to run.
#6471, reopened against 10.81: "on 10.81 I still have the exact same issue,
MongoDB cannot start. The web interface is still unreachable, and I do not see
the messages you added in the last commits."

The messages were not printed because the code that prints them never ran. The
4.2 reader added for this issue was in their snap and was never reached, because
two markers in $SNAP_COMMON are PERMANENT:

  .mongodb-data-too-old   written when no reader in the snap could open the data.
                          mongodb-control then refuses to start mongod at all,
                          and wekan-control serves an explanatory page instead of
                          WeKan.
  .mongod-start-failures  a counter; past three, mongodb-control stops re-running
                          the MongoDB -> FerretDB migration, so a
                          migration<->mongod restart loop cannot form.

Both are right, and both record a conclusion about what THAT SNAP could do. An
instance that had already failed on 10.79 or 10.80 - before mongod 4.2 was
bundled - carried a marker saying "no reader can open this" and a counter far
past three. So on 10.81 mongod was never started, the migration was never
attempted, and nothing new was printed. Upgrading to the version with the fix
changed nothing, which is exactly what their log shows: the database-selection
line, then "Waiting for MongoDB replica set primary..." repeating forever.

A conclusion about an older revision is not an answer about this one. Each marker
now records the revision that wrote it (snap-src/bin/stale-marker), and a marker
from a different revision is ignored and cleared, so the snap tries again with
whatever it can do now. A marker with no revision recorded at all is stale by
definition - that is precisely what the affected instances are carrying.

Within one revision nothing changes: same revision, same refusal, same three-try
loop protection. Not knowing the revision (running outside a snap) is never
treated as evidence that it changed, so nothing is cleared on a guess.

Three places consult it. mongodb-control clears both the marker AND the counter
before deciding - an instance that gave up on an older revision has both, and
leaving one latched fixes nothing. wekan-control makes the same judgement because
it can start first and would otherwise keep serving "your data is too old for this
snap" on a snap that has not tried. migration-control stamps the marker when it
writes it. A mongod that does start still clears the counter and its stamp
together.

tests/snapStaleMarker.test.cjs runs the helper for the reported case, the same
revision, the no-stamp case, and the two ways of not knowing, then pins all three
call sites.

Thanks to Philippe-Bentegeac, JDeepix, imlit and xet7 !
2026-08-11 16:24:52 +03:00
Lauri Ojansivu 6420819cfa Clicking an open card closes it, and a focused checkbox is not a diamond.
The two newest comments on #6465.

CLOSING BY CLICKING AGAIN was not a missing feature - it was an unreachable one.
clickOnMiniCard already ended with

    } else if (Session.equals('currentCard', card._id)) {
      Utils.goBoardId(Session.get('currentBoard'));

but the TITLE branch above it returns before that is ever reached, and a
minicard's title covers most of the minicard. So the second click almost always
landed in the title branch, which unconditionally re-opened the card that was
already open. The toggle worked only if you managed to click a part of the card
that was not its title, which is why two people asked for a feature that was
already written.

The title branch now asks the same question first, and closes the same way. A
click on a card that is NOT open still opens it, and still clears the phone popup
ids as it did. On a mini screen the card is a popup rather than a pane, so
"already open" is a different question - `popupCardId` and `Popup.isOpen()` - and
that path closes with `Popup.back()`. Multi-selection still short-circuits both,
so ticking cards is unaffected.

THE PROBLEMS PAGE GLITCH in the screenshot is the focus ring. The Admin Panel
draws its checkboxes as a material square that morphs into a tick, and the tick
IS a 40-degree rotation of the element. A browser draws its focus ring around the
element as it is - rotated - so a checkbox that is both checked and focused, which
is exactly what a checkbox you just clicked is, comes out as a blue DIAMOND where
the tick should be.

The ring is not removed: keyboard users need to see where they are. It moves to
the row that contains the checkbox, which is not rotated - every one of these sits
inside a label (problemsSummary.jade) - so it is a normal rectangle around the
whole row, which is easier to see than a 13px box anyway. `:focus-visible`, so a
mouse click does not leave a ring behind.

tests/cardClickToggle.test.cjs pins that the close decision comes before
re-opening (or it never happens), that opening a different card and the existing
non-title toggle both still work, that multi-selection still wins, that the tick
is still the rotation, and that the label nesting the ring relies on is really
there.

Thanks to csonkaoszimt, Heart1010 and xet7 !
2026-08-11 16:19:43 +03:00
Lauri Ojansivu 184e1713d3 Notifications: cap the tray array, which SQLite was being rewritten by.
#6533. A snap instance reports FerretDB at 737% CPU, logins taking over a minute,
boards not appearing, and logs full of:

    [db-retry] the database was busy: 0 write(s) retried, 0 succeeded on a retry,
      3 given up on.
    ValidationError: ... [collection.go:191 sqlite.(*collection).UpdateAll]
      database is locked (5) (SQLITE_BUSY)
      at _helpersConstructor.addNotification (models/users.js:2248)

The stack names the same write every time. addNotification is an `$addToSet` on
`profile.notifications`, which is an array INSIDE the user document: adding one
entry means reading the whole document, scanning the array for a duplicate, and
writing the whole document back. The cost is proportional to the array. FerretDB
on SQLite has a SINGLE writer, so those rewrites queue behind each other and
start failing outright - and login, which also writes to the user document,
queues behind them. That is the slow login, the boards that never arrive and the
pinned CPU, all from one array.

WHY IT GROWS WITHOUT LIMIT: the cleanup added in #5685 removes a notification only
when every entry for its activity has been READ and is past its removal age. A
user who does not clear their tray accumulates entries forever, and nothing else
in the product shrinks that array. The instance in the report has been running
since at least 2026-07-29 with contention warnings, which is what an array that
only ever grows looks like from the outside.

So the same cleanup pass now also caps it: the newest NOTIFICATION_TRAY_MAX_PER_USER
entries are kept (default 1000, a backstop rather than a retention policy) and the
rest dropped. Newest rather than oldest because `$addToSet` appends, so the tail
is the recent activity a tray is for; an unread notification from months ago is
not something anybody is going to act on.

Three things it is careful about. The cap is applied to what is LEFT AFTER the
expiry pull, not before, or it would trim entries that were about to be removed
anyway and keep fewer than the cap. A user needing neither change is not written
to at all - the whole problem here is writes to user documents, so the cleanup
must not add one per user per day for users with nothing to do. And it stays ONE
write per user, which is what #5685 collapsed K writes into; the capped case uses
`$set` because a `$pull` would have to list the entries it is removing, and there
being too many of them is the point.

This bounds the array. It does not make SQLite a multi-writer engine: an instance
this busy still wants the PostgreSQL backend, which is what the Problems pane
already tells the admin.

tests/notificationCap.test.cjs pins the decisions - keeping the newest, leaving an
array under the cap completely alone (so no write), refusing to act on junk or a
disabled cap, that unread entries are what this catches because the existing rule
provably cannot, and that it is still one write per user.

Thanks to Nissulya and xet7 !
2026-08-11 16:15:06 +03:00
Lauri Ojansivu 0c5cb99b7c Card dates: a typed two-digit year is refused, not stored as the year 26.
Email feedback: "If i write the expiration date with the keyboard it turns red,
if i choose it with the date picker it is yellow. Can you please tell me the
difference?"

It is a bug, and the colour was never it. `<input type="date">` reports its value
as YYYY-MM-DD, but a browser lets the YEAR sub-field be typed as two digits and
reports exactly that: entering 31-12-26 gives "0026-12-31", the year 26 AD. That
is a perfectly valid Date, so nothing refused it, and the card was saved with a
due date two thousand years in the past. Red is what an overdue date is supposed
to look like. The same day chosen from the calendar always fills four digits, so
it is 2026 and yellow, because it is only due soon.

The attached screenshot shows it rather than the colours: the yellow badges read
"31-12-2026 17:00" and the red ones "31-12-26 17:00" - two different years.

Saving now refuses a year outside 1000-9999 with "Invalid year. Please type all
four digits, for example 2026." and puts the cursor back in the date field. The
window is deliberately wide: WeKan holds real historical received dates and
long-dated deadlines, and this is only meant to catch a year that cannot have
been intended.

REFUSED, NOT CORRECTED. 0026 was almost certainly meant as 2026, but silently
rewriting a date somebody typed - one that other people's due-date reminders hang
off - is a guess in the wrong place. The message says which digits are missing,
which is the thing the reporter could not see.

tests/datePickerYear.test.cjs pins the reported value the browser really hands
over, the date that was meant, the width of the window either side, that the
refusal happens before storeDate, and that nothing rewrites the year.

Thanks to xet7 !
2026-08-11 16:07:47 +03:00
Lauri Ojansivu b6d0be0f45 Snap: the other two mongo42 give-up paths deleted their own staged directory.
The earlier fix for the stage filter covered one of three ways this part gives
up - the unsupported-architecture `exit 0`. The other two did `rm -rf "$dest"`
and exited 0, which leaves `stage: mongo42` naming a path that is not there, and
snapcraft ends the whole build on that:

    Failed to copy '/build/.../parts/mongo42/install/mongo42': no such file or directory

Those two are reached on amd64 and arm64, where the binary IS downloaded: OpenSSL
1.1 could not be obtained for the architecture, or the staged mongod 4.2 failed
the smoke test that checks it actually runs. Either would have ended the snap
build for the two architectures that matter most, in the same way it ended all
four Launchpad ones.

Both now remove the CONTENTS and keep the directory. An empty mongo42/ stages to
nothing, and migration-control guards every use on `-x "$M42/bin/mongod"` - the
binary, not the directory - so "no 4.2 reader here" is still exactly what an empty
one means. Checked: that is the only guard, so an empty directory cannot be
mistaken for a working reader.

tests/snapPartFilters.test.cjs now requires every give-up path to clear contents
only, rather than pinning the single shape the first failure happened to take.

Thanks to xet7 !
2026-08-11 15:59:29 +03:00
Lauri Ojansivu 3ebc38cb40 CHANGELOG: WhereBleed leads the release.
A CRITICAL SECURITY section comes first, so the bug subsections that follow it
become "and fixes ...". The summary paragraph leads with it too, in plain text -
the In short paragraph links nothing; the entry does.

Thanks to TungNGo02 and xet7 !
2026-08-11 15:57:11 +03:00
Lauri Ojansivu b4ebe48d7b WhereBleed: eight Admin Panel handlers ran the caller's selector unchecked.
GHSA-phm4-4v26-j2vq, CWE-943, CVSS 5.8. The Admin Panel's people, org, team and
translation publications and their companion count/page methods take a query
selector from the client and validate only its TYPE:

    check(query, Match.OneOf(Object, null));

which is not validation, because a MongoDB selector is executable data. `$where`
makes the database run the caller's JavaScript once per document scanned, so

    Meteor.subscribe('team', { $where: 'while(true){}' }, 25, 0)

pins a database worker for as long as the caller likes and is repeatable: denial
of service for every tenant on the instance, from one narrowly-scoped account.
The reporter demonstrated both halves on v10.81 against mongo:7 - `$where:
'sleep(2000) || true'` made the `people` subscription take 2.03s and return the
document, `$where: 'false'` returned nothing in 0.00s - which is the caller
deciding in JavaScript which documents come back.

It needs an authenticated admin session, so it is not reachable by a board member
or a visitor. It is still worth the fix at this severity because the people and
org surfaces are open to a PER-TENANT Global Admin, a role meant to be confined
to one Organization, and those two wrap the caller's selector as
`{ $and: [query, restriction] }` rather than stripping execution operators out of
it - so merging the tenant restriction never removed the `$where`, and a role
scoped to one tenant reached instance-wide impact.

WHAT MAKES THIS ONE PARTICULAR is that the defence was already here. classifySelector
(models/lib/injectionDetect.js) and hasWhere (models/lib/mongoSelectorSafety.js)
were written for exactly this class and were wired into the card-window
publication, with unit tests. Eight sibling handlers taking the identical shape of
client-supplied selector simply never called them. So this adds no new detection
logic: cardsWindow.js's own helper moves, unchanged, to
/server/lib/selectorGuard.js, and all nine call sites share the one copy. A second
copy of the check would be the same bug set up to happen again, which is why
cardsWindow.js now imports rather than keeps its own.

Each handler refuses with the "match nothing" selector that publication already
uses in production, `{ _id: { $in: [] } }` - frozen, so a caller cannot mutate the
shared object into one that matches - so a refused request returns an empty result
instead of throwing at an admin mid-page, and the canary still records who sent
what from where. Ordinary Admin Panel behaviour is untouched: none of the
searches, filters, regexes, $or/$and/$in/$elemMatch or date ranges those panes
send contains an execution operator, and the test pins fourteen real ones going
through unchanged.

FerretDB, WeKan's default database, rejects $where itself, so this degrades to a
rejected query there; the supported real-MongoDB path
(docker-compose-mongodb-v7.yml) is where it was reproduced. MongoDB 7 also happens
to reject $where inside the aggregation pipeline the four count methods use, but
that is an engine-level accident for one operator on one call path - $function and
$accumulator are legitimate aggregation operators - so the count methods are
guarded like the rest rather than left to it.

tests/selectorGuard.test.cjs pins the reported payloads (including nested inside
$and/$or, which is how the tenant merge would carry them), the other execution
operators, the fourteen ordinary queries, that all nine call sites route through
the guard, that none still passes the raw selector afterwards, and that exactly
one copy of the check exists. Three existing suites asserted the old shape -
canaryCoverage on where the canary trips, injectionDetect on the helper living in
cardsWindow.js, tenantWiring on the scope function receiving the raw query - and
each is updated to the new one, saying why.

Thanks to TungNGo02 and xet7 !
2026-08-11 15:50:31 +03:00
Lauri Ojansivu 09ce64ecf6 CHANGELOG: the snap database copy and the card drag scroll.
The two issues reported after upgrading from v6: a FerretDB migration that had
fallen weeks behind the MongoDB beside it and was being served in its place, and
a card drag that scrolled the board instead of the list because the vertical
auto-scroll never picked the list under the pointer.

Thanks to markusst1982 and xet7 !
2026-08-11 15:34:16 +03:00
Lauri Ojansivu 8456170d45 Dragging a card down scrolls the list, not the whole board.
"With v6 i was able to move a Card downwards by drag & Drop. Upwards is no
   problem, the Line scrolls automaticly up, but this does not work downwards.
   The whole Page/Site scrolls down and not ne Line"

The card sortable's `sort` handler auto-scrolls at the edges. HORIZONTALLY it
picks the lane under the pointer (#443). VERTICALLY it picked nothing: it always
scrolled `.board-canvas`, which is `overflow-y: auto` and holds the swimlanes,
rather than the `.list-body` under the pointer, which is `overflow-y: scroll` and
holds the cards. Scrolling the canvas moves the whole board - which is the report,
in the reporter's own words.

The asymmetry is what makes it reproducible rather than vague. Dragging UP, the
canvas is usually already at scrollTop 0, so computeEdgeScroll returned null, the
handler did nothing, and jQuery UI's own `scroll` option - which acts on the
placeholder's scrollParent, i.e. the list body - scrolled the list, which is why
"upwards is no problem". Dragging DOWN, the canvas nearly always has room left,
so the handler fired first and scrolled the board instead of the list.

The list body under the pointer is now scrolled first, found with the same
rectangle hit-test the lanes use, and the canvas only when the list cannot go
further that way - so a drag down a long list scrolls the list, and a drag past
the end of it moves on to the board, which is what dragging a card to another
swimlane needs. The horizontal lane scroll is unchanged, and the list branch sets
`scrolled` so the #6477 refreshPositions still runs after it: a manual scroll
invalidates jQuery UI's cached geometry whichever element was scrolled.

tests/cardDragVerticalScroll.test.cjs pins the order (list before canvas, canvas
only when the list did not move) and runs computeEdgeScroll with real numbers for
the three cases that decide it: a pointer in the bottom edge zone of a scrollable
list, the same at the end of its scroll range, and a list that does not overflow.

Thanks to markusst1982 and xet7 !

Fixes #6584,
2026-08-11 15:32:47 +03:00
Lauri Ojansivu b1e7b1e538 Snap: never serve a FerretDB copy that is older than the MongoDB beside it.
After upgrading from v6 the reporter saw "the state of the data from days ago"
and suspected a `snap revert` done four weeks earlier. They were right about the
cause, and nothing was lost.

The MongoDB -> FerretDB migration copies MongoDB into SQLite ONCE and writes
.migration-to-ferretdb-done. That copy is a snapshot; nothing keeps it in step.
Revert the snap to a revision that runs mongod and WeKan carries on writing to
MongoDB - for four weeks here - while the finished SQLite sits frozen at the date
it was made. Refresh forward again and mongodb-control's
switch_to_completed_ferretdb saw a marker plus a non-empty SQLite, called that a
completed migration, and switched the snap onto it. Every board and card written
during the revert was still on disk and simply not being served.

The data was never in danger: it lives in $SNAP_COMMON, which is shared across
revisions and is NOT rolled back by a revert (unlike $SNAP_DATA, which is
per-revision). What was wrong was WHICH of the two copies got served, and nothing
compared their ages.

snap-src/bin/ferretdb-migration-stale answers that one question: is the completed
migration older than the MongoDB data it was made from? mongod rewrites its
WiredTiger files whenever it commits, so the newest mtime among them is when
MongoDB was last written to; later than the marker means the copy is behind. It
counts DATA files only - a newer mongodb.log or .pid means the snap was started,
not that the database changed - and allows a margin, because the migration stops
its own temporary source mongod moments after writing the marker and that
shutdown touches the data directory. Anything it cannot tell (no marker, no
SQLite, no MongoDB data, unreadable mtimes) is reported as NOT stale: the callers
act on a yes, so being unsure has to leave the existing behaviour alone.

Three places act on it:

  mongodb-control's switch_to_completed_ferretdb refuses a stale migration and
  leaves MongoDB running, which has the newest data.

  mongodb-control's handle_mongod_start_failure - where mongod will not start at
  all, so keeping MongoDB is not on offer and the choice is between a fresh copy
  and a stale one - migrates AGAIN. The migration reads with its own temporary
  mongod and the 4.2/3.2 readers, none of which is the mongod that just failed,
  so it can still reach everything written since. It has to be restarted rather
  than resumed: migration-control exits immediately while the marker is there,
  and the checkpoint and old SQLite describe a finished copy of the old data, so
  the marker, the checkpoint and files/db are cleared first. The MongoDB data is
  never touched.

  wekan-control gains the second half of a guard it already had. It refused to
  start an EMPTY FerretDB while MongoDB had data; a FULL but out-of-date one looks
  worse, because WeKan comes up with boards and cards all present and only the
  last weeks missing, which reads as data loss rather than as a misconfiguration.
  It now stays on MongoDB and says why, including that nothing is lost.

tests/ferretdbMigrationStale.test.cjs runs the detector against synthesised
$SNAP_COMMON directories - the reported four-week case, a fresh migration, the
margin either side of it, a newer log file, and each "cannot tell" - and pins the
wiring at all three call sites.

Thanks to markusst1982 and xet7 !

Fixes #6583,
2026-08-11 15:32:32 +03:00
Lauri Ojansivu dac8d1da2f CHANGELOG: the two wekan-ldap links point into wekan/wekan.
github.com/wekan/wekan-ldap no longer exists - the API redirects and then answers
422 - so the two links in the v2.52 (2019) entry for "Added and then reverted
option to redirect OIDC OAuth2 login" were dead.

The HASHES do not carry over. The wekan-ldap code was copied into
packages/wekan-ldap on 2019-04-20 ("Include to Wekan packages directory
contents"), not history-merged, so 82a894ac and 36900cc exist in no repository
now: GitHub answers 422 for both under wekan/wekan, and neither object is in this
clone. Repointing the URL alone would have produced a link that still 404s.

What DOES exist is the pair of wekan/wekan commits that recorded those two steps
on the same day, and the first of them is certain rather than inferred - its
commit message IS the old link:

  part1  82a894ac  ->  188d42dcd  "[Add option to redirect OIDC OAuth2 login]
                                    (.../wekan-ldap/commit/82a894ac...)"
  part 2 36900cc   ->  d173844f9  "OAUTH2_LOGIN_STYLE popup or redirect,
                                    part 2 and 3."

So a reader following part1 still sees the original wekan-ldap hash, in the
message of the commit they land on. part3 is unchanged: 7919ae362 is in
wekan/wekan already and is the code change of that day (Dockerfile,
docker-compose.yml, server/authentication.js, the snap config).

Both targets checked against the GitHub API before use: HTTP 200.
releases/fix-changelog-hashes.sh --all-sections now reports all 3235 commit links
resolving.

Thanks to xet7 !
2026-08-11 15:18:26 +03:00
Lauri Ojansivu c452a2defd CHANGELOG: the canary-tokens entry links FerretDB, where that commit lives.
releases/fix-changelog-hashes.sh --all-sections checked all 3234 wekan/wekan
commit links and found exactly one that resolves to nothing:

  https://github.com/wekan/wekan/commit/a260a53e

It is not a rewritten hash, so there was nothing for the script to repoint - it
reported it and left it, which is what it should do. The hash is fine; the
REPOSITORY in the URL was wrong. The entry describes `internal/util/canary`,
which is a FerretDB path, and a260a53e is "Canary tokens: mark the operations the
client never issues." in wekan/FerretDB - confirmed both in .tools/FerretDB and
against the GitHub API, which serves it there and answers 422 for it under
wekan/wekan.

Thanks to xet7 !
2026-08-11 15:13:20 +03:00
Lauri Ojansivu c8393f3f53 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-08-11 11:36:19 +03:00
Lauri Ojansivu be558257cd CHANGELOG: the image-check work belongs to the next release, not to v10.81.
v10.81 was prepared, tagged and published (at "Bump versions for v10.81") while
this was being written, so the entry describing the chart-index image check was
added into a section for a release that had already gone out without it. A
release section has to describe what is IN that release; it is moved to a new
Upcoming section, where the change will actually ship.

What stays in v10.81 is what v10.81 really carries: the FerretDB
directConnection fix, All Boards on a phone, the retired Mac runner and the jobs
a cancellation skipped, the armv6 userland, the Launchpad snap part, and the two
chart commits that were pushed before the release was prepared.

Also: the new "is there an image for this version" check imports
releases/reindex-charts.py, and importing a module writes releases/__pycache__
beside it - untracked junk appearing in the repository on every release. It runs
with PYTHONDONTWRITEBYTECODE=1, verified to leave nothing behind.

Thanks to xet7 !
2026-08-11 11:36:18 +03:00
Lauri Ojansivu 7fc1f35bed CHANGELOG: link the image-check entry to its commit.
Thanks to xet7 !
2026-08-11 11:36:18 +03:00
Lauri Ojansivu a442959dda Charts: list only the ones whose container images still exist.
Backfilling the index taught this within the hour. Artifact Hub scans every entry
in a Helm index and mailed a list of errors:

    error scanning image ghcr.io/wekan/wekan:v9.62: image not found
    error scanning image docker.io/bitnami/mongodb:7.0.14-debian-12-r3: image not found

That was this side's doing, not Artifact Hub's. The rebuild listed every package
sitting on gh-pages, and a chart is a POINTER TO CONTAINER IMAGES: one whose
images have been deleted from their registries installs and then fails at the
pull, so listing it announces that the repository is broken when the repository
is fine and the images are gone.

135 of the 360 packages are in that state, from two unrelated causes. SIX have no
WeKan image - v8.30, v9.12, v9.14, v9.38, v9.39 and v9.62, releases whose own
docker job failed, and exactly the six ghcr.io errors that were reported. The
other 129 vendor the BITNAMI mongodb subchart and pin a tag Bitnami has since
deleted; charts from 8.41 on vendor groundhog2k's mongodb, which uses the
official `mongo` image and is unaffected.

The index now holds 225 entries: every one of the 216 that were listed before -
not one dropped - plus the 9 backfilled packages whose images all resolve. Twelve
listed charts were spot-checked against the registries afterwards and every image
pulled.

THE EXCLUSIONS ARE A FILE, unindexed.txt, beside the packages - not a check made
on every run. A rebuild during a release must not depend on reaching two
registries, where a blip would silently drop half the index; and a recorded
decision is reversible and reviewable, so an image that comes back is one deleted
line away from being listed again. The .tgz files all stay, so direct URLs keep
working. `--check-images` is what regenerates the file.

Two things this shook out, both about being careful with a negative result.

The registry handshake is now the REGISTRY'S OWN: ask for the manifest with no
credentials and let the 401 say which realm and scope to use. The first version
hard-coded a token URL per host and reported every quay.io image as missing -
including quay.io/wekan/wekan:latest, which plainly exists - because the wrong
endpoint returned a token quay would not accept and the manifest came back 404.
Four charts were nearly dropped on that. Related: a repository that already
begins with a host ("ghcr.io/wekan/wekan") must not be read as a docker.io path,
which is how a first pass came back "unknown" for every WeKan image.

And an image that cannot be CHECKED is never treated as missing - only a definite
404 excludes a chart. A registry that refuses anonymous auth or a request that
times out must not be able to silently unpublish anything.

release-charts.sh now refuses to publish a chart when
ghcr.io/wekan/wekan:v<version> does not exist, which is what created those six in
the first place. It checks before it touches the charts repository, and a
registry it cannot reach does not block a release.

Thanks to xet7 !
2026-08-11 11:36:18 +03:00
github-actions[bot] b6d003089c Bump versions for v10.81 2026-08-11 00:02:26 +00:00
Lauri Ojansivu fc1e089d38 Prepare v10.81 release 2026-08-11 03:01:27 +03:00
Lauri Ojansivu 35b44a1f8b CHANGELOG: link the chart-publishing entry to its commit.
Thanks to xet7 !
2026-08-11 02:54:55 +03:00
Lauri Ojansivu a434bb4bd5 Charts: both release workflows publish one, and the index is derived not edited.
The charts repository keeps the chart SOURCE on main and the published packages
plus index.yaml on gh-pages, and its own two scripts move between them: main's
release.sh commits the source, tars `wekan/` into wekan-<version>.0.tgz, checks
out gh-pages and drops the package there; gh-pages' release2.sh commits and
pushes it. release-charts.sh drives both and owns the index in between, and that
middle part is what changes here.

THE INDEX IS REBUILT FROM THE PACKAGES, not edited in place. It used to copy the
newest entry, substitute a few fields and prepend the result, and every one of
the index's four defects came from that: a re-run prepended a second entry for a
version instead of replacing it (9.36.0 ended with four, each a different digest,
so a helm client could pick one matching no file); appVersion was set to the
CHART version rather than the WeKan version on every entry; fields nobody
substituted never changed, so every entry claimed the mongodb subchart was 0.7.2
while the packages had moved to 0.7.6; and a release whose charts job did not run
left a hole nothing ever went back for. Deriving the index makes all four
impossible instead of repaired - one entry per package, its digest computed from
the file, its fields read out of the archive, and any package that was missed
picked up on the next run.

THE PACKAGE IS CHECKED AGAINST ITS OWN FILENAME FIRST. release.sh names the
tarball from its argument (`tar -cvzf wekan-$1.0.tgz wekan`) while the version
inside comes from the Chart.yaml this script sed-ed a moment earlier. When those
drift the repository gains a file called one version that declares another, and a
Helm index keys on the version INSIDE - so it can never be installed under the
name it was given. That is how wekan-1.2.7.tgz (containing 1.2.6) and
wekan-6.96.tgz (containing 6.9.6) came to exist; both are removed from the charts
repository, and a package like them now stops the release with a message naming
both versions rather than being published.

RELEASE ALL MISSING PUBLISHES A CHART TOO. It could rebuild any missing bundle
but not a missing chart - its header said the charts were out of scope - so a
release that never got a chart never would. It has a charts job now, and it is
the one part of that workflow which pushes to another repository, so it is
guarded twice: nothing happens without a WEKAN_REPO_TOKEN that can push to
wekan/charts (a named warning and a skip, not a failure inside git), and nothing
happens when the published index already lists that version, because
re-publishing would re-tar the package and change the digest of a chart people
have already pulled. Both workflows publish through the same
releases/release-charts.sh, so neither can drift from the other.

release-charts.sh also finds the checkout at .tools/charts, where CLAUDE.md keeps
companion repositories, before the older ../w/charts.

Verified against the real checkout: a package dropped in is picked up, sorted
first, with its appVersion and digest read from the archive - then removed and
the index restored. tests/releaseChartsIndex.test.cjs pins that the index is
derived rather than edited, that the filename check runs BEFORE indexing, that
both workflows publish through the same script, and that an already-published
version is not republished. It runs the script's own version-reading code against
a package named 1.2.7 that declares 1.2.6.

Thanks to xet7 !
2026-08-11 02:54:47 +03:00
Lauri Ojansivu 292ffb8906 CHANGELOG: the index.yaml rebuild entry.
Wrapped at 80 and repointed by releases/fix-changelog-hashes.sh after the amend
that rewrote its commit.

Thanks to xet7 !
2026-08-11 02:44:40 +03:00
Lauri Ojansivu 1a2ec06332 Charts: rebuild index.yaml from the packages the repository actually serves.
With wekan/charts cloned at .tools/charts, the index could be compared against
its own packages for the first time, and it had drifted from them in four ways at
once. All four come from the same habit: release-charts.sh writes an entry by
COPYING the previous entry and substituting a few fields, so the index was kept
as the record rather than as a description of the .tgz files beside it.

  146 PACKAGES HAD NO ENTRY. 362 packages, 220 entries. Those charts are
  downloadable by URL and invisible to `helm search`, to
  `helm install wekan --version 9.63.0` and to Artifact Hub. An entry is written
  once, during the release it belongs to, so a release whose charts job did not
  run left a hole and nothing ever went back for it.

  DUPLICATE ENTRIES WITH STALE DIGESTS. 9.36.0 had four entries and 10.30.0 two,
  each copy with a different digest and the same url, because a re-run of a
  release prepended a second entry instead of replacing the first. Only one
  digest per version can be the digest of the file served - the other four match
  no package in the repository - so a helm client that picked one of them failed
  the integrity check on a perfectly good file.

  A WRONG appVersion ON EVERY ENTRY. The index gave the CHART version there
  ("10.79.0") where the packaged Chart.yaml says the WeKan version ("10.79"),
  which is the one a reader is actually looking for.

  FROZEN DEPENDENCY VERSIONS. A field nobody substituted never changed: every
  entry claimed the mongodb subchart was 0.7.2 while the packages have been 0.7.6
  for some time.

releases/reindex-charts.py derives the index from the packages, reading each
one's Chart.yaml out of the archive - so an entry cannot describe a package
wrongly - and needs no helm, only python3.

Nothing is invented and nothing is lost. `created` is preserved exactly on every
entry whose digest matches its package; a package being added takes the date it
was committed to gh-pages, which is when it was really published (wekan-10.79.0
was committed at 2026-08-10T02:46:52Z and its entry already said created
2026-08-10T02:46:52.308983+00:00). Two 2023 packaging slips - wekan-1.2.7.tgz
containing chart 1.2.6, and wekan-6.96.tgz containing 6.9.6, each colliding with
the file whose NAME matches - are reported and left out rather than letting one
version mean two files; both files stay on the server, so a direct URL keeps
working.

Run against the real repository the index went from 220 entries to 360, and was
verified after writing: no repeated version, every digest equal to the sha256 of
its file, every file present, every url under wekan.github.io/charts, and no
version that was listed before missing.

backfill-charts.sh now looks in .tools/charts before ../w/charts, since that is
where CLAUDE.md keeps companion repositories, and both tools are Release menu
entries in build.sh and build.bat.

tests/reindexCharts.test.cjs runs the tool against a synthesised charts directory
- real gzipped chart packages, written without helm - and pins the rules that
have a decision in them: created is preserved rather than restamped, the
surviving duplicate is the one whose digest matches the file, a version claimed
by two packages is resolved by filename and REPORTED, no previously listed
version is dropped, and a report run does not touch the file.

Thanks to xet7 !
2026-08-11 02:43:19 +03:00
Lauri Ojansivu 2c7a016327 CHANGELOG: the Upcoming section for the six fixes.
Two bugs that stopped WeKan starting - the FerretDB replica-set handshake sending
the driver to 0.0.0.0, and All Boards on a phone - and four release-pipeline
failures: the retired macos-13 label that has never once built the Mac x64
bundle, the cancellation that silently skipped charts/ucs/nextcloud, the armv6
bundle assembled in a soft-float ARMv5 userland, and the optional MongoDB 4.2
part that ended every Launchpad snap. Plus the chart-index report.

The binaries table is v10.80's: nothing here rebuilds them.

Thanks to xet7 !
2026-08-11 02:19:44 +03:00
Lauri Ojansivu 18da0d2618 Snap: an optional part was ending every Launchpad build.
All four Launchpad architectures failed in v10.80 - armhf, s390x, ppc64el and
riscv64, three attempts each - and the job reported them as a Launchpad problem:
"often an OOM in the Meteor npm install, or a transient build-farm reset; re-run".
It was neither, and re-running was the one thing that could not help. The build
log, which the job prints, said the same deterministic thing every time:

    Staging mongo42
    Failed to copy '/build/.../parts/mongo42/install/mongo42':
      no such file or directory.
    Build failed

The mongo42 part downloads MongoDB 4.2 so a database from an old MongoDB snap can
still be migrated (#6471), and MongoDB publishes 4.2 for amd64 and arm64 only. On
every other architecture the part prints "nothing to migrate from there;
skipping" and exits 0 having created nothing - and its own comment calls that
"OPTIONAL BY DESIGN". It was not optional: the part also carries
`stage: [mongo42]` and `prime: [mongo42]`, and snapcraft does not skip a filter
whose path is missing, it ends the build. So a migration helper that no exotic
architecture has ever needed took the whole snap down on exactly the four
architectures that have no native runner and can only be built on Launchpad.

The directory is created before anything can decide to skip, so the filters
always have something to copy. The binary is still not downloaded anywhere but
amd64 and arm64, and migration-control still finds nothing there - "no MongoDB
4.2 to migrate from" is a missing binary again instead of a failed build.

riscv64 additionally lost its last attempt to the build farm's network:

    npm ERR! code ECONNRESET
    npm ERR! network request to .../string-width-4.2.3.tgz failed, reason: socket hang up
    'override-build' in part 'wekan' failed with code 1.

after half an hour of building. Every request there goes through Launchpad's
proxy and one tarball out of a couple of thousand can be cut mid-stream; npm's
own --fetch-retries covers the request, not a dropped socket. That npm install is
retried three times now, and still fails loudly if it is not the network.

The ::error:: no longer guesses. It sends the reader to the build log that is
already printed above it and lists what to look for in the order worth checking,
starting with a "Failed to copy .../parts/<part>/install/<path>", which is a
stage:/prime: filter naming something that architecture does not build and will
never pass on a re-run.

tests/snapPartFilters.test.cjs pins the ordering inside mongo42, that it is still
amd64/arm64 only, the npm retry, and the general rule the failure was one
instance of: a literal path in stage:/prime: must be something its part actually
produces.

Thanks to xet7 !
2026-08-11 02:16:30 +03:00
Lauri Ojansivu c0a5c2b7c9 All Boards on a phone: the scroll, the table view, the 4x tiles and the spacing.
Four things, reported together, in a issue that had been closed three times and
reopened against 10.10, 10.37, 10.38, 10.72 and 10.77.

THE SCROLL. Every fix so far removed viewport arithmetic from boardsList.css and
said so in its comments - "no viewport arithmetic here at all now". The last
piece of it was in layouts.css, where nobody looked:

    body.mobile-mode #content { height: calc(100dvh - 48px) }

48px is a guess at the height of the header. The header is not 48px and is not
any one number: Utils publishes --wekan-header-height from a ResizeObserver for
exactly this reason, and its own comment says the quick-access bar wraps to a
second and third row depending on language and window width and that "every fixed
number for it has been wrong for some of those". So on a phone whose bar wraps,
#content was TALLER than the room under the header, and its bottom sat below the
screen. body.mobile-mode is `position: fixed` and the phone rules make it
`overflow: hidden`, so that strip is unreachable, and the `height: 100%` chain
below inherited all of it: the board list scrolled, it just ended off screen, and
its last boards were where no gesture could reach them.

Nothing has to be computed. body is a flex column of exactly one viewport and
#content is its `flex: 1` item, so the space under the header IS that box, at
whatever height the header really is this paint. The calc was overriding a
correct answer with a wrong one. It is gone, and `min-height: 0` lets the chain
below actually shrink.

THE TABLE VIEW. The right column has two branches - the board icons and
`+tablePage` - and only the icons were ever given a scroller. In Table view the
table grew past the bottom of the column and `#content .wrapper` is
`overflow: hidden`, so everything past the fold was clipped with nothing to
scroll. Every section of All Boards can be shown as a table, so that was half the
page for all of them. `.table-page` now takes what the column has left and
scrolls in both directions, and the pane heading is `flex: 0 0 auto` so it keeps
its height instead of being squeezed while the list stays too tall.

THE 4x-TALL TILES. A grid defaults to `align-content: stretch` and
`align-items: stretch`, so a list SHORTER than its column has the leftover height
divided among its rows and each tile grown into it - and a board tile paints its
board colour over the whole cell, so one row of boards became one band several
times a normal icon's height. This was already fixed once, but only on
`.board-list.mobile-view`, the class the template sets from Utils.isMiniScreen().
The phone MEDIA QUERY builds a grid too, and a narrow window that is not a mini
screen - a desktop browser under 800px, a tablet in desktop mode - took that path
and never got it. It belongs on the rule that creates the grid, and is on both.

THE SPACING. The tiles had an 8px grid gap PLUS a `margin-bottom: 0.5rem` each,
so they were 16px apart vertically and 8px horizontally - which is what reads as
"too close to each other" sideways. One value now, 10px, in both directions, with
the margin removed on both the media-query and the mini-screen paths.

The mobile full-screen popup had the same class of bug as the first: `height:
100vh` on a `position: fixed` box (the viewport with the toolbars HIDDEN, so it
reaches under them) and a `.content-wrapper` of `calc(100vh - 48px)`, guessing at
its own title bar. It states dvh now, and the wrapper is left to the flex column
it is already part of.

Desktop is unaffected: above 800px none of these rules apply and the page scrolls
in #content as it always has.

tests/allBoardsPhoneScrollAndTiles.test.cjs pins all four, end to end - including
that no step between the body and the board list computes a height from the
viewport. tests/mobileViewportHeight.test.cjs REQUIRED the calc that was the bug,
so that assertion is replaced with its opposite and says why.

Thanks to mimZD and xet7 !

Fixes #6488,
2026-08-11 02:11:06 +03:00
Lauri Ojansivu 01f691ba3d Build menus: the chart-index report is a command a person runs.
build.sh and build.bat list every releases/*.sh that is not a workflow-only
helper, and backfill-charts.sh is not one of those - it is read by hand, on a
machine with a charts checkout. It is a Release menu entry in both, and
`./build.sh backfill-charts` / `build.bat backfill-charts` on the command line.

Thanks to xet7 !
2026-08-11 02:01:55 +03:00
Lauri Ojansivu 8248892e33 Charts: report the releases the index is missing, and repair it.
The charts job is fixed to RUN (previous commit); this is about what it left
behind. A chart entry is written once, by release-charts.sh, during the release
it belongs to - so a release whose charts job did not run leaves a hole that
nothing ever fills, because the next release adds ITS entry and never looks back.
v10.80 is one of those. Counted against the live index: 216 of the 690 WeKan
releases have a chart entry, and 474 do not.

releases/backfill-charts.sh answers "which WeKan releases should the index
list?" with: the ones that exist AND can be installed.

  keep   216  a chart is already published; never rebuilt, because repackaging
              changes a digest helm clients have already seen
  build  369  the release exists, its container image exists, and no chart is
              published - the hole to fill
  omit   162  the release exists but has NO container image on ghcr. A chart is
              a pointer to an image (tag: v<version>), so an entry for one of
              these is an install that fails at the pull. They stay OUT of the
              index rather than being listed and broken - most are releases whose
              own docker job failed, which is why they have no chart either.

The index is rebuilt with `helm repo index --merge`, from the packages actually
in the directory, so "omitted" needs no bookkeeping: a version with no .tgz gets
no entry, and no entry can point at a package that is not there. A ghcr request
that fails is "unknown", not "no image" - reading a network hiccup as absence
would omit a release that has one.

The run also found something already wrong. The published index has FOUR entries
for 9.36.0 and two for 10.30.0, every copy with a DIFFERENT digest and the SAME
url, because release-charts.sh PREPENDS an entry each time it runs and a re-run
duplicated the version instead of replacing it. At most one of those digests can
be the digest of the wekan-9.36.0.tgz that is really served; a helm client that
picks another fails the integrity check on a perfectly good file. So the rebuild
de-duplicates: for a version with more than one entry, keep the one whose digest
MATCHES the package on disk, and only when there is no package to compare
against fall back to the newest.

Nothing is written without --apply and nothing is pushed without --push, and the
charts job runs it in plan mode only, into the run summary - so the gap is
visible in the release it appears in, while rewriting a published Helm index
stays a command the maintainer runs deliberately.

tests/backfillCharts.test.cjs runs the dedup rule for real against a synthesised
index, including the case that separates the rule from its fallback: the matching
digest on the OLDEST copy, which must still win.

Thanks to xet7 !
2026-08-11 02:00:37 +03:00
Lauri Ojansivu 7b8c377099 Release: the retired Mac runner, the jobs a cancel skipped, and the armv6 userland.
Three failures from the v10.80 run, which reported four snap arches red, one
bundle red, and three jobs that never ran at all.

build-mac-x64 asked for macos-13, and GitHub has RETIRED that image:
actions/runner-images publishes macos-14, macos-15 and macos-26 only, and
macos-13 is gone from the hosted-runner table. A retired label does not fail -
that is the whole problem. The job QUEUES, waiting for a runner that is never
coming. It sat there for nearly two hours and had to be cancelled by hand, and
this has been happening every release: NO WeKan release has ever carried a
wekan-<version>-mac-x64.zip. Intel macOS did not go away, it was renamed, and
.tools/TSC already builds its Intel Mac on the new label: macos-15-intel.

Cancelling it is what skipped charts, ucs and nextcloud. A cancelled job cancels
the RUN, and a cancelling run skips every job that has not started yet - so all
three were skipped in the same second the docker job they wait on succeeded,
after the tag, the release, every bundle and the multi-arch image had shipped.
The Helm chart for a published WeKan was never pushed and nothing said why. A job
whose if is always() still runs while a run is cancelling, so the three now say
always() && <each need>.result == 'success': explicit about the one condition
that ever mattered, instead of relying on a default a cancellation overrides.

The armv6 bundle died on

    qemu-arm: Could not open '/lib/ld-linux-armhf.so.3': No such file or directory

400 lines in, after the whole apt install. DEBIAN HAS NO ARMv6 PORT.
debian:trixie publishes arm/v5 and arm/v7 and nothing between them, and because
containerd treats a lower ARM variant as compatible, --platform linux/arm/v6
quietly resolved to arm/v5: Debian armel, ARMv5 SOFT-float, which contains no
hard-float loader at all. node-armv6 is hard-float, so it could not start.

Two things were wrong. The preflight matched on "architecture": "arm" and ignored
the VARIANT, so "debian:trixie has no linux/arm/v6" was never the answer it gave;
it compares architecture and variant now, and an unrequested variant still
matches anything so linux/arm64 (recorded as arm64/v8) keeps working. And armv6
is built in Debian's arm/v7 container instead, which IS armhf - the same
hard-float ABI and the same loader, so node-armv6 runs in it.

Nothing ARMv7 reaches the bundle. The container's npm install compiles nothing:
it is npm-rebuild over pure-JavaScript packages, with no node-gyp and no CXX in
the log. node-armv6, ferretdb-armv6 and the armv6 MongoDB tools are all
downloaded already built for ARMv6. The single exception is the bundled qemu-user
(#6458), which is copied OUT of the container, and an ARMv7 qemu-armv7l in an
ARMv6 bundle is a binary a Raspberry Pi 1 cannot execute - under a name its
cpu-exec, which looks for qemu-armv6l, would never ask for. So the matrix marks
armv6 same_arch_container: false and install-node-for-arch.sh ships without one,
letting cpu-exec fall back, which is what it is written to do.

Three guards, because each of these was invisible until a release ran:
tests/workflowRunnerLabels.test.cjs checks every runs-on in every workflow
against the labels GitHub currently offers and names the replacement for a
retired one; tests/releaseCancelSafeJobs.test.cjs pins the always() guards and
that they still require every need to have SUCCEEDED;
tests/releaseArmv6Container.test.cjs pins the armv6 container and runs the
preflight's own platform match against the manifest shapes it meets, including
that debian:trixie answers NO to linux/arm/v6.

Thanks to xet7 !
2026-08-11 01:52:41 +03:00
Lauri Ojansivu e1c6221f22 FerretDB: connect with directConnection=true, so a fresh docker compose up starts.
A new install with nothing changed but the port and ROOT_URL could not reach the
database at all:

    MongoServerSelectionError: connect ECONNREFUSED 0.0.0.0:27017
    reason: TopologyDescription { type: 'ReplicaSetNoPrimary',
            servers: Map(1) { '0.0.0.0:27017' => [ServerDescription] },
            setName: 'rs0', ... }

0.0.0.0 is in no compose file. It is FerretDB's own listen address, and the
driver was handed it by the server.

The ferretdb service runs with --repl-set-name=rs0, added in #6480/#6481 so
Meteor can tail an OpLog instead of poll-and-diff. FerretDB therefore answers the
hello handshake as a one-member replica set, and fills hosts, me and primary with
its --listen-addr, which is the wildcard 0.0.0.0:27017. A driver that is not in
direct-connection mode reads that reply as an invitation to do replica-set
DISCOVERY: it adopts the advertised member list and drops the seed it was given,
because the server reports a name other than the one that was dialled. So
mongodb://ferretdb:27017 became 0.0.0.0:27017, which inside the wekan container is
the wekan container itself, where nothing listens.

Measured against FerretDB v1.49.0 - the version reported - with the mongodb
driver the bundle ships:

    mongodb://localhost:27017/wekan
      -> ReplicaSetWithPrimary, servers: 0.0.0.0:27017   (the seed discarded)
    mongodb://localhost:27017/wekan?directConnection=true
      -> Single, servers: localhost:27017

directConnection=true keeps the driver on exactly the host it was given. It costs
nothing else: the handshake still reports setName rs0, which is the only thing
Meteor checks before it will tail an OpLog (accounts for the check in the mongo
package's oplog_replica_set_error.js), so #6480/#6481 are unaffected and the
commented MONGO_OPLOG_URL examples keep replicaSet=rs0 beside it.

All five FerretDB v1 compose files are fixed, being the ones that start FerretDB
with --repl-set-name. The MongoDB compose files are deliberately left alone: those
are real replica sets whose members are reachable under the names they advertise,
and directConnection would pin Meteor to a single node of one.

tests/ferretdbDirectConnection.test.cjs pins both halves - that a compose file
running FerretDB as a replica set carries directConnection=true on every URL
pointing at it, and that the MongoDB ones do not.

Thanks to Dandrass and xet7 !

Fixes #6582,
2026-08-11 01:51:51 +03:00
1553 changed files with 405400 additions and 64298 deletions

No files matched your search

+34 -19
View File
@@ -1,7 +1,9 @@
name: AppImage
# Build WeKan as an AppImage for every CPU an AppImage runtime exists for, and
# attach them to the newest WeKan release.
# attach them to a WeKan release. The ordinary full-release path calls this
# workflow from release-all.yml with the exact tag after its core bundles are
# published; workflow_dispatch remains available for rebuilding an AppImage.
#
# WHAT IS INSIDE IS WHAT WEKAN ALREADY SHIPS. release-all.yml already produces a
# bundle that runs offline with nothing installed: the Meteor server (main.js),
@@ -80,6 +82,7 @@ jobs:
release:
name: find the release
runs-on: ubuntu-24.04
timeout-minutes: 15
outputs:
tag: ${{ steps.find.outputs.tag }}
version: ${{ steps.find.outputs.version }}
@@ -178,7 +181,7 @@ jobs:
run: |
set -euo pipefail
zip="wekan-${{ needs.release.outputs.version }}-${{ matrix.bundle_arch }}.zip"
if gh release view '${{ needs.release.outputs.tag }}' --repo "${{ github.repository }}" \
if gh release view --repo "${GITHUB_REPOSITORY:-wekan/wekan}" '${{ needs.release.outputs.tag }}' --repo "${{ github.repository }}" \
--json assets --jq '.assets[].name' 2>/dev/null | grep -qx "$zip"; then
echo "OK: $zip is on ${{ needs.release.outputs.tag }}; building its AppImage."
else
@@ -212,6 +215,7 @@ jobs:
run: |
set -euo pipefail
version='${{ needs.release.outputs.version }}'
arch='${{ matrix.appimage_arch }}'
rm -rf AppDir
mkdir -p AppDir/usr/lib/wekan AppDir/usr/share/applications \
AppDir/usr/share/icons/hicolor/512x512/apps
@@ -269,8 +273,21 @@ jobs:
echo "bundled FerretDB."
echo
# A missing AppImage may be rebuilt around an ALREADY-PUBLISHED base
# bundle. In that case its start-wekan.sh predates fixes in this
# checkout. Keep the 32-bit V8 address-space ceiling in AppRun too,
# so an i686/armhf AppImage made for an older release can start. An
# administrator's explicit NODE_OPTIONS remains authoritative.
case "@APPIMAGE_ARCH@" in
i686|armhf)
: "${NODE_OPTIONS:=--max-old-space-size=1024}"
export NODE_OPTIONS
;;
esac
exec "$BUNDLE/start-wekan.sh" "$@"
APPRUN
sed -i "s/@APPIMAGE_ARCH@/$arch/" AppDir/AppRun
chmod +x AppDir/AppRun
- name: Pack the AppImage
@@ -313,19 +330,16 @@ jobs:
# is caught here rather than by whoever downloads it.
export WRITABLE_PATH="$PWD/smoke-data" PORT=8080
# A CHECK THAT CANNOT RUN IS NOT A FAILED CHECK. The 32-bit builds are
# packed on a 64-bit runner, which executes them only if the kernel and
# loader for that word size are present - GitHub promises neither. If
# the binary cannot be executed AT ALL, the AppImage is as good as
# anything here knows and the release must not be withheld for it; say
# so and move on. Anything else - it starts and does not answer - is
# the bug this step exists for and still fails.
if ! APPIMAGE_EXTRACT_AND_RUN=1 "$app" --appimage-help >/dev/null 2>&1 \
&& ! APPIMAGE_EXTRACT_AND_RUN=1 "$app" --help >/dev/null 2>&1; then
if ! file "$app" | grep -q "$(uname -m)"; then
echo "::warning::this runner cannot execute a ${{ matrix.appimage_arch }} binary, so the AppImage was packed but not started. It is uploaded unchecked."
exit 0
fi
# A CHECK THAT CANNOT RUN IS NOT A FAILED CHECK. Probe the INNER Node,
# not the AppImage runtime: on the arm64 runner the armhf AppImage
# runtime itself executes, while its bundled hard-float Node cannot
# when /lib/ld-linux-armhf.so.3 is absent. Probing the outer wrapper
# misclassified that missing loader as an application startup bug.
# AppDir is the exact tree just packed, so this also avoids extracting
# the large image merely to make the capability decision.
if ! AppDir/usr/lib/wekan/bundle/node --version >/dev/null 2>&1; then
echo "::warning::this runner cannot execute the bundled ${{ matrix.appimage_arch }} Node binary, so the AppImage was packed but not started. It is uploaded unchecked."
exit 0
fi
APPIMAGE_EXTRACT_AND_RUN=1 "$app" > smoke.log 2>&1 &
@@ -353,9 +367,10 @@ jobs:
if-no-files-found: error
publish:
timeout-minutes: 30
name: attach to the release
needs: [release, build]
if: ${{ always() && inputs.publish != false }}
if: ${{ !cancelled() && inputs.publish != false }}
runs-on: ubuntu-24.04
steps:
- uses: actions/download-artifact@v8
@@ -383,7 +398,7 @@ jobs:
# No AppImage was produced. That is only a FAILURE if there was a base
# bundle to wrap: an AppImage cannot be built without one, and the
# build jobs skip (not fail) when the base bundle is not published yet.
published="$(gh release view '${{ needs.release.outputs.tag }}' --repo "${{ github.repository }}" --json assets --jq '.assets[].name' 2>/dev/null || true)"
published="$(gh release view --repo "${GITHUB_REPOSITORY:-wekan/wekan}" '${{ needs.release.outputs.tag }}' --repo "${{ github.repository }}" --json assets --jq '.assets[].name' 2>/dev/null || true)"
base_present=false
for arch in amd64 arm64 i386 armhf; do
printf '%s\n' "$published" | grep -qx "wekan-${{ needs.release.outputs.version }}-${arch}.zip" && base_present=true
@@ -411,7 +426,7 @@ jobs:
# call in this workflow already passes it; these two did not, so the
# AppImages were built, smoke-tested and uploaded as artifacts, and
# then nothing was attached to the release.
gh release upload '${{ needs.release.outputs.tag }}' assets/* \
gh release upload --repo "${GITHUB_REPOSITORY:-wekan/wekan}" '${{ needs.release.outputs.tag }}' assets/* \
--repo "${{ github.repository }}" --clobber
echo "Uploaded to $(gh release view '${{ needs.release.outputs.tag }}' \
echo "Uploaded to $(gh release view --repo "${GITHUB_REPOSITORY:-wekan/wekan}" '${{ needs.release.outputs.tag }}' \
--repo "${{ github.repository }}" --json url --jq .url)"
+11 -7
View File
@@ -153,8 +153,7 @@ jobs:
if: ${{ env.BUILD_THIS == 'true' }}
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y --no-install-recommends flatpak ostree zip
bash "$GITHUB_WORKSPACE/releases/apt-install.sh" --no-install-recommends flatpak ostree zip
flatpak remote-add --if-not-exists --user flathub \
https://flathub.org/repo/flathub.flatpakrepo
flatpak install --user -y --noninteractive flathub \
@@ -304,9 +303,15 @@ jobs:
publish:
name: attach to the release, and build the repository
needs: [release, build]
if: ${{ always() }}
if: ${{ !cancelled() }}
runs-on: ubuntu-24.04
steps:
# FIRST: this job runs releases/apt-install.sh, and actions/checkout cleans
# the workspace - so it has to happen before the artifacts are downloaded
# into it. Without the checkout the step fails with "No such file or
# directory", which is how the v10.88 run ended.
- uses: actions/checkout@v7
- uses: actions/download-artifact@v8
with:
pattern: flatpak-[0-9a-z]*
@@ -342,10 +347,10 @@ jobs:
# objects/, refs/, summaries/, summary, summary.idx - which is build
# scaffolding, not something to attach to a release. TSC hit this
# exact thing ("upload only the bundle files, not the ostree repo").
gh release upload '${{ needs.release.outputs.tag }}' \
gh release upload --repo "${GITHUB_REPOSITORY:-wekan/wekan}" '${{ needs.release.outputs.tag }}' \
assets/*.flatpak assets/*.flatpak.md5sum assets/*.flatpak.sha256sum \
--repo "${{ github.repository }}" --clobber
echo "Uploaded to $(gh release view '${{ needs.release.outputs.tag }}' \
echo "Uploaded to $(gh release view --repo "${GITHUB_REPOSITORY:-wekan/wekan}" '${{ needs.release.outputs.tag }}' \
--repo "${{ github.repository }}" --json url --jq .url)"
# ---- the repository -------------------------------------------------
@@ -359,8 +364,7 @@ jobs:
if: ${{ inputs.publish_repo != false }}
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y --no-install-recommends flatpak ostree
bash "$GITHUB_WORKSPACE/releases/apt-install.sh" --no-install-recommends flatpak ostree
shopt -s nullglob
parts=(repos/flatpak-repo-*)
if [ ${#parts[@]} -eq 0 ]; then
+9 -4
View File
@@ -10,10 +10,15 @@
# half-hour emulated build, and no way for the variant image to drift from the
# release it claims to be.
#
# `workflow_dispatch` ONLY, on purpose. release-all.yml publishes wekanteam/wekan
# to Docker Hub, Quay and GHCR on every release; the variant images are published
# when the maintainer decides to, which is what this file exists for. Nothing
# here runs on a push, a tag or a schedule.
# A RELEASE NO LONGER NEEDS THIS. release-all.yml's docker job tags both variants
# on all three registries in its own `docker buildx build --push`, so every
# release publishes them with the release's own digests. What this file is for now
# is everything outside a release: publishing a variant for a version whose
# release predates that change, repairing a tag, or pointing a variant at a
# different version deliberately.
#
# `workflow_dispatch` ONLY, on purpose: nothing here runs on a push, a tag or a
# schedule.
#
# If a variant repository ever stops being identical to wekan/wekan, this
# retag becomes wrong: build that repository's own Dockerfile instead.
+2 -3
View File
@@ -50,8 +50,7 @@ jobs:
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y p7zip-full wget curl build-essential g++ make python3 unzip xz-utils git zip
bash "$GITHUB_WORKSPACE/releases/apt-install.sh" p7zip-full wget curl build-essential g++ make python3 unzip xz-utils git zip
- name: Install Sandstorm (dev, unattended)
# build-deps.sh's [bridge] step copies the modern
@@ -104,4 +103,4 @@ jobs:
exit 0
fi
echo "Uploading ${{ steps.zip.outputs.zip }} to the newest release: $TAG"
gh release upload "$TAG" "${{ steps.zip.outputs.zip }}" --clobber
gh release upload --repo "${GITHUB_REPOSITORY:-wekan/wekan}" "$TAG" "${{ steps.zip.outputs.zip }}" --clobber
+131 -15
View File
@@ -10,7 +10,13 @@ name: Release all missing
# those six without running the whole thing again, version bump and all.
#
# This finishes a release that already exists. It never bumps a version, never
# tags, never touches Docker, snap, the website or the charts.
# tags, and never touches Docker, snap or the website.
#
# It DOES publish the Helm chart, because that is a thing a release can be
# missing like any other: the chart entry is written once, during the release it
# belongs to, so a release whose charts job did not run leaves a hole that the
# next release never goes back for - v10.80 is one of those, and it was not
# alone. See the charts job at the end.
#
# WHAT IT CAN BUILD, and why that is not everything. Only build-amd64 runs
# Meteor; every other bundle WeKan ships is that same bundle repacked. This job
@@ -65,6 +71,7 @@ jobs:
extra: ${{ steps.plan.outputs.extra }}
appimage: ${{ steps.plan.outputs.appimage }}
flatpak: ${{ steps.plan.outputs.flatpak }}
windows: ${{ steps.plan.outputs.windows }}
manual: ${{ steps.plan.outputs.manual }}
total: ${{ steps.plan.outputs.total }}
steps:
@@ -79,14 +86,14 @@ jobs:
V='${{ inputs.version }}'
V="${V#v}"
if [ -z "$V" ]; then
V="$(gh release view --json tagName --jq .tagName)"
V="$(gh release view --repo "${GITHUB_REPOSITORY:-wekan/wekan}" --json tagName --jq .tagName)"
V="${V#v}"
echo "No version given; the newest release is v$V."
fi
echo "version=$V" >> "$GITHUB_OUTPUT"
if gh release view "v$V" >/dev/null 2>&1; then
gh release view "v$V" --json assets --jq '.assets[].name' | sort > existing.txt
if gh release view --repo "${GITHUB_REPOSITORY:-wekan/wekan}" "v$V" >/dev/null 2>&1; then
gh release view --repo "${GITHUB_REPOSITORY:-wekan/wekan}" "v$V" --json assets --jq '.assets[].name' | sort > existing.txt
else
echo "::error::There is no release v$V to complete. This workflow FINISHES a release; release-all.yml makes one."
exit 1
@@ -108,7 +115,7 @@ jobs:
total="$(wc -l < missing.txt | tr -d ' ')"
echo "total=$total" >> "$GITHUB_OUTPUT"
for kind in extra appimage flatpak; do
for kind in extra appimage flatpak windows; do
sels="$(awk -v k="$kind" '$1 == k { print $2 }' missing.txt)"
json="$(printf '%s\n' $sels | sed '/^$/d' | jq -R . | jq -sc .)"
[ -n "$json" ] || json='[]'
@@ -188,19 +195,18 @@ jobs:
echo "v${VERSION} already has ${{ matrix.arch }}; nothing to do."
fi
- uses: actions/checkout@v7
- name: Install dependencies
if: ${{ steps.want.outputs.build == 'yes' }}
run: |
sudo apt-get update
sudo apt-get install -y curl unzip zip
bash "$GITHUB_WORKSPACE/releases/apt-install.sh" curl unzip zip
- name: Set up QEMU for ${{ matrix.arch }} emulation
if: ${{ steps.want.outputs.build == 'yes' }}
uses: docker/setup-qemu-action@v4
# Before the download, because actions/checkout cleans the workspace.
- uses: actions/checkout@v7
if: ${{ steps.want.outputs.build == 'yes' }}
# Before the download, because actions/checkout cleans the workspace. if: ${{ steps.want.outputs.build == 'yes' }}
# THE DIFFERENCE from release-all.yml, and the whole point of this
# workflow: the amd64 bundle comes from the RELEASE, not from an artifact
@@ -244,11 +250,11 @@ jobs:
asset="wekan-${VERSION}-${{ matrix.arch }}.zip"
# No --clobber: this architecture was absent, so an upload that would
# overwrite something means the release changed under this run.
gh release upload "v${VERSION}" "$asset" "${asset}.sha256sum"
gh release upload --repo "${GITHUB_REPOSITORY:-wekan/wekan}" "v${VERSION}" "$asset" "${asset}.sha256sum"
# Confirm from the other side. An upload that reports success and
# leaves no asset behind is the failure nobody notices until a
# download 404s.
gh release view "v${VERSION}" --json assets --jq '.assets[].name' > release-assets.txt
gh release view --repo "${GITHUB_REPOSITORY:-wekan/wekan}" "v${VERSION}" --json assets --jq '.assets[].name' > release-assets.txt
grep -qxF "$asset" release-assets.txt || {
echo "::error::$asset is not on release v${VERSION} although the upload reported success."
exit 1
@@ -279,10 +285,120 @@ jobs:
publish: true
only: ${{ needs.plan.outputs.flatpak }}
windows-exe:
name: Windows single EXE
needs: plan
if: ${{ needs.plan.outputs.windows != '[]' }}
uses: ./.github/workflows/windows.yml
permissions:
contents: write
with:
tag: v${{ needs.plan.outputs.version }}
publish: true
# ─── The Helm chart, for a release that never got one ───────────────────────
#
# Everything else here rebuilds a missing BUNDLE; this publishes a missing
# CHART, which goes missing the same way and for the same reason. A chart is
# written once, by release-charts.sh, during the release it belongs to, so a
# release whose charts job did not run has no chart and nothing ever goes back
# for it. That is not rare - 146 of the packages already on gh-pages had no
# index entry, and v10.80 has no package at all, because a cancelled
# build-mac-x64 cancelled its run before the charts job started.
#
# It is the one part of this workflow that PUSHES to another repository, so it
# is guarded twice: it does nothing unless a token that can push to
# wekan/charts is configured, and nothing if the chart for this version is
# already published. Re-publishing an existing version would re-tar the package
# and change the digest of a chart people have already pulled.
charts:
name: Helm chart
needs: plan
if: ${{ !cancelled() && needs.plan.outputs.version != '' }}
runs-on: ubuntu-latest
steps:
- name: Checkout wekan (for releases/release-charts.sh)
if: steps.check.outputs.skip != 'true'
uses: actions/checkout@v7
with:
path: wekan
- name: Is a chart already published for this version?
id: check
env:
V: ${{ needs.plan.outputs.version }}
REPO_TOKEN: ${{ secrets.WEKAN_REPO_TOKEN }}
run: |
set -uo pipefail
if [ -z "${REPO_TOKEN:-}" ]; then
echo "::warning::No WEKAN_REPO_TOKEN, so the Helm chart cannot be published. Add a token that can push to wekan/charts to have this finish the chart too."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
# The published index is the authority on what exists, not this
# checkout. An entry's own version is at EXACTLY four spaces; the
# mongodb subchart's version sits deeper and would match a loose grep.
bash wekan/releases/fetch.sh -o index.yaml https://wekan.github.io/charts/index.yaml || {
echo "::warning::Could not read the published chart index; not touching the charts repo."
echo "skip=true" >> "$GITHUB_OUTPUT"; exit 0; }
# -F, a FIXED string: the dots in a version are regex wildcards
# otherwise, so "10.80" would also match a line reading 10x80. -x so it
# is the whole line, and the four-space indent is what makes it an
# entry's own version rather than the mongodb subchart's, which sits
# deeper.
if grep -qxF " version: ${V}.0" index.yaml; then
echo "::notice::v$V already has a published Helm chart (${V}.0); nothing to do."
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "v$V has no published chart; publishing ${V}.0."
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Checkout wekan/charts (main + gh-pages)
if: steps.check.outputs.skip != 'true'
uses: actions/checkout@v7
with:
repository: wekan/charts
token: ${{ secrets.WEKAN_REPO_TOKEN }}
fetch-depth: 0
path: charts
- name: Install Helm
if: steps.check.outputs.skip != 'true'
uses: azure/setup-helm@v5
- name: Publish the chart for this release
if: steps.check.outputs.skip != 'true'
env:
CHARTS_DIR: ${{ github.workspace }}/charts
run: |
set -euo pipefail
cd charts
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git fetch origin '+refs/heads/*:refs/remotes/origin/*'
git branch --track gh-pages origin/gh-pages 2>/dev/null || true
git checkout main
cd ..
./wekan/releases/release-charts.sh "${{ needs.plan.outputs.version }}"
- name: Job result
if: always()
run: |
case "${{ job.status }}" in
success) mark='OK ' ;;
failure) mark='FAILED ' ;;
cancelled) mark='CANCELLED' ;;
*) mark='${{ job.status }}' ;;
esac
echo "$mark charts" >> "$GITHUB_STEP_SUMMARY"
done:
name: what is still missing
needs: [plan, extra-arches, appimage, flatpak]
if: ${{ always() }}
needs: [plan, extra-arches, appimage, flatpak, charts]
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
@@ -296,7 +412,7 @@ jobs:
echo "::notice::v$V was already complete - nothing was built."
exit 0
fi
gh release view "v$V" --json assets --jq '.assets[].name' | sort > now.txt
gh release view --repo "${GITHUB_REPOSITORY:-wekan/wekan}" "v$V" --json assets --jq '.assets[].name' | sort > now.txt
bash releases/expected-assets.sh "$V" > expected.txt
still=""
while read -r kind selector asset sums; do
File diff suppressed because it is too large. Load diff
+2 -3
View File
@@ -51,8 +51,7 @@ jobs:
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y p7zip-full wget curl build-essential g++ make python3 unzip xz-utils git
bash "$GITHUB_WORKSPACE/releases/apt-install.sh" p7zip-full wget curl build-essential g++ make python3 unzip xz-utils git
- name: Install Sandstorm (dev, unattended)
# Provides the `spk` command used to publish, and the sandstorm-http-bridge that
@@ -110,7 +109,7 @@ jobs:
# 3) Download the newest .spk asset on that release. sandstorm.yml attaches it
# as wekan-sandstorm-YYYY_MM_DD-HH_MM_SS.spk; if several exist, take the
# lexicographically-newest (the timestamp sorts chronologically).
ASSET="$(gh release view "$TAG" --json assets \
ASSET="$(gh release view --repo "${GITHUB_REPOSITORY:-wekan/wekan}" "$TAG" --json assets \
--jq '[.assets[].name | select(endswith(".spk"))] | sort | last' 2>/dev/null || true)"
if [ -z "${ASSET:-}" ] || [ "$ASSET" = "null" ]; then
echo "::error::Release $TAG has no .spk asset. Run sandstorm.yml first to build and attach one."
+2 -3
View File
@@ -54,8 +54,7 @@ jobs:
- name: Install system dependencies and Meteor
run: |
sudo apt-get update
sudo apt-get install -y p7zip-full wget curl build-essential g++ make python3 unzip xz-utils git qemu-user-static
bash "$GITHUB_WORKSPACE/releases/apt-install.sh" p7zip-full wget curl build-essential g++ make python3 unzip xz-utils git qemu-user-static
if [ ! -f "$HOME/.meteor/meteor" ]; then curl https://install.meteor.com/ | sh; fi
echo "$HOME/.meteor" >> "$GITHUB_PATH"
@@ -141,4 +140,4 @@ jobs:
exit 1
fi
echo "Uploading ${{ steps.build.outputs.spk }} to the newest release: $TAG"
gh release upload "$TAG" "${{ steps.build.outputs.spk }}" --clobber
gh release upload --repo "${GITHUB_REPOSITORY:-wekan/wekan}" "$TAG" "${{ steps.build.outputs.spk }}" --clobber
+242
View File
@@ -0,0 +1,242 @@
name: Windows single EXE
# Wrap the already-published self-contained win64 ZIP as one Windows executable.
# Enigma Virtual Box is the FREEWARE download, not Enigma Protector's DEMO. The
# ZIP remains the source of truth: this workflow embeds its exact Node.js,
# FerretDB, MongoDB Database Tools and Meteor server without rebuilding them.
# Persistent data remains outside the virtual filesystem, in a real wekan-files/
# directory beside the EXE, so the EXE and its data can be moved together.
on:
workflow_call:
inputs:
tag:
required: false
type: string
publish:
required: false
default: true
type: boolean
workflow_dispatch:
inputs:
tag:
description: 'Release tag to wrap and attach to (empty = newest release)'
required: false
type: string
publish:
description: 'Attach the single EXE to the release'
required: false
default: true
type: boolean
permissions:
contents: write
concurrency:
group: wekan-windows-exe-${{ inputs.tag || github.ref }}
cancel-in-progress: false
jobs:
release:
name: find the release
runs-on: ubuntu-24.04
timeout-minutes: 15
outputs:
tag: ${{ steps.find.outputs.tag }}
version: ${{ steps.find.outputs.version }}
available: ${{ steps.find.outputs.available }}
steps:
- uses: actions/checkout@v7
- id: find
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
tag='${{ inputs.tag }}'
[ -n "$tag" ] || tag="$(gh release list --repo "${{ github.repository }}" \
--limit 1 --json tagName --jq '.[0].tagName')"
[ -n "$tag" ] || { echo "::error::No release to wrap."; exit 1; }
version="${tag#v}"
available=false
if gh release view "$tag" --repo "${{ github.repository }}" --json assets \
--jq '.assets[].name' | grep -qx "wekan-${version}-win64.zip"; then
available=true
else
echo "::notice::wekan-${version}-win64.zip is not published; there is no Windows bundle to wrap."
fi
echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "available=$available" >> "$GITHUB_OUTPUT"
build:
name: Enigma Virtual Box win64
needs: release
if: ${{ needs.release.outputs.available == 'true' }}
runs-on: windows-latest
timeout-minutes: 90
steps:
- uses: actions/checkout@v7
- name: Fetch and verify the published win64 bundle
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
run: |
$ErrorActionPreference = 'Stop'
$zip = 'wekan-${{ needs.release.outputs.version }}-win64.zip'
$assets = gh release view '${{ needs.release.outputs.tag }}' `
--repo '${{ github.repository }}' --json assets --jq '.assets[].name'
if ($assets -notcontains $zip) {
throw "$zip is not on ${{ needs.release.outputs.tag }}. The single EXE can only wrap a published win64 bundle."
}
gh release download '${{ needs.release.outputs.tag }}' `
--repo '${{ github.repository }}' --pattern $zip --clobber
Expand-Archive -LiteralPath $zip -DestinationPath . -Force
foreach ($file in @('main.js', 'node.exe', 'ferretdb.exe', 'start-wekan.bat')) {
if (-not (Test-Path "bundle/$file" -PathType Leaf)) {
throw "$zip has no bundle/$file; it is not a self-contained WeKan bundle."
}
}
- uses: ilammy/msvc-dev-cmd@v1
- name: Compile the small native launcher
shell: cmd
run: >-
cl /nologo /O2 /MT /W4 /DUNICODE /D_UNICODE
releases\windows-single-exe-launcher.c /Fe:wekan-launcher.exe
- name: Download and verify the archived Enigma Virtual Box 11.30 FREEWARE
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$url = 'https://github.com/wekan/wekan/releases/download/v11.25/enigmavb.exe'
Invoke-WebRequest -Uri $url -OutFile enigmavb-installer.exe
$expected = 'ab743f5e3dd927a288e126bbb053d367f270592e89378c9a06b7f3b15fa1ee35'
$actual = (Get-FileHash enigmavb-installer.exe -Algorithm SHA256).Hash.ToLowerInvariant()
if ($actual -ne $expected) {
throw "Archived Enigma Virtual Box installer SHA256 changed: expected $expected, got $actual. Restore the reviewed v11.25 asset instead of running an unknown executable."
}
$process = Start-Process .\enigmavb-installer.exe -Wait -PassThru `
-ArgumentList '/VERYSILENT','/SUPPRESSMSGBOXES','/NORESTART','/SP-'
if ($process.ExitCode -ne 0) {
throw "Enigma Virtual Box installer exited $($process.ExitCode)."
}
$console = Get-ChildItem @(${env:ProgramFiles}, ${env:ProgramFiles(x86)}) `
-Filter enigmavbconsole.exe -Recurse -ErrorAction SilentlyContinue `
| Select-Object -First 1 -ExpandProperty FullName
if (-not $console) { throw 'The official installer did not install enigmavbconsole.exe.' }
"EVB_CONSOLE=$console" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append
- name: Generate and build the virtual-box project
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
New-Item -ItemType Directory -Force dist | Out-Null
$output = (Resolve-Path dist).Path + '\WeKan-${{ needs.release.outputs.version }}-win64.exe'
npx --yes '@insco/enigma-virtualbox@1.3.4' generate bundle `
--input (Resolve-Path wekan-launcher.exe).Path `
--output $output `
--project-name wekan.evb `
--evbOptions.compressFiles False `
--evbOptions.deleteExtractedOnExit True `
--evbOptions.shareVirtualSystem True `
--evbOptions.mapExecutableWithTemporaryFile True `
--evbOptions.allowRunningOfVirtualExeFiles True `
--evbOptions.processesOfAnyPlatforms True
& $env:EVB_CONSOLE (Resolve-Path wekan.evb).Path
if ($LASTEXITCODE -ne 0) { throw "enigmavbconsole.exe exited $LASTEXITCODE." }
if (-not (Test-Path $output -PathType Leaf)) { throw 'Enigma produced no EXE.' }
if ((Get-Item $output).Length -lt 10MB) { throw 'The EXE is too small to contain WeKan.' }
$hash = (Get-FileHash $output -Algorithm SHA256).Hash.ToLowerInvariant()
"$hash WeKan-${{ needs.release.outputs.version }}-win64.exe" |
Out-File "$output.sha256sum" -Encoding ascii
Get-ChildItem dist | Format-Table Name,Length
- name: Free ports used by the packaged EXE before smoke test
shell: pwsh
run: |
# The GitHub Actions Windows runner ships MongoDB 7 as a running
# service. Its mongod holds 27017, and the pre-installed mongosh /
# MongoDB tools also register port 8088 in some images. Both ports
# are needed by the embedded FerretDB inside the packaged EXE, so we
# must release them before the smoke test or FerretDB cannot bind and
# WeKan never starts.
foreach ($svc in @('MongoDB', 'mongod')) {
$s = Get-Service -Name $svc -ErrorAction SilentlyContinue
if ($s -and $s.Status -ne 'Stopped') {
Write-Host "Stopping service: $svc"
Stop-Service -Name $svc -Force -ErrorAction SilentlyContinue
}
}
# Kill any remaining process that holds the ports we need.
foreach ($port in @(27017, 8088)) {
$pids = (netstat -ano | Select-String ":$port\s") |
ForEach-Object { ($_ -split '\s+')[-1] } |
Where-Object { $_ -match '^\d+$' } |
Sort-Object -Unique
foreach ($pid in $pids) {
Write-Host "Killing PID $pid (held port $port)"
taskkill /PID $pid /F 2>$null | Out-Null
}
}
- name: Check that the single EXE starts
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$env:WRITABLE_PATH = "$pwd\smoke-data"
$env:PORT = '8080'
$env:ROOT_URL = 'http://localhost:8080'
$exe = (Resolve-Path 'dist/WeKan-${{ needs.release.outputs.version }}-win64.exe').Path
$process = Start-Process $exe -PassThru -RedirectStandardOutput smoke.log `
-RedirectStandardError smoke-error.log
$ready = $false
try {
for ($attempt = 0; $attempt -lt 90; $attempt++) {
if ($process.HasExited) { break }
try {
Invoke-WebRequest http://localhost:8080/sign-in -UseBasicParsing -TimeoutSec 3 | Out-Null
$ready = $true
break
} catch {}
Start-Sleep -Seconds 2
}
} finally {
if (-not $process.HasExited) { taskkill /PID $process.Id /T /F | Out-Null }
}
if (-not $ready) {
Get-Content smoke.log -Tail 100 -ErrorAction SilentlyContinue
Get-Content smoke-error.log -Tail 100 -ErrorAction SilentlyContinue
throw 'The packaged EXE did not answer on port 8080.'
}
- uses: actions/upload-artifact@v7
with:
name: windows-single-exe-win64
path: dist/
if-no-files-found: error
publish:
name: attach to the release
needs: [release, build]
if: ${{ !cancelled() && needs.build.result == 'success' && inputs.publish != false }}
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/download-artifact@v8
with:
name: windows-single-exe-win64
path: assets
- name: Upload the EXE and checksum
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
gh release upload '${{ needs.release.outputs.tag }}' assets/* \
--repo '${{ github.repository }}' --clobber
gh release view '${{ needs.release.outputs.tag }}' \
--repo '${{ github.repository }}' --json url --jq .url
+8
View File
@@ -115,3 +115,11 @@ node-patches/
# MongoDB Database Tools that WeKan's bundled bsondump/mongodump/... are built
# from, replacing the wekan/mongo-tools source fork.
mongo-tools-patches/
# Meteor Modern-Tools build context directories
*/build-assets*
*/build-chunks*
# Meteor custom local directory (METEOR_LOCAL_DIR)
*/build-assets-local-test*
*/build-chunks-local-test*
+2 -2
View File
@@ -6,9 +6,9 @@
meteor-base@1.5.2
# Build system
ecmascript@0.19.0
ecmascript@0.19.1-beta352.0
standard-minifier-js@3.2.0
rspack@1.2.0
rspack@1.3.0-beta352.0
# Declared explicitly because server/modernBrowsers.js imports it, to say that
# Yandex Browser is modern and must not be served the legacy bundle (#6557).
modern-browsers@0.2.3
+1 -1
View File
@@ -1 +1 @@
METEOR@3.5.1
METEOR@3.5.2-beta.0
+10 -10
View File
@@ -1,14 +1,14 @@
accounts-base@3.3.0
accounts-base@3.3.1-beta352.0
accounts-express@1.0.0
accounts-oauth@1.4.7
accounts-password@3.3.1
aldeed:collection2@4.2.1
aldeed:collection2@4.2.2
aldeed:schema-index@4.0.0
aldeed:simple-schema@1.13.1
allow-deny@2.1.0
audit-argument-checks@1.0.8
autoupdate@2.0.1
babel-compiler@7.15.0
babel-compiler@7.15.1-beta352.0
babel-runtime@1.5.2
base64@1.0.14
binary-heap@1.0.13
@@ -24,13 +24,13 @@ communitypackages:flow-routing@2.1.0
communitypackages:unstyled@2.0.1
core-runtime@1.0.0
ddp@1.4.2
ddp-client@3.4.0
ddp-client@3.4.1-beta352.0
ddp-common@1.4.5
ddp-rate-limiter@1.3.0
ddp-server@3.4.0
diff-sequence@1.1.3
dynamic-import@0.7.4
ecmascript@0.19.0
ecmascript@0.19.1-beta352.0
ecmascript-runtime@0.8.3
ecmascript-runtime-client@0.13.0
ecmascript-runtime-server@0.11.1
@@ -56,7 +56,7 @@ meteortesting:browser-tests@1.8.0
meteortesting:mocha@3.3.0
meteortesting:mocha-core@8.2.0
minifier-css@2.0.2
minifier-js@3.3.0
minifier-js@3.3.1-beta352.0
minimongo@2.2.0
modern-browsers@0.2.3
modules@0.20.3
@@ -65,7 +65,7 @@ mongo@2.5.0
mongo-decimal@0.2.0
mongo-dev-server@1.1.1
mongo-id@1.0.9
npm-mongo@6.16.2
npm-mongo@6.16.3-beta352.0
oauth@3.0.4
oauth2@1.3.3
observe-sequence@2.0.1
@@ -87,7 +87,7 @@ reload@1.3.2
retry@1.1.1
reywood:publish-composite@1.9.0
routepolicy@1.1.2
rspack@1.2.0
rspack@1.3.0-beta352.0
service-configuration@1.3.5
session@1.2.3
sha@1.0.10
@@ -100,9 +100,9 @@ templating@1.4.5
templating-compiler@2.0.1
templating-runtime@2.0.2
templating-tools@2.0.1
tools-core@1.2.0
tools-core@1.3.0-beta352.0
tracker@1.3.4
typescript@5.11.0
typescript@5.11.1-beta352.0
url@1.3.6
webapp@2.3.0
webapp-hashing@1.1.2
+342 -52
View File
@@ -85,7 +85,7 @@ When asked to fix an open issue (one issue at a time):
- npm — https://www.npmjs.com
4. **After fixing, add tests** — unit tests, negative tests, and UI tests where
appropriate — and **run the new tests to verify they pass** (see Tests / the sandbox
build+test instructions in `docs/Security/Sandboxes/vscode/README.md`).
build+test instructions in `docs/Security/Sandboxes/vscodium/README.md`).
5. **Commit** (maintainer only — when the current git user is `Lauri Ojansivu
<x@xet7.org>`), with no "Co-Authored" / AI trailer, a message body ending:
@@ -105,23 +105,33 @@ from source and say clearly what was and was not verified.
WeKan translations live in `imports/i18n/data/<lang>.i18n.json` (flat
`key -> string`, 2-space indent, key order matches `en.i18n.json`). Transifex holds
the human translations. **The policy is: never overwrite a human translation with a
filled (or English) one, but always take the newest translations from Transifex.**
the human translations. **The policy is: never overwrite a human translation in the
correct language with a filled (or English) one, but always take the newest correct-
language translations from Transifex. Text in another language is not protected: the
locale tag is authoritative, and wrong-language or mixed-language values must be
replaced with the language named by that tag.**
- Pull with `releases/translations/pull-translations.sh`. It runs `tx pull -a -f`
(which fills every string that is UNtranslated on Transifex with the English source)
and then a **per-key merge** (`releases/translations/merge-translations.mjs`) that,
for every language file and every key:
- Transifex has a real translation (pulled value differs from English) → **keep it**
(the newest human translation always wins);
- the pull reverted it to English but a human translation is committed in git →
**restore the committed translation** (a pull never reverts a human translation,
even in files that also received real new Transifex translations);
- Transifex has a real translation in the locale's declared language (pulled value
differs from English) → **keep it** (the newest correct-language human translation
always wins);
- the pull returned English but the pre-pull local file has a translation →
**restore the local translation as the fallback**. It may be human or a direct
machine/LLM fill; either way it remains local, while a real correct-language
Transifex translation takes precedence;
- **no translation anywhere** (untranslated on Transifex AND never committed) → leave
the English source as a placeholder. **This is the only case a non-human value is
used.** A separate fill step may fill *only* these English placeholders, so a filled
string can never overwrite a human translation.
- Restored languages are pushed back to Transifex so they stop reverting.
- After the merge, audit for mixed or wrong-language values. A value that differs from
English can still be wrong for its locale; replace it directly as described below.
- **The pull workflow never pushes translations to Transifex.** It cannot distinguish a
restored human translation from a committed direct fill, so automatic push-back would
misrepresent machine/LLM translations as human. Push only separately reviewed,
provenance-known human translations with an explicit push command.
### Filling the remaining untranslated strings — directly, no translation service
@@ -133,6 +143,59 @@ translations** and general **kanban terminology** for the language as the refere
earlier `machine-translate.mjs` that called LibreTranslate/DeepL is removed on purpose
(it did not work and needed a password). There is **no `WEKAN_MT*` env var** anymore.
- **EVERY language gets translated — including the ones nobody has volunteered for.**
Klingon, Volapük, Wolof, Venda, Tamazight, Acehnese and the rest are not exceptions:
WeKan cannot find a speaker for every language, and a file left in English stays in
English for years. **Look the words up.** Any dictionary, word list, grammar or
Wiktionary page on the Internet is a legitimate source — read it, take the terms it
gives, and write the translation from them.
- **A locale file must contain its declared language, not whichever language happened
to seed it.** If a Mongolian file contains Russian, replace the Russian with
Mongolian; likewise replace French, German, Malay, Zulu or any other wrong-language
text with the language named by the file's locale tag. This rule applies to isolated
mixed-language values and to an entire wrongly seeded file. Wrong-language text is
not a human translation for that locale and does not receive human-preference
protection. Because `fill-translations.mjs --apply` deliberately writes only English
placeholders, make these corrections directly in the locale JSON (preserving key
order and formatting), add regression coverage for the correct language/script, and
note low-confidence replacements in the commit. Script checks alone are insufficient
when both languages share a script, such as Russian and Mongolian; inspect vocabulary
as well.
- **Placeholders are code, not language. Never translate them.** Any source token that
begins and ends with underscores (for example `__board__`, `__card__` or
`__username__`) and any format token whose token begins with `%` (for example `%s`,
`%d` or `%1$s`) must be copied from the corresponding value in `en.i18n.json`
exactly, character for character. Preserve every placeholder, its spelling, case,
count and format; translate only the prose around it. If a locale has translated,
renamed, removed or malformed one of these tokens, restore the original token from
the same key in `en.i18n.json`, even when the surrounding translation is human.
Regression coverage for a translation batch must compare its underscore-delimited
and percent-prefixed token inventory with English so a translated placeholder cannot
pass as ordinary prose.
- **Add the languages WeKan does not have yet.** The 154 files under
`imports/i18n/data/` are not the list of languages worth supporting - they are the
list somebody happened to start. When a language has a dictionary, a word list or a
Wiktionary anywhere on the Internet, it can have a WeKan. Adding one is three edits,
and all three are needed or the language is invisible:
1. `imports/i18n/data/<tag>.i18n.json` — the strings, in `en.i18n.json`'s key ORDER
(`tests/boardItemLinks.test.cjs` checks that), 2-space indent.
2. `imports/i18n/languages.js` — `{ code, tag, name, load, rtl }`. The **name is
written in that language itself** ("Suomi", "tlhIngan Hol"), the way every entry
there already is, because it is read by somebody who does not read English. `rtl:
true` for Arabic, Hebrew, Persian, Uyghur and the rest that are written
right-to-left.
3. `client/components/users/userHeader.js` — the `flagMap` in `languageFlag()`, so the
picker shows a **flag** beside the name. A language without a country of its own
takes the flag of where it is spoken, and a constructed one (Esperanto, Klingon,
Volapük) keeps the `🌐` fallback rather than being given somebody's country.
- **A bad translation beats no translation.** An imperfect string is readable, it is
obviously improvable, and it is an *invitation*: somebody who speaks the language sees
it is wrong and fixes it, which an English placeholder never provokes. So: do not skip
a language because the result would be imperfect. Translate it, say in the commit which
ones were done with low confidence, and let a human correct it — the merge rules below
guarantee that when that human translation arrives on Transifex, it REPLACES the filled
one and is never overwritten by it.
- `node releases/translations/fill-translations.mjs --missing` — per-language count of
strings still needing translation (English + `en-*` variants are skipped: English by
design). Also printed at the end of `pull-translations.sh`.
@@ -145,14 +208,14 @@ earlier `machine-translate.mjs` that called LibreTranslate/DeepL is removed on p
**Both directions are safe, and it is verified:**
`node releases/translations/verify-human-preference.mjs` proves (pure-logic, no network)
that the pull-merge keeps/restores human translations and that a fill only touches
placeholders. Filled strings stay **local** — they are **NOT** pushed to Transifex (only
the merge-restored human languages are pushed), so a filled string can never masquerade
as human there. So: **only missing strings are ever filled, only when missing
everywhere, human strings are always preferred and merged**, and nothing you fill is
pushed to Transifex as if it were human.
that a real Transifex translation wins, the pre-pull local translation fills only an
English result, a fill only touches placeholders, and the pull script contains no push.
Thus **correct-language Transifex human strings are preferred and merged**, remaining
keys retain their local human or machine/LLM translations, and nothing restored or
filled is pushed to Transifex as if it were human. Wrong-language values are corrected
directly after the merge.
## General practices (from ../log/v10/Claude.txt)
## General practices (from .tools/log/v10/Claude.txt)
- **[maintainer only]** Commit as `Lauri Ojansivu <x@xet7.org>`, with **no**
"Co-Authored-By" or any other AI trailer, directly to the `main` branch of WeKan and
@@ -165,7 +228,7 @@ pushed to Transifex as if it were human.
- Directory structure:
- `wekan` — this repo (https://github.com/wekan/wekan); see
`docs/DeveloperDocs/Directory-Structure.md`; `CHANGELOG.md` at root.
- `../w/wekan.fi` — the WeKan website.
- `.tools/wekan.fi` — the WeKan website companion repository.
- `.tools/` — everything that is NOT part of this repository but is needed to
build, test and release it, in ONE directory that `.gitignore` and
`.meteorignore` already exclude (each used to need its own ignore entry at
@@ -186,7 +249,7 @@ pushed to Transifex as if it were human.
| `.tools/sandstorm` | sandstorm-io/sandstorm | — | upstream Sandstorm, when present — somebody else's project, cloned for reference |
**Unpacked toolchains and caches.** Downloads, not repositories — put there by
the sandbox instructions in `docs/Security/Sandboxes/vscode/README.md`, deleted
the sandbox instructions in `docs/Security/Sandboxes/vscodium/README.md`, deleted
and re-fetched freely, never committed anywhere:
`node-v<version>-linux-<arch>/` (the Node.js the test suites are run with),
`go/` with `gopath/`, `gomodcache/` and `gocache/` (FerretDB's Go builds),
@@ -228,11 +291,45 @@ pushed to Transifex as if it were human.
the newest release (FerretDB uses `## Upcoming FerretDB release`; the patch repos use
`# Upcoming <repo> release`). Do **not** hand-edit
`package.json` or any other version reference — the release workflow bumps those.
- **Whenever `# Upcoming WeKan ® release` is updated, audit every feature, bug fix
and security fix in that section for regression coverage.** If coverage does not
exist yet, add a positive test, a negative test and a UI test wherever that kind
of test is possible and relevant. Run the tests that can run in the current
environment; when a UI stack or another required service is unavailable, still
add and syntax-check/register the test and state clearly what could not be run.
Do not duplicate an existing test merely to satisfy the audit — extend it when
that keeps one behavior in one suite.
- **CHANGELOG.md holds the CURRENT MONTH only.** It reached 2.6 MB and 51,365
lines over 1,100 releases back to 2015 (#6580), which is slow to open and
slower to read. Moving whole years out left 1.9 MB, still too large, because
releases here are frequent: 2026 alone is 272 releases over eight months and
July was 80 on its own. So:
| | |
| --- | --- |
| `CHANGELOG.md` | the current month, plus `# Platforms`, `# TODO Later`, `# Upcoming` |
| `old-CHANGELOG/<year>/<MM>.md` | earlier months of the current year |
| `old-CHANGELOG/<year>.md` | years that are over, whole |
Past years stay one file each because they are already small (30–107 KB);
splitting them further would trade a size problem nobody has for a hundred
more files. Each archive opens with a **release count** — per month in a year
file, per day in a month file — and a bullet in `# Platforms` links every one.
That `git blame` is less useful on the split file is accepted: the history is
still in git (`gitk`, `git-gui`, `git log --follow`), and being small enough
to open is worth more.
Run `node releases/changelog-archive.mjs` at the start of a month. It is
idempotent — a run with nothing to move only refreshes the tables — and it
takes the month to keep from the FILE rather than the clock, so two people
running it on the same day agree. An archived section is never edited, for the
same reason a released one is not.
- **The file's shape, top to bottom** — keep it exactly as it is now:
1. `# Platforms` — the line `Newest WeKan at these platforms:` and the Install /
Upgrade / Docs / Mac ChangeLog bullets, then a `<details>` whose `<summary>` is
`Version` holding "which WeKan version uses what". There is no `# Version`
heading of its own.
Upgrade / Docs / Mac ChangeLog bullets, the `Older releases:` bullet linking
the per-year archives, then a `<details>` whose `<summary>` is `Version`
holding "which WeKan version uses what". There is no `# Version` heading of
its own.
2. `# TODO Later` — a `<details>` whose `<summary>` is `Carried to a future
release.` explaining the list, then one `<details>` per category (below).
3. The releases, newest first, each `# v<MAJOR>.<MINOR> YYYY-MM-DD WeKan ® release`.
@@ -295,16 +392,17 @@ pushed to Transifex as if it were human.
subsection just keeps its single `This release …:` header. Use `and adds the following
new features:`, `and fixes the following bugs:`, `and updates the following
dependencies:`, etc., matching the verb to the subsection.
- **The Upcoming section opens with an `**In short:**` paragraph.** One paragraph,
right under the `# Upcoming WeKan ® release` line and above the first
`This release …:` header, saying what the whole release amounts to — the areas
that changed and what changed about them, with the notable names in `**bold**`
so it can be skimmed. It is a SUMMARY, not a list: it does not link commits, it
does not repeat an entry's wording, and it ends by accounting for the rest in a
clause (`Below that: dependency updates, nine bug fixes … and the usual
documentation and translation work.`). Keep it current as entries are added —
it is the first thing a reader sees, so a stale one misdescribes the release.
A finished release keeps the paragraph it was written with.
- **The Upcoming section opens with an `**In short:**` paragraph.** It must be
short: put one compact, high-level paragraph directly under the
`# Upcoming WeKan ® release` line. Aim for 2–4 sentences and no more than about
120 words, however many commits the release contains. Name only the release's
major outcomes or themes, with notable names in `**bold**` so it can be skimmed.
It is not a commit inventory, progress log or miniature changelog: do not list
batches, per-language counts, individual test fixes or every touched component,
do not link commits, and do not repeat entry wording. The topic groups and their
`<details>` blocks below carry that information. Keep the paragraph current as
topics change, and shorten it when added commits make it grow. A finished release
keeps the paragraph it was written with.
- **Under the summary comes the BINARIES TABLE: what each platform ships.** So the
top of a release section is, in order, (1) the `**In short:**` paragraph and
(2) this table, and only then the `This release …:` subsections. A WeKan bundle
@@ -339,7 +437,7 @@ pushed to Transifex as if it were human.
has no rows**, which is how the table also answers "why is there no i386 bundle
this time": no source published a Node.js for it (see
`releases/resolve-node-source.sh`).
- **Inside a subsection, entries are GROUPED BY AREA.** A release touches a handful
- **Inside a subsection, entries are GROUPED BY TOPIC/AREA.** A release touches a handful
of areas and repeating the area's name in every summary is the noise this
removes — twelve entries that each begin "All Boards:" say "All Boards" twelve
times and the part that differs starts halfway through the line. Instead the area
@@ -367,15 +465,24 @@ pushed to Transifex as if it were human.
…
```
The group line is `**Area** - short description.` on ONE line, wrapped at 80 like
everything else, with a blank line under it. It is NOT a heading: a `##` inside a
release breaks the version list (see above). Group labels are the areas of the
The group line is `**Topic** - short summary.` on ONE line where practical,
wrapped at 80 like everything else, with a blank line under it. This line is the
high-level summary of ALL commits in that topic: say what changed and why it
matters without copying a commit summary or enumerating implementation details.
The `<summary>` under it gives each commit's short outcome, and the expanded
`<details>` body carries the implementation, rationale and test evidence. In
other words, the release reads from release summary → topic summary → commit
detail, with each level adding information instead of repeating the level above.
The group line is NOT a heading: a `##` inside a release breaks the version list
(see above). Group labels are the areas of the
app — `All Boards`, `The first header bar`, `The left menus`, `The Admin Panel`,
`Board views`, `Public Boards`, `Member Settings`, `Board roles` — and the same
label is reused across subsections when an area has both a feature and a fix.
EVERY entry of a grouped subsection belongs to a group, including a group with
one entry: a section that is half grouped and half loose reads as a mistake. A
subsection with only one entry, and the dependency bullets, stay flat.
one entry: a section that is half grouped and half loose reads as a mistake. Put
each commit under the topic it actually changes; do not create a chronological
catch-all group. A subsection with only one entry, and the dependency bullets,
stay flat.
- **CRITICAL security header — match the previous releases' wording.** A security release
leads with `This release fixes the following CRITICAL SECURITY ISSUE of
[Name](https://wekan.fi/hall-of-fame/namebleed/):` for a single named *Bleed, or
@@ -422,17 +529,84 @@ Fixes #1234,
Fixes #1235.
```
- **A commit that fixes a GitHub issue ENDS with `Fixes #NNNN`** — last lines of
the message, one per issue, comma after each and a full stop on the last. That
trailer is what closes the issue when the commit is pushed; a fix that only
*mentions* the number in prose leaves the issue open, and somebody has to close
it by hand later or it stays open forever. Several issues in one commit get
several lines. The same applies to an issue that turns out to be **already
fixed**: commit the test or the note that proves it and end with `Fixes #NNNN,`
so the issue closes with a reference to where it was fixed.
- **Only when the commit really fixes it.** A commit that improves the
diagnostics, narrows the cause, or fixes one of several reported problems
references the issue in the body (`#6585 comment 5276581923`) and does NOT
carry the trailer — closing an issue whose reporter is still stuck is worse
than leaving it open. If an issue was closed and then REOPENED for follow-up
items (as #6586 was), it is fixed again only when those items are done.
- **A commit with no issue behind it has no trailer** — the email reports, the
release-tooling fixes found in build logs, refactors. `Thanks to ... and xet7 !`
still names whoever reported it.
### Making a release — no version number needed **[maintainer only]**
All publishing / release steps below are maintainer-only. Contributors never run them.
**Releases are FREQUENT, and that is the normal state of this repository — not an
interruption to it.** The maintenance loop is:
1. `./build.sh` (or `build.bat` on Windows) → **option 1, "git pull and git
push"**. Both directions do the whole job: a pull rebases and then repairs the
CHANGELOG commit links the rebase made stale, and a push repairs them again
BEFORE publishing, because a stale link that reaches GitHub 404s for everyone
who reads the release notes.
2. `./releases/release-all.sh`, **with no arguments**, whenever there is something
worth shipping — which is often, several times a day when a fault is being
chased. It takes the version from the CHANGELOG and needs nothing typed.
So a release happening "in the middle" of a piece of work is not a special case
to reason about; it is what always happens, and anything that only works when
releases are rare is broken here. Two consequences worth stating, because both
have cost a released section its accuracy:
- **Work continues immediately after a release**, so `release-all.sh` renames
`# Upcoming WeKan ® release` to `# v<NEW> …` and then OPENS A NEW EMPTY
`# Upcoming` (`releases/changelog-open-next.mjs`), so the next entry has
somewhere correct to go. Without it an entry appended above the closing
`Thanks to above GitHub users …` line lands INSIDE the release just published.
The new section carries an `**In short:** nothing here yet.` placeholder and
the binaries table, so the file stays valid; replace the placeholder as entries
are added. `tests/changelogEntriesBelongToTheirRelease.test.cjs` checks the
newest few releases against git and fails when a section links a commit that
release does not contain.
- **A released section is a RECORD, not a draft.** When a release turns out to be
broken, its section keeps saying what it shipped — including the part that was
wrong — and the fix goes in a new `# Upcoming` above it. Do NOT edit a
published entry to describe the smaller, tidier change you wish had shipped;
add to it that it was wrong and where the fix is. Somebody reading v10.97's
notes is most likely somebody whose v10.97 just died.
- WeKan: run `./releases/release-all.sh` (no arguments). It renames
`# Upcoming WeKan ® release` to the next version (same increment as the last release;
9.99 → 10.00) dated today, commits + pushes, and triggers
`.github/workflows/release-all.yml` (its `bump` job bumps `package.json` and every
version reference, then the release jobs tag `v<new>` and publish the GitHub Release).
An explicit `oldversion newversion` pair still overrides. Adding entries under Upcoming
is the only hand step.
`.github/workflows/release-all.yml` — whose `bump` job bumps `package.json` and every
version reference and rebuilds the API docs, whose `prepare` job then pushes the tag
`v<new>` and checks the notes exist, and whose `release` job publishes the GitHub
Release the bundle jobs attach to. An explicit `oldversion newversion` pair still
overrides. Adding entries under Upcoming is the only hand step.
- **A release that FAILED and one that shipped BROKEN are handled differently, and
the difference is whether anything was PUBLISHED.** Both happened with v10.92, so
neither is hypothetical:
- **Nothing published** (the workflow died before the GitHub Release existed): there
is no release for that number, so `# v<new> …` must not claim there is one. Rename
the heading back to `# Upcoming WeKan ® release`, add the fix under it, and run
`release-all.sh` again — it will take the same number, since the newest release is
still the previous one.
- **It published, and something in it is broken**: the release exists and people can
download it, so its section STAYS as it is — a released section is a record, not a
draft. Add a NEW `# Upcoming WeKan ® release` above it with the fix, exactly as
during ordinary development, and the next run takes the next number.
What decides it is the GitHub Release, not the tag: `prepare` pushes the tag early,
so a tag can exist for a release that never published.
- FerretDB: run `./build.sh release-ferretdb` from `.tools/FerretDB` (no
version). It renames `## Upcoming FerretDB release` to the next version with the
correct git-tag link, commits + tags + pushes, then triggers `release-all.yml` (which
@@ -442,8 +616,85 @@ All publishing / release steps below are maintainer-only. Contributors never run
### Security issues
- Fix the vulnerability, add a CRITICAL section to the WeKan CHANGELOG like previous
entries, and update `../w/wekan.fi/hall-of-fame/index.html` and the vuln-name
entries, and update `.tools/wekan.fi/hall-of-fame/index.html` and the vuln-name
subdirectory `index.html` like previous security issues.
- **Every security fix gets a TEST and a NEGATIVE TEST, and they are written so the
fault cannot exist ANYWHERE in the codebase — not just at the place it was
reported.** A test that pins one call site leaves the same mistake free to live in
the other five, and that is how most of these arrive: SignupBleed's guard read an
option nothing sets, and the same shape sat in a second endpoint; the source-map
trim was safe on the client and fatal on the server. So:
- the **test** proves the fix does what it claims, driving the decision itself
where that is possible. A pure module — `loginFailureDecision.js`,
`lockoutDecision.js` — can be tested as arithmetic, without a server or a
database, and reproduces the reporter's attack exactly rather than approximately;
- the **negative test** proves the fault is gone rather than moved. Search the
whole tree for the SHAPE of it and assert nothing matches: no other endpoint
reads the dead option, no other counter is global, no other caller skips the
check. When the shape is a pattern, pin the pattern.
- and a test that reads the source is a real test here. `tests/*.test.cjs` may
parse a file and fail on a construct — that is what makes "and nowhere else"
checkable at all.
- **If somebody ATTEMPTS the attack, and the secure default DENIES it, that denial
has to be visible in Admin Panel → Problems** — in every case where the denial can
be attributed. This applies to every security fix added to Upcoming. That is the
difference between a hole that is closed and a hole that is closed and watched:
an administrator should be able to see that somebody tried.
- Add a key to the catalog in `models/lib/securityCategories.js` (category, the
hall-of-fame `bleed` name, severity, CWE), and call
`require('/server/lib/securityLog').record({ key, action: 'blocked', source, detail })`
on the refusal path. `action` is `'blocked'` when the fix stopped it and
`'detected'` when it was only noticed.
- **Wrap the call so logging can never break the guard**:
`try { ... } catch (e) { /* logging must never break the guard */ }`. The refusal
matters more than the record of it.
- **Only log an ATTEMPT, never ordinary use.** The test to apply is whether a
legitimate user can reach that line. Registration refused while registration is
off has no legitimate caller, so it is logged; an admin endpoint whose fault was
in what its answer CARRIED fires on every normal call, so it is NOT — that is why
HashBleed (GHSA-6qpx-x7vr-p9w6) deliberately has no key. A log that fills with
normal traffic hides the one line that mattered.
- Where the denial cannot be attributed to an attempt — a fix that changes what a
response contains, or one that only takes effect at build time — there is nothing
to record, and that is a decision to state in the entry rather than an omission.
- **Admin Panel → Problems is a SUMMARY, never a row per event.** A guard on a path
an attacker controls fires as fast as they can send, so a document per occurrence
grows the database with the attack, turns the page into a scroll of near-identical
lines, and buries the one event that mattered under ten thousand that did not. The
admin's question is never *list every attempt* — it is *what is happening, how
much, since when, and who*. So each problem is ONE row that accumulates
(`models/lib/eventLogSummary.js`, written through `server/lib/eventLogFold.js`,
shared by the security, speed and test loggers):
| field | what it holds |
| --- | --- |
| `count` | how many times this problem has happened |
| `firstAt` … `at` | the window it happened in |
| `actors` | who, each with their own count — `username1 25, 100.100.100.100 30` |
| `username` / `ip` / `detail` | the MOST RECENT occurrence |
- **Identity is the KIND of thing that happened** — stream, `bleed`, category,
action, source, severity, CWE (and `type`/`db`/`kind` for the database stream).
The actor is NOT part of it: putting a username or an address in the key gives a
row per attacker per attempt, which is the cost being removed.
- **A username and an address are tallied SEPARATELY**, not as a pair. They answer
different questions — which account, and where from — and an unauthenticated
attempt has an address and no name.
- **The tally is CAPPED** (`MAX_ACTORS`), with the remainder counted in
`actorsOverflow`. Otherwise an attacker rotating addresses grows the row with the
attack and reintroduces the same bug one level down — and *"and 9,412 others"* is
itself the signal that the source is spread rather than single.
- **A logger that writes per event is a bug to fix, not a style.** If one is added
or found, route it through the shared fold and migrate what it already wrote
(`server/lib/eventLogSummaryMigration.js` folds legacy rows in place, in batches,
idempotently).
- **Every hall-of-fame vulnerability that CAN be detected at runtime should have a
catalog key**, so Admin Panel → Problems groups attempts under the same name the
Hall of Fame uses and an admin can go from one to the other.
`tests/hallOfFameProblemsCoverage.test.cjs` lists the names that have no key and
requires each to be accounted for — a *Bleed with nothing to detect (a fix that
changed what a response carried, or one that only applies at build time) is
recorded there as a deliberate omission with its reason, not left silent.
- **A Hall of Fame row has EIGHT cells**, in this order: CVE, Icon, Vulnerability
name, Date, Responsible Security Disclosure by, Stars, Process, Vulnerabilities.
One thing per cell — the name without its icons, the Font Awesome icons alone in
@@ -498,19 +749,17 @@ All publishing / release steps below are maintainer-only. Contributors never run
- Add tests, negative tests and UI tests for all new features and fixes that do not yet
have tests. When adding a test, run or validate it and fix it until it works.
- **"Check newest test logs":** test logs go into a `log/<datetime>/` directory,
one per run (e.g. `2026-07-21_20-58-09/`). That is **`../log/`**, one level up
from the repo, so a run does not show up in `git status` - **unless the parent
is not writable**, as in a Flatpak sandbox that shares only the repository;
there it is **`./log/`** inside the repo (gitignored). `build.sh` prints which
one when a run starts. The
one per run (e.g. `2026-07-21_20-58-09/`). That is **`.tools/log/`** inside the
repository's ignored tool area, so a run does not show up in `git status` and
does not depend on the parent directory being writable. The
newest datetime directory is the latest run. Each holds the Playwright per-browser
logs (`wekan-alltests-chromium.log`, `-firefox.log`, `-webkit.log`), the mocha/unit
log (`wekan-alltests-mocha.log`), the e2e/import logs, and `wekan-test-server.log`
(the WeKan test server + database output). A whole-run directory also holds the
database-conformance logs (`db-conformance-*.log`, `-report.md`, `-summary.txt`)
and FerretDB's own (`ferretdb-unit.log`, `-vet.log`, `-integration.log`). "Check
the newest test logs" means: open the most recent `log/<datetime>/` - look in
`../log/` first, then `./log/` - and read those.
the newest test logs" means: open the most recent
`.tools/log/<datetime>/` and read those.
- **Check and fix WHILE the tests are still running.** A full run takes a long time
(three browsers, then every database with an image for this CPU, then FerretDB's
own suites), and its stages finish one at a time. Do not wait for the end: read
@@ -533,12 +782,53 @@ All publishing / release steps below are maintainer-only. Contributors never run
## Environment
- The editor (VSCode) runs inside a **Flatpak sandbox**, launched by
`docs/Security/Sandboxes/vscode/vscode-sandbox.sh`. What the sandbox allows/blocks and
how it is set up is documented in `docs/Security/Sandboxes/vscode/README.md` in that
- The editor (VSCodium) runs inside a **Flatpak sandbox**, launched by
`docs/Security/Sandboxes/vscodium/vscodium-sandbox.sh`. What the sandbox allows/blocks and
how it is set up is documented in `docs/Security/Sandboxes/vscodium/README.md` in that
same directory — read it when something behaves differently than a normal host (file
access, network, running services).
### Flatpak sandbox: install task tools under `.tools/`
- In the Flatpak sandbox launched by
`docs/Security/Sandboxes/vscodium/vscodium-sandbox.sh`,
the repository may be the only writable/shared host directory. When a command
needs a tool that is missing, install its binary, virtual environment, models
and caches under the repository-local `.tools/` directory. Do not install it
into the read-only Flatpak image or scatter generated tool files through the
source tree. `.tools/` is excluded by both `.gitignore` and `.meteorignore`, so
local toolchains neither enter commits nor consume Meteor file-watcher slots.
- Use the version selected by the repository, resolved from the current files at
install time; never copy a version number from an old log or hard-code the
example currently shown in the sandbox README. In particular: Node.js and npm
come from `NODE_VERSION` and `NPM_VERSION` in `Dockerfile`; Meteor comes from
`.meteor/release`; companion repositories use the versions in their own build
scripts or module files. Use the architecture reported by `uname -m`. Reuse an
existing matching `.tools` installation before downloading another copy.
- Follow the complete, tested bootstrap commands and environment variables in
`docs/Security/Sandboxes/vscodium/README.md`, especially its "Reuse same
in-sandbox toolchain" and test-runtime sections. Prefer repository setup
helpers such as
`build.sh` and `releases/ensure-tools.sh` when they already install the needed
pinned tool. Keep `HOME`, `PATH`, `GOROOT`, `GOPATH`, `GOCACHE`, `GOMODCACHE`
and similar overrides scoped to the command or sandbox terminal; do not change
the real host home or a system installation.
- For screenshot or image text that cannot be read by the normal image viewer
because Flatpak/bubblewrap cannot create a user namespace, install RapidOCR
locally instead of guessing from filenames or logs:
```bash
python3 -m venv .tools/ocr-venv
.tools/ocr-venv/bin/pip install rapidocr_onnxruntime pillow
.tools/ocr-venv/bin/python -c 'from rapidocr_onnxruntime import RapidOCR; import sys; result, _ = RapidOCR()(sys.argv[1]); print("\n".join(row[1] for row in (result or [])))' path/to/screenshot.png
```
Process a timestamped screenshot series in chronological order and correlate
OCR output with the matching `.tools/log/<datetime>/` files. OCR is evidence
with recognition errors: retain timestamps and coordinates when needed, verify
suspicious numbers against adjacent frames, and do not claim visual details
that neither OCR nor another available viewer confirmed.
### Always validate from the actual code
- When doing anything, check how it actually works in the code first.
+27510 -37814
View File
File diff suppressed because it is too large. Load diff
+342 -52
View File
@@ -85,7 +85,7 @@ When asked to fix an open issue (one issue at a time):
- npm — https://www.npmjs.com
4. **After fixing, add tests** — unit tests, negative tests, and UI tests where
appropriate — and **run the new tests to verify they pass** (see Tests / the sandbox
build+test instructions in `docs/Security/Sandboxes/vscode/README.md`).
build+test instructions in `docs/Security/Sandboxes/vscodium/README.md`).
5. **Commit** (maintainer only — when the current git user is `Lauri Ojansivu
<x@xet7.org>`), with no "Co-Authored" / AI trailer, a message body ending:
@@ -105,23 +105,33 @@ from source and say clearly what was and was not verified.
WeKan translations live in `imports/i18n/data/<lang>.i18n.json` (flat
`key -> string`, 2-space indent, key order matches `en.i18n.json`). Transifex holds
the human translations. **The policy is: never overwrite a human translation with a
filled (or English) one, but always take the newest translations from Transifex.**
the human translations. **The policy is: never overwrite a human translation in the
correct language with a filled (or English) one, but always take the newest correct-
language translations from Transifex. Text in another language is not protected: the
locale tag is authoritative, and wrong-language or mixed-language values must be
replaced with the language named by that tag.**
- Pull with `releases/translations/pull-translations.sh`. It runs `tx pull -a -f`
(which fills every string that is UNtranslated on Transifex with the English source)
and then a **per-key merge** (`releases/translations/merge-translations.mjs`) that,
for every language file and every key:
- Transifex has a real translation (pulled value differs from English) → **keep it**
(the newest human translation always wins);
- the pull reverted it to English but a human translation is committed in git →
**restore the committed translation** (a pull never reverts a human translation,
even in files that also received real new Transifex translations);
- Transifex has a real translation in the locale's declared language (pulled value
differs from English) → **keep it** (the newest correct-language human translation
always wins);
- the pull returned English but the pre-pull local file has a translation →
**restore the local translation as the fallback**. It may be human or a direct
machine/LLM fill; either way it remains local, while a real correct-language
Transifex translation takes precedence;
- **no translation anywhere** (untranslated on Transifex AND never committed) → leave
the English source as a placeholder. **This is the only case a non-human value is
used.** A separate fill step may fill *only* these English placeholders, so a filled
string can never overwrite a human translation.
- Restored languages are pushed back to Transifex so they stop reverting.
- After the merge, audit for mixed or wrong-language values. A value that differs from
English can still be wrong for its locale; replace it directly as described below.
- **The pull workflow never pushes translations to Transifex.** It cannot distinguish a
restored human translation from a committed direct fill, so automatic push-back would
misrepresent machine/LLM translations as human. Push only separately reviewed,
provenance-known human translations with an explicit push command.
### Filling the remaining untranslated strings — directly, no translation service
@@ -133,6 +143,59 @@ translations** and general **kanban terminology** for the language as the refere
earlier `machine-translate.mjs` that called LibreTranslate/DeepL is removed on purpose
(it did not work and needed a password). There is **no `WEKAN_MT*` env var** anymore.
- **EVERY language gets translated — including the ones nobody has volunteered for.**
Klingon, Volapük, Wolof, Venda, Tamazight, Acehnese and the rest are not exceptions:
WeKan cannot find a speaker for every language, and a file left in English stays in
English for years. **Look the words up.** Any dictionary, word list, grammar or
Wiktionary page on the Internet is a legitimate source — read it, take the terms it
gives, and write the translation from them.
- **A locale file must contain its declared language, not whichever language happened
to seed it.** If a Mongolian file contains Russian, replace the Russian with
Mongolian; likewise replace French, German, Malay, Zulu or any other wrong-language
text with the language named by the file's locale tag. This rule applies to isolated
mixed-language values and to an entire wrongly seeded file. Wrong-language text is
not a human translation for that locale and does not receive human-preference
protection. Because `fill-translations.mjs --apply` deliberately writes only English
placeholders, make these corrections directly in the locale JSON (preserving key
order and formatting), add regression coverage for the correct language/script, and
note low-confidence replacements in the commit. Script checks alone are insufficient
when both languages share a script, such as Russian and Mongolian; inspect vocabulary
as well.
- **Placeholders are code, not language. Never translate them.** Any source token that
begins and ends with underscores (for example `__board__`, `__card__` or
`__username__`) and any format token whose token begins with `%` (for example `%s`,
`%d` or `%1$s`) must be copied from the corresponding value in `en.i18n.json`
exactly, character for character. Preserve every placeholder, its spelling, case,
count and format; translate only the prose around it. If a locale has translated,
renamed, removed or malformed one of these tokens, restore the original token from
the same key in `en.i18n.json`, even when the surrounding translation is human.
Regression coverage for a translation batch must compare its underscore-delimited
and percent-prefixed token inventory with English so a translated placeholder cannot
pass as ordinary prose.
- **Add the languages WeKan does not have yet.** The 154 files under
`imports/i18n/data/` are not the list of languages worth supporting - they are the
list somebody happened to start. When a language has a dictionary, a word list or a
Wiktionary anywhere on the Internet, it can have a WeKan. Adding one is three edits,
and all three are needed or the language is invisible:
1. `imports/i18n/data/<tag>.i18n.json` — the strings, in `en.i18n.json`'s key ORDER
(`tests/boardItemLinks.test.cjs` checks that), 2-space indent.
2. `imports/i18n/languages.js` — `{ code, tag, name, load, rtl }`. The **name is
written in that language itself** ("Suomi", "tlhIngan Hol"), the way every entry
there already is, because it is read by somebody who does not read English. `rtl:
true` for Arabic, Hebrew, Persian, Uyghur and the rest that are written
right-to-left.
3. `client/components/users/userHeader.js` — the `flagMap` in `languageFlag()`, so the
picker shows a **flag** beside the name. A language without a country of its own
takes the flag of where it is spoken, and a constructed one (Esperanto, Klingon,
Volapük) keeps the `🌐` fallback rather than being given somebody's country.
- **A bad translation beats no translation.** An imperfect string is readable, it is
obviously improvable, and it is an *invitation*: somebody who speaks the language sees
it is wrong and fixes it, which an English placeholder never provokes. So: do not skip
a language because the result would be imperfect. Translate it, say in the commit which
ones were done with low confidence, and let a human correct it — the merge rules below
guarantee that when that human translation arrives on Transifex, it REPLACES the filled
one and is never overwritten by it.
- `node releases/translations/fill-translations.mjs --missing` — per-language count of
strings still needing translation (English + `en-*` variants are skipped: English by
design). Also printed at the end of `pull-translations.sh`.
@@ -145,14 +208,14 @@ earlier `machine-translate.mjs` that called LibreTranslate/DeepL is removed on p
**Both directions are safe, and it is verified:**
`node releases/translations/verify-human-preference.mjs` proves (pure-logic, no network)
that the pull-merge keeps/restores human translations and that a fill only touches
placeholders. Filled strings stay **local** — they are **NOT** pushed to Transifex (only
the merge-restored human languages are pushed), so a filled string can never masquerade
as human there. So: **only missing strings are ever filled, only when missing
everywhere, human strings are always preferred and merged**, and nothing you fill is
pushed to Transifex as if it were human.
that a real Transifex translation wins, the pre-pull local translation fills only an
English result, a fill only touches placeholders, and the pull script contains no push.
Thus **correct-language Transifex human strings are preferred and merged**, remaining
keys retain their local human or machine/LLM translations, and nothing restored or
filled is pushed to Transifex as if it were human. Wrong-language values are corrected
directly after the merge.
## General practices (from ../log/v10/Claude.txt)
## General practices (from .tools/log/v10/Claude.txt)
- **[maintainer only]** Commit as `Lauri Ojansivu <x@xet7.org>`, with **no**
"Co-Authored-By" or any other AI trailer, directly to the `main` branch of WeKan and
@@ -165,7 +228,7 @@ pushed to Transifex as if it were human.
- Directory structure:
- `wekan` — this repo (https://github.com/wekan/wekan); see
`docs/DeveloperDocs/Directory-Structure.md`; `CHANGELOG.md` at root.
- `../w/wekan.fi` — the WeKan website.
- `.tools/wekan.fi` — the WeKan website companion repository.
- `.tools/` — everything that is NOT part of this repository but is needed to
build, test and release it, in ONE directory that `.gitignore` and
`.meteorignore` already exclude (each used to need its own ignore entry at
@@ -186,7 +249,7 @@ pushed to Transifex as if it were human.
| `.tools/sandstorm` | sandstorm-io/sandstorm | — | upstream Sandstorm, when present — somebody else's project, cloned for reference |
**Unpacked toolchains and caches.** Downloads, not repositories — put there by
the sandbox instructions in `docs/Security/Sandboxes/vscode/README.md`, deleted
the sandbox instructions in `docs/Security/Sandboxes/vscodium/README.md`, deleted
and re-fetched freely, never committed anywhere:
`node-v<version>-linux-<arch>/` (the Node.js the test suites are run with),
`go/` with `gopath/`, `gomodcache/` and `gocache/` (FerretDB's Go builds),
@@ -228,11 +291,45 @@ pushed to Transifex as if it were human.
the newest release (FerretDB uses `## Upcoming FerretDB release`; the patch repos use
`# Upcoming <repo> release`). Do **not** hand-edit
`package.json` or any other version reference — the release workflow bumps those.
- **Whenever `# Upcoming WeKan ® release` is updated, audit every feature, bug fix
and security fix in that section for regression coverage.** If coverage does not
exist yet, add a positive test, a negative test and a UI test wherever that kind
of test is possible and relevant. Run the tests that can run in the current
environment; when a UI stack or another required service is unavailable, still
add and syntax-check/register the test and state clearly what could not be run.
Do not duplicate an existing test merely to satisfy the audit — extend it when
that keeps one behavior in one suite.
- **CHANGELOG.md holds the CURRENT MONTH only.** It reached 2.6 MB and 51,365
lines over 1,100 releases back to 2015 (#6580), which is slow to open and
slower to read. Moving whole years out left 1.9 MB, still too large, because
releases here are frequent: 2026 alone is 272 releases over eight months and
July was 80 on its own. So:
| | |
| --- | --- |
| `CHANGELOG.md` | the current month, plus `# Platforms`, `# TODO Later`, `# Upcoming` |
| `old-CHANGELOG/<year>/<MM>.md` | earlier months of the current year |
| `old-CHANGELOG/<year>.md` | years that are over, whole |
Past years stay one file each because they are already small (30–107 KB);
splitting them further would trade a size problem nobody has for a hundred
more files. Each archive opens with a **release count** — per month in a year
file, per day in a month file — and a bullet in `# Platforms` links every one.
That `git blame` is less useful on the split file is accepted: the history is
still in git (`gitk`, `git-gui`, `git log --follow`), and being small enough
to open is worth more.
Run `node releases/changelog-archive.mjs` at the start of a month. It is
idempotent — a run with nothing to move only refreshes the tables — and it
takes the month to keep from the FILE rather than the clock, so two people
running it on the same day agree. An archived section is never edited, for the
same reason a released one is not.
- **The file's shape, top to bottom** — keep it exactly as it is now:
1. `# Platforms` — the line `Newest WeKan at these platforms:` and the Install /
Upgrade / Docs / Mac ChangeLog bullets, then a `<details>` whose `<summary>` is
`Version` holding "which WeKan version uses what". There is no `# Version`
heading of its own.
Upgrade / Docs / Mac ChangeLog bullets, the `Older releases:` bullet linking
the per-year archives, then a `<details>` whose `<summary>` is `Version`
holding "which WeKan version uses what". There is no `# Version` heading of
its own.
2. `# TODO Later` — a `<details>` whose `<summary>` is `Carried to a future
release.` explaining the list, then one `<details>` per category (below).
3. The releases, newest first, each `# v<MAJOR>.<MINOR> YYYY-MM-DD WeKan ® release`.
@@ -295,16 +392,17 @@ pushed to Transifex as if it were human.
subsection just keeps its single `This release …:` header. Use `and adds the following
new features:`, `and fixes the following bugs:`, `and updates the following
dependencies:`, etc., matching the verb to the subsection.
- **The Upcoming section opens with an `**In short:**` paragraph.** One paragraph,
right under the `# Upcoming WeKan ® release` line and above the first
`This release …:` header, saying what the whole release amounts to — the areas
that changed and what changed about them, with the notable names in `**bold**`
so it can be skimmed. It is a SUMMARY, not a list: it does not link commits, it
does not repeat an entry's wording, and it ends by accounting for the rest in a
clause (`Below that: dependency updates, nine bug fixes … and the usual
documentation and translation work.`). Keep it current as entries are added —
it is the first thing a reader sees, so a stale one misdescribes the release.
A finished release keeps the paragraph it was written with.
- **The Upcoming section opens with an `**In short:**` paragraph.** It must be
short: put one compact, high-level paragraph directly under the
`# Upcoming WeKan ® release` line. Aim for 2–4 sentences and no more than about
120 words, however many commits the release contains. Name only the release's
major outcomes or themes, with notable names in `**bold**` so it can be skimmed.
It is not a commit inventory, progress log or miniature changelog: do not list
batches, per-language counts, individual test fixes or every touched component,
do not link commits, and do not repeat entry wording. The topic groups and their
`<details>` blocks below carry that information. Keep the paragraph current as
topics change, and shorten it when added commits make it grow. A finished release
keeps the paragraph it was written with.
- **Under the summary comes the BINARIES TABLE: what each platform ships.** So the
top of a release section is, in order, (1) the `**In short:**` paragraph and
(2) this table, and only then the `This release …:` subsections. A WeKan bundle
@@ -339,7 +437,7 @@ pushed to Transifex as if it were human.
has no rows**, which is how the table also answers "why is there no i386 bundle
this time": no source published a Node.js for it (see
`releases/resolve-node-source.sh`).
- **Inside a subsection, entries are GROUPED BY AREA.** A release touches a handful
- **Inside a subsection, entries are GROUPED BY TOPIC/AREA.** A release touches a handful
of areas and repeating the area's name in every summary is the noise this
removes — twelve entries that each begin "All Boards:" say "All Boards" twelve
times and the part that differs starts halfway through the line. Instead the area
@@ -367,15 +465,24 @@ pushed to Transifex as if it were human.
…
```
The group line is `**Area** - short description.` on ONE line, wrapped at 80 like
everything else, with a blank line under it. It is NOT a heading: a `##` inside a
release breaks the version list (see above). Group labels are the areas of the
The group line is `**Topic** - short summary.` on ONE line where practical,
wrapped at 80 like everything else, with a blank line under it. This line is the
high-level summary of ALL commits in that topic: say what changed and why it
matters without copying a commit summary or enumerating implementation details.
The `<summary>` under it gives each commit's short outcome, and the expanded
`<details>` body carries the implementation, rationale and test evidence. In
other words, the release reads from release summary → topic summary → commit
detail, with each level adding information instead of repeating the level above.
The group line is NOT a heading: a `##` inside a release breaks the version list
(see above). Group labels are the areas of the
app — `All Boards`, `The first header bar`, `The left menus`, `The Admin Panel`,
`Board views`, `Public Boards`, `Member Settings`, `Board roles` — and the same
label is reused across subsections when an area has both a feature and a fix.
EVERY entry of a grouped subsection belongs to a group, including a group with
one entry: a section that is half grouped and half loose reads as a mistake. A
subsection with only one entry, and the dependency bullets, stay flat.
one entry: a section that is half grouped and half loose reads as a mistake. Put
each commit under the topic it actually changes; do not create a chronological
catch-all group. A subsection with only one entry, and the dependency bullets,
stay flat.
- **CRITICAL security header — match the previous releases' wording.** A security release
leads with `This release fixes the following CRITICAL SECURITY ISSUE of
[Name](https://wekan.fi/hall-of-fame/namebleed/):` for a single named *Bleed, or
@@ -422,17 +529,84 @@ Fixes #1234,
Fixes #1235.
```
- **A commit that fixes a GitHub issue ENDS with `Fixes #NNNN`** — last lines of
the message, one per issue, comma after each and a full stop on the last. That
trailer is what closes the issue when the commit is pushed; a fix that only
*mentions* the number in prose leaves the issue open, and somebody has to close
it by hand later or it stays open forever. Several issues in one commit get
several lines. The same applies to an issue that turns out to be **already
fixed**: commit the test or the note that proves it and end with `Fixes #NNNN,`
so the issue closes with a reference to where it was fixed.
- **Only when the commit really fixes it.** A commit that improves the
diagnostics, narrows the cause, or fixes one of several reported problems
references the issue in the body (`#6585 comment 5276581923`) and does NOT
carry the trailer — closing an issue whose reporter is still stuck is worse
than leaving it open. If an issue was closed and then REOPENED for follow-up
items (as #6586 was), it is fixed again only when those items are done.
- **A commit with no issue behind it has no trailer** — the email reports, the
release-tooling fixes found in build logs, refactors. `Thanks to ... and xet7 !`
still names whoever reported it.
### Making a release — no version number needed **[maintainer only]**
All publishing / release steps below are maintainer-only. Contributors never run them.
**Releases are FREQUENT, and that is the normal state of this repository — not an
interruption to it.** The maintenance loop is:
1. `./build.sh` (or `build.bat` on Windows) → **option 1, "git pull and git
push"**. Both directions do the whole job: a pull rebases and then repairs the
CHANGELOG commit links the rebase made stale, and a push repairs them again
BEFORE publishing, because a stale link that reaches GitHub 404s for everyone
who reads the release notes.
2. `./releases/release-all.sh`, **with no arguments**, whenever there is something
worth shipping — which is often, several times a day when a fault is being
chased. It takes the version from the CHANGELOG and needs nothing typed.
So a release happening "in the middle" of a piece of work is not a special case
to reason about; it is what always happens, and anything that only works when
releases are rare is broken here. Two consequences worth stating, because both
have cost a released section its accuracy:
- **Work continues immediately after a release**, so `release-all.sh` renames
`# Upcoming WeKan ® release` to `# v<NEW> …` and then OPENS A NEW EMPTY
`# Upcoming` (`releases/changelog-open-next.mjs`), so the next entry has
somewhere correct to go. Without it an entry appended above the closing
`Thanks to above GitHub users …` line lands INSIDE the release just published.
The new section carries an `**In short:** nothing here yet.` placeholder and
the binaries table, so the file stays valid; replace the placeholder as entries
are added. `tests/changelogEntriesBelongToTheirRelease.test.cjs` checks the
newest few releases against git and fails when a section links a commit that
release does not contain.
- **A released section is a RECORD, not a draft.** When a release turns out to be
broken, its section keeps saying what it shipped — including the part that was
wrong — and the fix goes in a new `# Upcoming` above it. Do NOT edit a
published entry to describe the smaller, tidier change you wish had shipped;
add to it that it was wrong and where the fix is. Somebody reading v10.97's
notes is most likely somebody whose v10.97 just died.
- WeKan: run `./releases/release-all.sh` (no arguments). It renames
`# Upcoming WeKan ® release` to the next version (same increment as the last release;
9.99 → 10.00) dated today, commits + pushes, and triggers
`.github/workflows/release-all.yml` (its `bump` job bumps `package.json` and every
version reference, then the release jobs tag `v<new>` and publish the GitHub Release).
An explicit `oldversion newversion` pair still overrides. Adding entries under Upcoming
is the only hand step.
`.github/workflows/release-all.yml` — whose `bump` job bumps `package.json` and every
version reference and rebuilds the API docs, whose `prepare` job then pushes the tag
`v<new>` and checks the notes exist, and whose `release` job publishes the GitHub
Release the bundle jobs attach to. An explicit `oldversion newversion` pair still
overrides. Adding entries under Upcoming is the only hand step.
- **A release that FAILED and one that shipped BROKEN are handled differently, and
the difference is whether anything was PUBLISHED.** Both happened with v10.92, so
neither is hypothetical:
- **Nothing published** (the workflow died before the GitHub Release existed): there
is no release for that number, so `# v<new> …` must not claim there is one. Rename
the heading back to `# Upcoming WeKan ® release`, add the fix under it, and run
`release-all.sh` again — it will take the same number, since the newest release is
still the previous one.
- **It published, and something in it is broken**: the release exists and people can
download it, so its section STAYS as it is — a released section is a record, not a
draft. Add a NEW `# Upcoming WeKan ® release` above it with the fix, exactly as
during ordinary development, and the next run takes the next number.
What decides it is the GitHub Release, not the tag: `prepare` pushes the tag early,
so a tag can exist for a release that never published.
- FerretDB: run `./build.sh release-ferretdb` from `.tools/FerretDB` (no
version). It renames `## Upcoming FerretDB release` to the next version with the
correct git-tag link, commits + tags + pushes, then triggers `release-all.yml` (which
@@ -442,8 +616,85 @@ All publishing / release steps below are maintainer-only. Contributors never run
### Security issues
- Fix the vulnerability, add a CRITICAL section to the WeKan CHANGELOG like previous
entries, and update `../w/wekan.fi/hall-of-fame/index.html` and the vuln-name
entries, and update `.tools/wekan.fi/hall-of-fame/index.html` and the vuln-name
subdirectory `index.html` like previous security issues.
- **Every security fix gets a TEST and a NEGATIVE TEST, and they are written so the
fault cannot exist ANYWHERE in the codebase — not just at the place it was
reported.** A test that pins one call site leaves the same mistake free to live in
the other five, and that is how most of these arrive: SignupBleed's guard read an
option nothing sets, and the same shape sat in a second endpoint; the source-map
trim was safe on the client and fatal on the server. So:
- the **test** proves the fix does what it claims, driving the decision itself
where that is possible. A pure module — `loginFailureDecision.js`,
`lockoutDecision.js` — can be tested as arithmetic, without a server or a
database, and reproduces the reporter's attack exactly rather than approximately;
- the **negative test** proves the fault is gone rather than moved. Search the
whole tree for the SHAPE of it and assert nothing matches: no other endpoint
reads the dead option, no other counter is global, no other caller skips the
check. When the shape is a pattern, pin the pattern.
- and a test that reads the source is a real test here. `tests/*.test.cjs` may
parse a file and fail on a construct — that is what makes "and nowhere else"
checkable at all.
- **If somebody ATTEMPTS the attack, and the secure default DENIES it, that denial
has to be visible in Admin Panel → Problems** — in every case where the denial can
be attributed. This applies to every security fix added to Upcoming. That is the
difference between a hole that is closed and a hole that is closed and watched:
an administrator should be able to see that somebody tried.
- Add a key to the catalog in `models/lib/securityCategories.js` (category, the
hall-of-fame `bleed` name, severity, CWE), and call
`require('/server/lib/securityLog').record({ key, action: 'blocked', source, detail })`
on the refusal path. `action` is `'blocked'` when the fix stopped it and
`'detected'` when it was only noticed.
- **Wrap the call so logging can never break the guard**:
`try { ... } catch (e) { /* logging must never break the guard */ }`. The refusal
matters more than the record of it.
- **Only log an ATTEMPT, never ordinary use.** The test to apply is whether a
legitimate user can reach that line. Registration refused while registration is
off has no legitimate caller, so it is logged; an admin endpoint whose fault was
in what its answer CARRIED fires on every normal call, so it is NOT — that is why
HashBleed (GHSA-6qpx-x7vr-p9w6) deliberately has no key. A log that fills with
normal traffic hides the one line that mattered.
- Where the denial cannot be attributed to an attempt — a fix that changes what a
response contains, or one that only takes effect at build time — there is nothing
to record, and that is a decision to state in the entry rather than an omission.
- **Admin Panel → Problems is a SUMMARY, never a row per event.** A guard on a path
an attacker controls fires as fast as they can send, so a document per occurrence
grows the database with the attack, turns the page into a scroll of near-identical
lines, and buries the one event that mattered under ten thousand that did not. The
admin's question is never *list every attempt* — it is *what is happening, how
much, since when, and who*. So each problem is ONE row that accumulates
(`models/lib/eventLogSummary.js`, written through `server/lib/eventLogFold.js`,
shared by the security, speed and test loggers):
| field | what it holds |
| --- | --- |
| `count` | how many times this problem has happened |
| `firstAt` … `at` | the window it happened in |
| `actors` | who, each with their own count — `username1 25, 100.100.100.100 30` |
| `username` / `ip` / `detail` | the MOST RECENT occurrence |
- **Identity is the KIND of thing that happened** — stream, `bleed`, category,
action, source, severity, CWE (and `type`/`db`/`kind` for the database stream).
The actor is NOT part of it: putting a username or an address in the key gives a
row per attacker per attempt, which is the cost being removed.
- **A username and an address are tallied SEPARATELY**, not as a pair. They answer
different questions — which account, and where from — and an unauthenticated
attempt has an address and no name.
- **The tally is CAPPED** (`MAX_ACTORS`), with the remainder counted in
`actorsOverflow`. Otherwise an attacker rotating addresses grows the row with the
attack and reintroduces the same bug one level down — and *"and 9,412 others"* is
itself the signal that the source is spread rather than single.
- **A logger that writes per event is a bug to fix, not a style.** If one is added
or found, route it through the shared fold and migrate what it already wrote
(`server/lib/eventLogSummaryMigration.js` folds legacy rows in place, in batches,
idempotently).
- **Every hall-of-fame vulnerability that CAN be detected at runtime should have a
catalog key**, so Admin Panel → Problems groups attempts under the same name the
Hall of Fame uses and an admin can go from one to the other.
`tests/hallOfFameProblemsCoverage.test.cjs` lists the names that have no key and
requires each to be accounted for — a *Bleed with nothing to detect (a fix that
changed what a response carried, or one that only applies at build time) is
recorded there as a deliberate omission with its reason, not left silent.
- **A Hall of Fame row has EIGHT cells**, in this order: CVE, Icon, Vulnerability
name, Date, Responsible Security Disclosure by, Stars, Process, Vulnerabilities.
One thing per cell — the name without its icons, the Font Awesome icons alone in
@@ -498,19 +749,17 @@ All publishing / release steps below are maintainer-only. Contributors never run
- Add tests, negative tests and UI tests for all new features and fixes that do not yet
have tests. When adding a test, run or validate it and fix it until it works.
- **"Check newest test logs":** test logs go into a `log/<datetime>/` directory,
one per run (e.g. `2026-07-21_20-58-09/`). That is **`../log/`**, one level up
from the repo, so a run does not show up in `git status` - **unless the parent
is not writable**, as in a Flatpak sandbox that shares only the repository;
there it is **`./log/`** inside the repo (gitignored). `build.sh` prints which
one when a run starts. The
one per run (e.g. `2026-07-21_20-58-09/`). That is **`.tools/log/`** inside the
repository's ignored tool area, so a run does not show up in `git status` and
does not depend on the parent directory being writable. The
newest datetime directory is the latest run. Each holds the Playwright per-browser
logs (`wekan-alltests-chromium.log`, `-firefox.log`, `-webkit.log`), the mocha/unit
log (`wekan-alltests-mocha.log`), the e2e/import logs, and `wekan-test-server.log`
(the WeKan test server + database output). A whole-run directory also holds the
database-conformance logs (`db-conformance-*.log`, `-report.md`, `-summary.txt`)
and FerretDB's own (`ferretdb-unit.log`, `-vet.log`, `-integration.log`). "Check
the newest test logs" means: open the most recent `log/<datetime>/` - look in
`../log/` first, then `./log/` - and read those.
the newest test logs" means: open the most recent
`.tools/log/<datetime>/` and read those.
- **Check and fix WHILE the tests are still running.** A full run takes a long time
(three browsers, then every database with an image for this CPU, then FerretDB's
own suites), and its stages finish one at a time. Do not wait for the end: read
@@ -533,12 +782,53 @@ All publishing / release steps below are maintainer-only. Contributors never run
## Environment
- The editor (VSCode) runs inside a **Flatpak sandbox**, launched by
`docs/Security/Sandboxes/vscode/vscode-sandbox.sh`. What the sandbox allows/blocks and
how it is set up is documented in `docs/Security/Sandboxes/vscode/README.md` in that
- The editor (VSCodium) runs inside a **Flatpak sandbox**, launched by
`docs/Security/Sandboxes/vscodium/vscodium-sandbox.sh`. What the sandbox allows/blocks and
how it is set up is documented in `docs/Security/Sandboxes/vscodium/README.md` in that
same directory — read it when something behaves differently than a normal host (file
access, network, running services).
### Flatpak sandbox: install task tools under `.tools/`
- In the Flatpak sandbox launched by
`docs/Security/Sandboxes/vscodium/vscodium-sandbox.sh`,
the repository may be the only writable/shared host directory. When a command
needs a tool that is missing, install its binary, virtual environment, models
and caches under the repository-local `.tools/` directory. Do not install it
into the read-only Flatpak image or scatter generated tool files through the
source tree. `.tools/` is excluded by both `.gitignore` and `.meteorignore`, so
local toolchains neither enter commits nor consume Meteor file-watcher slots.
- Use the version selected by the repository, resolved from the current files at
install time; never copy a version number from an old log or hard-code the
example currently shown in the sandbox README. In particular: Node.js and npm
come from `NODE_VERSION` and `NPM_VERSION` in `Dockerfile`; Meteor comes from
`.meteor/release`; companion repositories use the versions in their own build
scripts or module files. Use the architecture reported by `uname -m`. Reuse an
existing matching `.tools` installation before downloading another copy.
- Follow the complete, tested bootstrap commands and environment variables in
`docs/Security/Sandboxes/vscodium/README.md`, especially its "Reuse same
in-sandbox toolchain" and test-runtime sections. Prefer repository setup
helpers such as
`build.sh` and `releases/ensure-tools.sh` when they already install the needed
pinned tool. Keep `HOME`, `PATH`, `GOROOT`, `GOPATH`, `GOCACHE`, `GOMODCACHE`
and similar overrides scoped to the command or sandbox terminal; do not change
the real host home or a system installation.
- For screenshot or image text that cannot be read by the normal image viewer
because Flatpak/bubblewrap cannot create a user namespace, install RapidOCR
locally instead of guessing from filenames or logs:
```bash
python3 -m venv .tools/ocr-venv
.tools/ocr-venv/bin/pip install rapidocr_onnxruntime pillow
.tools/ocr-venv/bin/python -c 'from rapidocr_onnxruntime import RapidOCR; import sys; result, _ = RapidOCR()(sys.argv[1]); print("\n".join(row[1] for row in (result or [])))' path/to/screenshot.png
```
Process a timestamped screenshot series in chronological order and correlate
OCR output with the matching `.tools/log/<datetime>/` files. OCR is evidence
with recognition errors: retain timestamps and coordinates when needed, verify
suspicious numbers against adjacent frames, and do not claim visual details
that neither OCR nor another available viewer confirmed.
### Always validate from the actual code
- When doing anything, check how it actually works in the code first.
+1 -1
View File
@@ -10,7 +10,7 @@ For all code at WeKan GitHub Organization https://github.com/wekan
- All known vulnerabilities have been fixed at newest WeKan, please upgrade https://wekan.fi/upgrade/
- There is no known backdoors. If WeKan is installed by someone else, first registered user is Admin and has Admin Panel access.
- Maintainer of WeKan has no access to someone elses installed WeKan.
- For someone elses installed WeKan, see [Forgot Password](https://github.com/wekan/wekan/blob/main/docs/Login/Forgot-Password.md)
- For someone elses installed WeKan, see [Forgot Password](https://github.com/wekan/wekan/blob/main/docs/Features/Login/Forgot-Password.md)
# Reporting about violations or something else
+60 -4
View File
@@ -20,14 +20,15 @@ LABEL org.opencontainers.image.source="https://github.com/wekan/wekan"
# TARGETARCH and TARGETVARIANT are automatically provided by Docker Buildx
ARG TARGETARCH
ARG TARGETVARIANT
ARG VERSION=10.80
ARG VERSION=11.26
ARG DEBIAN_FRONTEND=noninteractive
ENV BUILD_DEPS="apt-utils gnupg wget bzip2 g++ curl libarchive-tools build-essential git ca-certificates python3 unzip"
ENV \
DEBUG=false \
NODE_VERSION=v24.19.0 \
DDP_TRANSPORT=sockjs \
NODE_VERSION=v24.20.0 \
METEOR_RELEASE=METEOR@3.5-rc.2 \
USE_EDGE=false \
NPM_VERSION=11.12.1 \
@@ -188,6 +189,31 @@ ENV \
# never end up on Node.js from different places. It is copied in rather than
# reimplemented here; a second copy of that order would drift from the first.
COPY --chmod=755 releases/resolve-node-source.sh /tmp/resolve-node-source.sh
# It asks nodejs.org and github.com which builds exist, through releases/fetch.sh
# - which retries a 503 instead of reading it as "that build does not exist".
# The two travel together: without this line the resolve step dies on the first
# lookup. tests/releaseDownloads.test.cjs pins the pair.
COPY --chmod=755 releases/fetch.sh /tmp/fetch.sh
# The bundle's `npm install` leaves node-gyp's whole tree - 83 of the 120
# packages in programs/server/node_modules - in a bundle that compiles nothing at
# run time, and a scan of the published image reads it as what it is. The same
# script runs in every per-arch leg of release-all.yml, so the .zip bundles and
# this image are pruned identically. tests/imageBuildOnlyModules.test.cjs pins it.
COPY --chmod=755 releases/prune-build-only-modules.mjs /tmp/prune-build-only-modules.mjs
# Its companion, and its manifest: the npm packages Meteor's own packages bundle,
# which nothing in package.json can reach. The .zip is built with these already
# applied; what this run has to redo is the part `npm install` puts back.
COPY --chmod=755 releases/bump-bundle-npm-deps.mjs /tmp/bump-bundle-npm-deps.mjs
COPY --chmod=644 releases/bundle-npm-security-bumps.json /tmp/bundle-npm-security-bumps.json
# And the third of the same kind: WeKan talks DDP over sockjs on every platform,
# so no bundle carries uWebSockets.js. ddp-server requires that module only
# inside the uws transport's setup(), which a sockjs server never calls, and it
# is 121M of prebuilt binaries for OS/CPU/ABI combinations one machine cannot
# use. The entrypoint coerces DDP_TRANSPORT=uws to sockjs, so an existing
# compose file that asks for uws keeps working rather than crash-looping.
COPY --chmod=755 releases/bundle-trim.mjs /tmp/bundle-trim.mjs
# Its companion: the same idea applied to programs/server/npm/node_modules.
COPY --chmod=755 releases/prune-unreachable-npm.mjs /tmp/prune-unreachable-npm.mjs
RUN <<EOR
set -o xtrace
@@ -279,8 +305,12 @@ esac
chmod +x /usr/local/bin/node
# npm + npx from the official amd64 tarball of the SAME version; extract only
# those paths, not the amd64 node binary.
wget "https://nodejs.org/dist/${node_full}/node-${node_full}-linux-x64.tar.gz"
wget "https://nodejs.org/dist/${node_full}/SHASUMS256.txt"
# Same flags as the download above: a bare wget treats a 503 as fatal, and
# nodejs.org has them.
wget --tries=20 --waitretry=20 --retry-on-http-error=403,500,502,503 \
"https://nodejs.org/dist/${node_full}/node-${node_full}-linux-x64.tar.gz"
wget --tries=20 --waitretry=20 --retry-on-http-error=403,500,502,503 \
"https://nodejs.org/dist/${node_full}/SHASUMS256.txt"
grep " node-${node_full}-linux-x64.tar.gz\$" SHASUMS256.txt | sha256sum -c -
tar xzf "node-${node_full}-linux-x64.tar.gz" -C /usr/local --strip-components=1 --no-same-owner \
"node-${node_full}-linux-x64/lib/node_modules/npm" \
@@ -311,6 +341,21 @@ wget --tries=20 --waitretry=20 --retry-on-http-error=404,403,500,502,503 "${WEKA
unzip "wekan-${VERSION}-${WEKAN_ARCH}.zip"
rm "wekan-${VERSION}-${WEKAN_ARCH}.zip"
npm install --prefix ./bundle/programs/server
# node-gyp and @mapbox/node-pre-gyp compiled nothing here - every native module
# in the bundle is a prebuilt .node - and nothing in boot.js reaches them. Their
# tree is what shipped `tar` 6.2.1 (CRITICAL) and npm's networking stack to the
# published image, so it goes now that the install is done.
node /tmp/prune-build-only-modules.mjs ./bundle
# The .zip already carries the bumped meteor/ tree; this install put back
# meteor-dev-bundle's own underscore 1.13.7 pin (CVE-2026-27601) over it, so the
# same pass runs here.
node /tmp/bump-bundle-npm-deps.mjs ./bundle
# No uWebSockets.js, no legacy client, no source maps: this image runs sockjs,
# serves web.browser to every browser, and has no debugger attached to it.
node /tmp/bundle-trim.mjs ./bundle --transport sockjs --drop-legacy-client
# And the npm tree rspack cannot tree-shake, because Atmosphere packages load it
# through Npm.require(). Only what it can prove nothing requires.
node /tmp/prune-unreachable-npm.mjs ./bundle
mv /home/wekan/app/bundle /build
# The .zip bundle now ships a self-contained launcher + its own Node.js for the
@@ -325,6 +370,13 @@ rm -f /build/node /build/start-wekan.sh /build/start-wekan.bat
mv $(which tar)~ $(which tar)
# Cleanup
# npm is a BUILD tool in this image and nothing else: the only thing it does is
# the `npm install` above, and the container starts bash + wekan-entrypoint.sh,
# which never calls it. Shipping it shipped npm's own bundled dependencies - its
# `tar` (CRITICAL), `sigstore`, `@sigstore/*`, `ip-address`, `brace-expansion` -
# as image content that no code path can reach. node itself stays, because that
# is what runs WeKan.
rm -rf /usr/local/lib/node_modules/npm /usr/local/bin/npm /usr/local/bin/npx
# Remove unused Go-based pebble binary shipped by base image to reduce CVE surface.
apt-get remove --purge --assume-yes pebble || true
rm -f /usr/bin/pebble
@@ -348,6 +400,10 @@ COPY --chmod=755 releases/ferretdb/wekan-entrypoint.sh /build/wekan-entrypoint.s
# #6492: standalone "recovering data" page the entrypoint serves as a brief bridge on
# the web port while a just-restored FerretDB comes back up during a data recovery.
COPY --chmod=644 releases/ferretdb/recovery-bridge.mjs /build/recovery-bridge.mjs
# #6595: the entrypoint asks this whether the database answers yet, so it can
# serve the "waiting for database" page instead of leaving the web port unbound
# for a reverse proxy to time out on.
COPY --chmod=644 releases/ferretdb/db-ready.mjs /build/db-ready.mjs
USER wekan
ENV PORT=8080
+2 -1
View File
@@ -67,7 +67,8 @@ that by providing one-click installation on various platforms.
- WeKan ® is used in [most countries of the world](https://snapcraft.io/wekan).
- WeKan ® largest user has 30k users using WeKan ® in their company.
- WeKan ® has been [translated](https://app.transifex.com/wekan/) to 154 languages.
- WeKan ® has been [translated](https://app.transifex.com/wekan/) to 234 languages,
197 of them essentially complete.
- [Features][features]: WeKan ® has real-time user interface.
- [Platforms](https://wekan.fi/install/): WeKan ® supports many platforms.
WeKan ® is critical part of new platforms Wekan is currently being integrated to.
+1 -1
View File
@@ -1,5 +1,5 @@
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
appVersion: "v10.80.0"
appVersion: "v11.26.0"
files:
userUploads:
- README.md
+159 -51
View File
@@ -51,14 +51,17 @@ REM development sessions and test runs don't crash with "FATAL ERROR: ...
REM JavaScript heap out of memory". TOOL_NODE_FLAGS controls the Meteor
REM command-line/build process (the one that hits the limit during
REM `meteor run` / `meteor test` / `meteor build`); NODE_OPTIONS covers the
REM child Node/rspack processes. Both default to 8 GB and honor any value you
REM already set. Lower it if your machine has less RAM.
if not defined TOOL_NODE_FLAGS set "TOOL_NODE_FLAGS=--max-old-space-size=8192"
if not defined NODE_OPTIONS set "NODE_OPTIONS=--max-old-space-size=8192"
REM child Node/rspack processes. Both derive their defaults from installed RAM and honor any value you
set "WEKAN_MEMORY_MB=16384"
for /f "usebackq delims=" %%M in (`powershell.exe -NoProfile -Command "[math]::Floor((Get-CimInstance Win32_OperatingSystem).TotalVisibleMemorySize / 1024)" 2^>NUL`) do set "WEKAN_MEMORY_MB=%%M"
set /a WEKAN_BUILD_HEAP_MB=WEKAN_MEMORY_MB/2
if %WEKAN_BUILD_HEAP_MB% GTR 16384 set "WEKAN_BUILD_HEAP_MB=16384"
if not defined TOOL_NODE_FLAGS set "TOOL_NODE_FLAGS=--max-old-space-size=%WEKAN_BUILD_HEAP_MB%"
if not defined NODE_OPTIONS set "NODE_OPTIONS=--max-old-space-size=%WEKAN_BUILD_HEAP_MB%"
REM Every log this script writes goes into ..\log\ (one directory up from the
REM repo). Create it up front so redirections never fail on a missing directory.
if not exist "..\log" md "..\log"
REM Every log this script writes goes into the repo-local ignored .tools\log\.
REM Create it up front so redirections never fail on a missing directory.
if not exist ".tools\log" md ".tools\log"
REM --- Platform detection (OS + CPU arch), like detect_platform in the .sh ---
set "PLATFORM_OS=windows"
@@ -67,7 +70,7 @@ if /i "%PROCESSOR_ARCHITECTURE%"=="ARM64" set "PLATFORM_ARCH=arm64"
if /i "%PROCESSOR_ARCHITEW6432%"=="ARM64" set "PLATFORM_ARCH=arm64"
echo Platform: %PLATFORM_OS% %PLATFORM_ARCH%
echo Repo: %REPO%
echo Note: Dev-server console output is also logged to ..\log\wekan-log.log
echo Note: Dev-server console output is also logged to .tools\log\wekan-log.log
:menu
echo.
@@ -99,15 +102,17 @@ REM ===========================================================================
echo.
echo -- Setup -- ^(0 = Back^)
echo 1^) Install dependencies
echo 2^) Build WeKan
echo 3^) git pull ^(fetch, fast-forward or rebase, repoint moved CHANGELOG links^)
echo 4^) git push ^(check the CHANGELOG links, push, pull-and-retry once if rejected^)
echo 2^) Build WeKan release bundle
echo 3^) Build WeKan development bundle
echo 4^) git pull ^(fetch, fast-forward or rebase, repoint moved CHANGELOG links^)
echo 5^) git push ^(check the CHANGELOG links, push, pull-and-retry once if rejected^)
set "choice="
set /p "choice=Choose: "
if "%choice%"=="1" goto install
if "%choice%"=="2" goto build
if "%choice%"=="3" goto gitpull
if "%choice%"=="4" goto gitpush
if "%choice%"=="3" goto builddev
if "%choice%"=="4" goto gitpull
if "%choice%"=="5" goto gitpush
if "%choice%"=="0" goto menu
goto menu_setup
@@ -140,13 +145,13 @@ REM ===========================================================================
:menu_tests
echo.
echo -- Tests -- ^(0 = Back^)
echo 1^) EVERYTHING ^(sequential^): the floating-promises guard, then WeKan's own
echo tests ^(mocha, node suites, import, node E2E, three browsers^), then every
echo database with an image for this CPU, then all FerretDB tests ^(unit, vet,
echo integration^). Logs in ..\log\^<datetime^>\
echo 2^) WeKan's own tests only, parallel: Mocha, node unit suites, import, node
echo E2E and all three browsers, concurrently. No databases, no FerretDB
echo 3^) WeKan's own tests only, sequential: the same suite, one job at a time
echo 1^) EVERYTHING two-worker: one stage at a time, two Playwright workers
echo per browser; every database and all FerretDB tests remain sequential.
echo Logs in .tools\log\^<datetime^>\
echo 2^) EVERYTHING one by one: one stage and one Playwright worker at a time
echo for minimum RAM usage; database and FerretDB stages stay sequential
echo 3^) EVERYTHING at once: WeKan test jobs concurrently; database and
echo FerretDB stages remain sequential
echo 4^) Mocha ^(server-side^)
echo 5^) Import regression
echo 6^) Node E2E regressions
@@ -162,9 +167,9 @@ echo against every database with an image for this CPU, compare the answer
echo 15^) Run all FerretDB tests - SEQUENTIAL ^(unit, vet, integration^)
set "choice="
set /p "choice=Choose: "
if "%choice%"=="1" goto test_everything
if "%choice%"=="2" goto test_all_parallel
if "%choice%"=="3" goto test_all_sequential
if "%choice%"=="1" goto test_everything_two
if "%choice%"=="2" goto test_everything_one
if "%choice%"=="3" goto test_everything_all
if "%choice%"=="4" goto test_mocha
if "%choice%"=="5" goto test_import
if "%choice%"=="6" goto test_e2e
@@ -253,6 +258,8 @@ echo 19^) Delete a git tag, locally and on the remote
echo 20^) Move the 'stable' tag to HEAD
echo 21^) Release the wekan-ondra / wekan-gantt-gpl variants, part 1
echo 22^) Release the wekan-ondra / wekan-gantt-gpl variants, part 2
echo 23^) Report ^(or repair^) the Helm chart index for past releases
echo 24^) Rebuild the Helm index.yaml from the chart packages
set "choice="
set /p "choice=Choose: "
if "%choice%"=="1" call :rel_run "releases/release-all.sh" ""
@@ -277,6 +284,8 @@ if "%choice%"=="19" call :rel_run "releases/delete-tag.sh" "Version tag, e.g. v1
if "%choice%"=="20" call :rel_cmd "git tag --force stable HEAD && git push --tags --force && git push --follow-tags" ""
if "%choice%"=="21" call :rel_run "releases/release-ondra-1.sh" ""
if "%choice%"=="22" call :rel_run "releases/release-ondra-2.sh" ""
if "%choice%"=="23" call :rel_run "releases/backfill-charts.sh" ""
if "%choice%"=="24" call :rel_run "releases/reindex-charts.py" ""
if "%choice%"=="0" goto menu_releases
goto rel_release
@@ -628,6 +637,8 @@ if /I "%K%"=="release-ln" (set "CMD=bash releases/release-ln.sh" ^& goto cli_go)
if /I "%K%"=="release-x2" (set "CMD=bash releases/release-x2.sh" ^& goto cli_go)
if /I "%K%"=="release-cleanup" (set "CMD=bash releases/release-cleanup.sh" ^& goto cli_go)
if /I "%K%"=="release-charts" (set "CMD=bash releases/release-charts.sh" ^& goto cli_go)
if /I "%K%"=="backfill-charts" (set "CMD=bash releases/backfill-charts.sh" ^& goto cli_go)
if /I "%K%"=="reindex-charts" (set "CMD=python3 releases/reindex-charts.py" ^& goto cli_go)
if /I "%K%"=="release-website" (set "CMD=bash releases/release-website.sh" ^& goto cli_go)
if /I "%K%"=="npm-publish" (set "CMD=bash releases/npm-publish.sh" ^& goto cli_go)
if /I "%K%"=="test-download-urls" (set "CMD=bash releases/test-download-urls.sh" ^& goto cli_go)
@@ -794,11 +805,52 @@ call npm -g install meteor
echo Done. Open a new terminal so PATH changes take effect, then re-run this script.
goto end
REM ===========================================================================
REM Two entries, one build. :build adds what a RELEASE bundle is on top of
REM :builddev, and both share :buildcommon - so the plain build cannot drift
REM from the one the release steps run on.
:builddev
echo Building the WeKan DEVELOPMENT bundle ^(plain meteor build^).
call :buildcommon
if errorlevel 1 goto end
echo.
echo Done. This is NOT what a release ships: it still has the legacy client, the
echo source maps and uWebSockets.js, and no Node.js, FerretDB or launcher of its
echo own. Use "Build WeKan release bundle" to find out whether a release starts.
goto end
REM ===========================================================================
:build
echo Building WeKan.
REM Also clears the rspack dev-build caches (_build and node_modules\.cache) so the
REM next `meteor run` recompiles from scratch instead of serving stale modules.
echo Building the WeKan RELEASE bundle.
call :buildcommon
if errorlevel 1 goto end
REM THE REST OF WHAT A RELEASE BUNDLE IS - the same steps as the Release All
REM workflow for this platform, minus the .zip: the server's npm modules, the
REM three prunes, the sockjs / legacy-client / source-map trim, a verified
REM Node.js, FerretDB, the MongoDB Database Tools and start-wekan.bat. So this
REM entry answers whether the bundle a release would publish starts here, which
REM `meteor build` on its own never could.
REM
REM Through bash, like the git actions above and for the same reason: this is
REM releases/build-release-bundle.sh, the script the release itself runs, not a
REM batch copy of it that would drift from it.
where bash >nul 2>&1
if errorlevel 1 (
echo.
echo WARNING: bash was not found, so .build\bundle is a plain `meteor build`
echo bundle - no Node.js, no FerretDB, no launcher, nothing trimmed.
echo bash comes with Git for Windows ^(Git Bash^) and with WSL.
goto end
)
bash releases/build-release-bundle.sh .build/bundle
echo Done.
goto end
REM ===========================================================================
REM The part both build entries do: clear the rspack dev-build caches (_build and
REM node_modules\.cache) so the next `meteor run` recompiles from scratch instead
REM of serving stale modules, then build the bundle.
:buildcommon
if exist "%REPO%\node_modules" rmdir /s /q "%REPO%\node_modules"
if exist "%REPO%\node_modules\.cache" rmdir /s /q "%REPO%\node_modules\.cache"
if exist "%REPO%\.meteor\local" rmdir /s /q "%REPO%\.meteor\local"
@@ -807,8 +859,11 @@ if exist "%REPO%\_build" rmdir /s /q "%REPO%\_build"
call meteor update --npm
call meteor npm install
call meteor build .build --directory
echo Done.
goto end
if not exist "%REPO%\.build\bundle\main.js" (
echo ERROR: the build produced no .build\bundle\main.js.
exit /b 1
)
exit /b 0
REM ===========================================================================
:gitpull
@@ -922,6 +977,12 @@ goto end
REM ===========================================================================
:test_all_parallel
REM Clear anything a previous run left listening before starting: cmd cannot
REM trap Ctrl-C, so an interrupted run's database outlives it, and the harness
REM would REUSE it (it reuses a database that answers) with data this run never
REM seeded. build.sh does this with a trap on the way out; here it is done on
REM the way in, which covers the same gap.
call :stop_test_databases
echo Running ALL tests against ONE WeKan server on http://localhost:3000 - all jobs run IN PARALLEL (concurrently). Needs plenty of RAM (fine on 32 GB).
echo Two WeKan servers are involved:
echo :3000 - the PRECOMPILED .build\bundle run as a plain Node server (Meteor's mongod on :3001, db "meteor")
@@ -946,12 +1007,12 @@ set "S_mocha=RUN" & set "S_unit=RUN" & set "S_import=RUN" & set "S_e2e=RUN"
set "S_chromium=RUN" & set "S_firefox=RUN" & set "S_webkit=RUN"
set "C_mocha=0" & set "C_unit=0" & set "C_import=0" & set "C_e2e=0"
set "C_chromium=0" & set "C_firefox=0" & set "C_webkit=0"
REM Each run gets its own ..\log\<timestamp>\ dir (stamped once at run start), so
REM Each run gets its own .tools\log\<timestamp>\ dir (stamped once at run start), so
REM logs are never overwritten and previous runs are kept. PowerShell gives a
REM locale-independent yyyy-MM-dd_HH-mm-ss; %RUN_LOGDIR% is absolute so it works
REM from any job's working directory (e.g. the browser job runs in tests\playwright).
for /f %%i in ('powershell -NoProfile -Command "Get-Date -Format yyyy-MM-dd_HH-mm-ss"') do set "RUN_TS=%%i"
set "RUN_LOGDIR=%REPO%\..\log\%RUN_TS%"
set "RUN_LOGDIR=%REPO%\.tools\log\%RUN_TS%"
if not exist "%RUN_LOGDIR%" md "%RUN_LOGDIR%"
echo Logs for this run: %RUN_LOGDIR%\ - previous runs are kept
REM Clear completion flags from any previous run.
@@ -1030,9 +1091,7 @@ if "!ALLDONE!"=="0" (
)
echo.
echo Stopping WeKan test server (bundle node :3000).
taskkill /FI "WINDOWTITLE eq WekanTestServer*" /T /F >nul 2>&1
if "!MONGOD_STARTED!"=="1" ( echo Stopping test MongoDB ^(mongod :3001^). & taskkill /FI "WINDOWTITLE eq WekanTestMongo*" /T /F >nul 2>&1 )
call :stop_test_databases
REM Final pass/fail per job (RUN means it never wrote a flag = treat as FAIL).
if "!S_mocha!"=="FAIL" set "FAILED=1"
@@ -1060,6 +1119,12 @@ goto end
REM ===========================================================================
:test_all_sequential
REM Clear anything a previous run left listening before starting: cmd cannot
REM trap Ctrl-C, so an interrupted run's database outlives it, and the harness
REM would REUSE it (it reuses a database that answers) with data this run never
REM seeded. build.sh does this with a trap on the way out; here it is done on
REM the way in, which covers the same gap.
call :stop_test_databases
echo Running ALL tests against ONE WeKan server on http://localhost:3000 - all jobs run SEQUENTIALLY (one at a time).
echo Two WeKan servers are involved (they do NOT run tests in parallel; the suites run one at a time):
echo :3000 - the PRECOMPILED .build\bundle run as a plain Node server (Meteor's mongod on :3001, db "meteor")
@@ -1084,12 +1149,12 @@ set "S_mocha=RUN" & set "S_unit=RUN" & set "S_import=RUN" & set "S_e2e=RUN"
set "S_chromium=RUN" & set "S_firefox=RUN" & set "S_webkit=RUN"
set "C_mocha=0" & set "C_unit=0" & set "C_import=0" & set "C_e2e=0"
set "C_chromium=0" & set "C_firefox=0" & set "C_webkit=0"
REM Each run gets its own ..\log\<timestamp>\ dir (stamped once at run start), so
REM Each run gets its own .tools\log\<timestamp>\ dir (stamped once at run start), so
REM logs are never overwritten and previous runs are kept. PowerShell gives a
REM locale-independent yyyy-MM-dd_HH-mm-ss; %RUN_LOGDIR% is absolute so it works
REM from any job's working directory (e.g. the browser job runs in tests\playwright).
for /f %%i in ('powershell -NoProfile -Command "Get-Date -Format yyyy-MM-dd_HH-mm-ss"') do set "RUN_TS=%%i"
set "RUN_LOGDIR=%REPO%\..\log\%RUN_TS%"
set "RUN_LOGDIR=%REPO%\.tools\log\%RUN_TS%"
if not exist "%RUN_LOGDIR%" md "%RUN_LOGDIR%"
echo Logs for this run: %RUN_LOGDIR%\ - previous runs are kept
REM Clear completion flags from any previous run.
@@ -1153,9 +1218,7 @@ set "S_e2e=SKIP" & set "S_chromium=SKIP" & set "S_firefox=SKIP" & set "S_webkit=
:server_jobs_done
echo.
echo Stopping WeKan test server (bundle node :3000).
taskkill /FI "WINDOWTITLE eq WekanTestServer*" /T /F >nul 2>&1
if "!MONGOD_STARTED!"=="1" ( echo Stopping test MongoDB ^(mongod :3001^). & taskkill /FI "WINDOWTITLE eq WekanTestMongo*" /T /F >nul 2>&1 )
call :stop_test_databases
REM Final pass/fail per job (RUN means it never wrote a flag = treat as FAIL).
if "!S_mocha!"=="FAIL" set "FAILED=1"
@@ -1290,7 +1353,7 @@ REM
REM It used to be a single `playwright test --project=... --project=...` call
REM with no log at all: everything went to the terminal and there was nothing
REM left to read afterwards, which is what tests/dbConformanceWiring.test.cjs
REM means by "every Tests option writes its log to ../log/<datetime>/".
REM means by "every Tests option writes its log to .tools/log/<datetime>/".
set "PW_ALL_FAILED=0"
for %%B in (chromium firefox webkit) do call :pw_one_browser %%B
if "%PW_ALL_FAILED%"=="1" ( echo RESULT: Some Playwright browsers FAILED ^(see the logs above^). ) else ( echo RESULT: All Playwright browsers passed. )
@@ -1463,7 +1526,7 @@ exit /b 0
:set_dev_env
REM Common dev-server environment (caller sets ROOT_URL afterwards).
set "DEFAULT_METEOR_REACTIVITY_ORDER=changeStreams,oplog,polling"
set "DDP_TRANSPORT=uws"
set "DDP_TRANSPORT=sockjs"
set "DEBUG=true"
set "WRITABLE_PATH=.."
set "WITH_API=true"
@@ -1471,7 +1534,7 @@ set "RICHER_CARD_COMMENT_EDITOR=false"
exit /b 0
:onelog
REM Set ONELOG to ..\log\<datetime>\wekan-%1.log - the same place every other
REM Set ONELOG to .tools\log\<datetime>\wekan-%1.log - the same place every other
REM test run writes, so "the newest test logs" is one directory whichever option
REM produced them. A larger run (EVERYTHING) exports WEKAN_LOGDIR first, and then
REM the whole run stays in that one directory. The Windows equivalent of build.sh's
@@ -1479,7 +1542,7 @@ REM one_log().
if defined WEKAN_LOGDIR (
set "ONELOGDIR=%WEKAN_LOGDIR%"
) else (
for /f %%T in ('powershell -NoProfile -Command "Get-Date -Format yyyy-MM-dd_HH-mm-ss"') do set "ONELOGDIR=..\log\%%T"
for /f %%T in ('powershell -NoProfile -Command "Get-Date -Format yyyy-MM-dd_HH-mm-ss"') do set "ONELOGDIR=.tools\log\%%T"
)
if not exist "%ONELOGDIR%" md "%ONELOGDIR%" >nul 2>&1
set "ONELOG=%ONELOGDIR%\wekan-%~1.log"
@@ -1495,8 +1558,8 @@ exit /b 0
:runlog
REM Run "meteor run <args>" showing output live AND copying it to
REM ..\log\wekan-log.log - the Windows equivalent of the .sh's
REM "meteor run ... 2>&1 | tee ../log/wekan-log.log". cmd has no built-in tee,
REM .tools\log\wekan-log.log - the Windows equivalent of the .sh's
REM "meteor run ... 2>&1 | tee .tools/log/wekan-log.log". cmd has no built-in tee,
REM so pipe through PowerShell's Tee-Object. %* = all args forwarded to meteor.
REM Note: PowerShell buffers the pipeline, so console output can appear in
REM bursts; the full stream is always captured in the log file.
@@ -1504,8 +1567,8 @@ REM Callers always pass "--port <PORT>" first, so %2 is the port: kill any
REM Meteor dev server already listening there before starting a new one.
call :kill_meteor_on_port %2
if errorlevel 1 exit /b 1
if not exist "..\log" md "..\log"
call meteor run %* 2>&1 | powershell -NoProfile -Command "$input | Tee-Object -FilePath '..\log\wekan-log.log'"
if not exist ".tools\log" md ".tools\log"
call meteor run %* 2>&1 | powershell -NoProfile -Command "$input | Tee-Object -FilePath '.tools\log\wekan-log.log'"
exit /b 0
:kill_meteor_on_port
@@ -1562,6 +1625,33 @@ REM %1 = port. Kill whatever is LISTENING on that TCP port (and its process tree
for /f "tokens=5" %%p in ('netstat -ano ^| findstr /r /c:":%~1 .*LISTENING"') do taskkill /F /T /PID %%p >nul 2>&1
exit /b 0
:stop_test_databases
REM Stop what a test run started: the bundle server on :3000, the test mongod on
REM :3001 when THIS run launched it, and any database-conformance container.
REM
REM Called at the end of both all-tests flows, and again before a run starts,
REM which is the batch answer to build.sh's trap. cmd cannot trap Ctrl-C, so an
REM interrupted run leaves its database behind; clearing before starting means
REM the next run does not inherit it. That matters because the harness reuses a
REM database only when it ANSWERS - a leftover mongod answers, so it would be
REM reused, holding a database this run never seeded, and the failures would
REM land somewhere else entirely.
echo Stopping WeKan test server (bundle node :3000).
taskkill /FI "WINDOWTITLE eq WekanTestServer*" /T /F >nul 2>&1
if "!MONGOD_STARTED!"=="1" ( echo Stopping test MongoDB ^(mongod :3001^). & taskkill /FI "WINDOWTITLE eq WekanTestMongo*" /T /F >nul 2>&1 )
REM The conformance stage runs each engine in a container named
REM wekan-conformance-db-<timestamp> and removes it when that engine is done. An
REM interrupted run leaves the one it was on, and a container still holding 5432
REM or 3306 fails the next run's engine before it starts.
where docker >nul 2>&1
if not errorlevel 1 (
for /f %%c in ('docker ps -aq --filter "name=wekan-conformance-db-" 2^>nul') do (
echo Stopping database-conformance container %%c.
docker rm -f %%c >nul 2>&1
)
)
exit /b 0
:kill_all_dev_servers
REM Kill every dev/test server this script can start, freeing all dev/test ports
REM at once: the dev app (3000) + its Mongo (3001), the Mocha test server (3100)
@@ -1668,7 +1758,7 @@ if "!SERVER_READY!"=="1" echo ==^> WeKan test server is ready on http://localhos
exit /b 0
:rebuild_for_tests
REM Delete .build (and the rspack dev-build caches, as :build does) and build the
REM Delete .build (and the rspack dev-build caches, as :buildcommon does) and build the
REM WeKan bundle the test server runs. Same steps as Setup -> "Build WeKan", so the
REM two can never drift apart.
echo ==^> Deleting .build and building WeKan before running the tests ^(always, so the tests run against the current source^).
@@ -1832,7 +1922,7 @@ REM there, and installing Go and the module dependencies if they are
REM missing), then run the whole FerretDB v1 query catalogue against every
REM database that has a Docker image for THIS CPU - one at a time, because they
REM all use the same FerretDB port - and compare that they all answered the same.
REM Results go to ..\log\<datetime>\ with every other test run's.
REM Results go to .tools\log\<datetime>\ with every other test run's.
REM
REM The orchestration is one bash script, shared with build.sh rather than
REM rewritten here: a second implementation would drift, and Docker Desktop on
@@ -1855,7 +1945,7 @@ REM that .gitignore and .meteorignore already exclude, instead of one ignored
REM subdirectory each at the repo root. It is cloned here when it is not there,
REM the same as build.sh's ensure_tool_repo does, so neither script depends on
REM the other having been run first. Its build.sh installs Go and the Go modules
REM when they are missing, and writes its logs to ..\log\<datetime>\ with every
REM when they are missing, and writes its logs to .tools\log\<datetime>\ with every
REM other test run's.
where bash >nul 2>&1
if errorlevel 1 (
@@ -1886,13 +1976,27 @@ bash -c "cd .tools/FerretDB && ./build.sh test-all"
goto end
REM ===========================================================================
:test_everything_two
set "WEKAN_EVERYTHING_MODE=two-worker"
goto test_everything
:test_everything_one
set "WEKAN_EVERYTHING_MODE=sequential"
goto test_everything
:test_everything_all
set "WEKAN_EVERYTHING_MODE=parallel"
goto test_everything
:test_everything
REM Every test WeKan and FerretDB have, one stage at a time: WeKan's own suite,
REM then the database conformance run for every database with an image for this
REM CPU, then all of FerretDB's tests. One ..\log\<datetime>\ directory for the
REM CPU, then all of FerretDB's tests. One .tools\log\<datetime>\ directory for the
REM whole run, and nothing runs concurrently, which is what makes a failure
REM readable.
REM
REM The shared runner first stops and waits for any older EVERYTHING run, so the
REM two runs cannot share ports 3000/3001, databases or browser output.
REM The WeKan stage builds a Meteor bundle and runs a server, which needs the
REM POSIX shell throughout - so this hands the whole run to bash rather than
REM reimplementing it here, exactly as options 14 and 15 do.
@@ -1901,7 +2005,11 @@ if errorlevel 1 (
echo ERROR: bash was not found. It comes with Git for Windows ^(Git Bash^) and with WSL.
goto end
)
bash ./releases/run-everything.sh
bash ./releases/run-everything.sh %WEKAN_EVERYTHING_MODE%
if errorlevel 1 (
echo ERROR: EVERYTHING did not start or did not finish successfully. See the message above.
goto end
)
goto end
REM ===========================================================================
+619 -156
View File
File diff suppressed because it is too large. Load diff
+8 -16
View File
@@ -1,13 +1,19 @@
/* The board sidebar's Activities heading. It is the section's own control now -
a caret, the icon and the name, clicked to show the activities under it - so
it is a row of three things beside each other rather than a heading with a
toggle pushed to the far end. client/components/sidebar/sidebar.jade */
.activity-title {
margin: 0 10px 9px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 0.4em;
cursor: pointer;
}
.reactions-popup .add-comment-reaction {
display: inline-block;
cursor: pointer;
border-radius: 10px;
font-size: 18px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
text-align: center;
line-height: 1.3;
width: 70px;
@@ -15,20 +21,6 @@
.reactions-popup .add-comment-reaction:hover {
background-color: #b0c4de;
}
/* Eye / eye-slash icon toggle for showing/hiding activities. Open eye
(.eye-icon) = activities shown; crossed eye (.eye-slash-icon) = hidden.
Matches the password-visibility toggle used on the login/register pages. */
.activities-eye-toggle {
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 0.4em;
text-decoration: none;
}
.activities-eye-toggle .eye-icon,
.activities-eye-toggle .eye-slash-icon {
line-height: 1;
}
.activities {
clear: both;
}
+1 -1
View File
@@ -31,7 +31,7 @@ template(name="activity")
| {{{_ 'activity-attached' attachmentLink cardLink}}}.
if($neq mode 'board')
if activity.attachment.isImage
img.attachment-image-preview(src=activity.attachment.url)
img.attachment-image-preview(src=activity.attachment.url alt="")
//- board activity ------------------------------------------------------
if($eq activity.activityType 'createBoard')
+51 -24
View File
@@ -6,6 +6,7 @@ import { sanitizeHTML, sanitizeText } from '/imports/lib/secureDOMPurify';
import { TAPi18n } from '/imports/i18n';
import { Utils } from '/client/lib/utils';
import { getSidebarInstance } from '/client/features/sidebar/service';
import { activityCardLinkData } from '/models/lib/activityCardLink';
// #6480/#6481: 500 was far more than the sidebar/card activity feed ever shows
// at once, and with FerretDB (no oplog) every live cursor is re-run on a timer —
@@ -87,9 +88,20 @@ function _showActivities(data) {
const currentCard = Utils.getCurrentCard();
ret = currentCard.showActivities ?? false;
} else if (mode === 'card') {
// For card mode, get showActivities from data context or fallback to current card
const card = data?.card || Utils.getCurrentCard();
ret = card?.showActivities ?? false;
// The card's Activities section is COLLAPSED by default and renders this
// template only when its caret has been opened - so being here means the
// reader asked for the history, and the history is what they get.
//
// Until the section had a caret, the card carried a `showActivities` flag
// toggled by an eye beside the heading, and `false` - the default -
// subscribed to comments ONLY (server/publications/activities.js). The eye
// is gone: one control for one idea. Nothing is fetched at all while the
// section is closed, which is the cheaper half of what the flag was for,
// and opening it now means the whole history rather than a filtered part
// of it. Do not consult the obsolete per-card `showActivities` value here:
// existing cards commonly persist its old default (`false`), which would
// silently turn an explicitly opened Activities section into comments-only.
ret = true;
} else {
ret = Utils.getCurrentBoard().showActivities ?? false;
}
@@ -150,23 +162,23 @@ Template.activity.helpers({
return TAPi18n.__('this-card');
}
else if (this.mode !== 'board') {
return createCardLink(this.activity.card(), null);
return createCardLink(this.activity, this.activity.card(), null);
}
else if (currentBoardId != this.activity.boardId) {
return createCardLink(this.activity.card(), this.activity.board().title);
return createCardLink(this.activity, this.activity.card(), this.activity.board().title);
}
return createCardLink(this.activity.card(), null);
return createCardLink(this.activity, this.activity.card(), null);
},
cardLink() {
const currentBoardId = Session.get('currentBoard');
if (this.mode !== 'board') {
return createCardLink(this.activity.card(), null);
return createCardLink(this.activity, this.activity.card(), null);
}
else if (currentBoardId != this.activity.boardId) {
return createCardLink(this.activity.card(), this.activity.board().title);
return createCardLink(this.activity, this.activity.card(), this.activity.board().title);
}
return createCardLink(this.activity.card(), null);
return createCardLink(this.activity, this.activity.card(), null);
},
receivedDate() {
@@ -365,21 +377,36 @@ Template.commentReactions.helpers({
}
})
function createCardLink(card, board) {
if (!card) return '';
let text = card.title;
if (board) text = `${board} > ` + text;
return (
card &&
Blaze.toHTML(
HTML.A(
{
href: card.originRelativeUrl(),
class: 'action-card',
},
sanitizeHTML(text),
),
)
// #3144: the card is not always here. An ARCHIVED card is not published to the
// client, so `activity.card()` returned nothing and every sentence that named it
// was rendered around a hole - "Activities for archived card displayed as
// undefined on board settings".
//
// The activity itself recorded the title in most cases (`cardTitle`), and a card
// URL can be built from the ids it carries, so the link survives the card not
// being here. models/lib/activityCardLink.js decides what to show;
// this turns it into the anchor, and marks an archived card the way the reporter
// asked for: "{{ card title }} [archived]".
function createCardLink(activity, card, boardTitle) {
const link = activityCardLinkData(activity, card, card ? card.board && card.board() : null);
if (!link) {
// Nothing named the card - an old activity from before the titles were
// recorded, on a card this client cannot see. Say "this card" rather than
// leaving a gap in the middle of a sentence.
return sanitizeHTML(TAPi18n.__('this-card'));
}
let text = link.title;
if (link.archived) text = `${text} [${TAPi18n.__('archived')}]`;
if (boardTitle) text = `${boardTitle} > ` + text;
if (!link.url) return sanitizeHTML(text);
return Blaze.toHTML(
HTML.A(
{
href: link.url,
class: 'action-card',
},
sanitizeHTML(text),
),
);
}
+2 -2
View File
@@ -110,7 +110,7 @@
}
.comments .comment .comment-desc .reactions .open-comment-reaction-popup span {
display: inline-block;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
font-weight: 500;
line-height: 1;
margin-inline-start: 4px;
@@ -129,7 +129,7 @@
background-color: #b0c4de;
}
.comments .comment .comment-desc .reactions .reaction .reaction-count {
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
}
.comments .comment .comment-desc .comment-meta {
font-size: 0.8em;
+4 -3
View File
@@ -39,7 +39,8 @@ template(name="comment")
+editor(autofocus=true)
= text
.edit-controls
button.primary(type="submit") {{_ 'edit'}}
button.primary(type="submit" title="{{_ 'edit'}}" aria-label="{{_ 'edit'}}")
i.fa.fa-pencil-square-o(aria-hidden="true")
a.js-close-inlined-form(title="{{_ 'close' }}")
i.fa.fa-times-thin
else
@@ -56,7 +57,8 @@ template(name="comment")
template(name="editOrDeleteComment")
= ' - '
a.js-open-inlined-form {{_ "edit"}}
a.js-open-inlined-form(title="{{_ 'edit'}}" aria-label="{{_ 'edit'}}")
i.fa.fa-pencil-square-o(aria-hidden="true")
= ' - '
a.js-delete-comment {{_ "delete"}}
@@ -81,4 +83,3 @@ template(name="addReactionPopup")
.reactions-popup
each codepoint in codepoints
span.add-comment-reaction(data-codepoint="#{codepoint}") !{codepoint}
@@ -47,7 +47,7 @@
.board-conversion-header h3 {
margin: 0 0 8px 0;
color: #333;
font-size: 20px;
font-size: calc(20px * var(--wekan-ui-font-scale, 1));
font-weight: 500;
}
@@ -59,7 +59,7 @@
.board-conversion-header p {
margin: 0;
color: #666;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
}
.board-conversion-content {
@@ -116,14 +116,14 @@
text-align: center;
font-weight: 600;
color: #2196F3;
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
}
.conversion-status {
text-align: center;
margin-bottom: 16px;
color: #333;
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
}
.conversion-status i {
@@ -134,7 +134,7 @@
.conversion-time {
text-align: center;
color: #666;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
background-color: #f5f5f5;
padding: 8px 12px;
border-radius: 4px;
@@ -155,7 +155,7 @@
.conversion-info {
text-align: center;
color: #666;
font-size: 13px;
font-size: calc(13px * var(--wekan-ui-font-scale, 1));
line-height: 1.4;
}
@@ -179,6 +179,6 @@
}
.board-conversion-header h3 {
font-size: 18px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
}
}
+29 -18
View File
@@ -7,7 +7,7 @@
//- the same `.sidebar-content` with a title and a back arrow above the view. So
//- it looks and behaves like the one on a board without pretending to be it.
//-
//- docs/Design/Page/Search.md, docs/Design/Page/Multi-Selection.md
//- docs/Features/Page/Search.md, docs/Features/Page/Multi-Selection.md
template(name="allBoardsSidebar")
//- The theme class goes on an ANCESTOR of `.sidebar`, never on it: every
@@ -42,7 +42,7 @@ template(name="allBoardsHomeSidebar")
what you are looking at, which belongs beside the page's name rather than
behind a panel you have to open. Search and Multi-Selection were in that bar
as well and are NOT repeated here: this view has offered both as rows since
it was written. docs/Design/Page/All-Boards.md
it was written. docs/Features/Page/All-Boards.md
a.sidebar-btn.js-open-boards-sort(class="{{#unless isBoardsSort 'custom'}}emphasis{{/unless}}")
i.fa.fa-sort
span {{_ 'sort-boards'}}
@@ -52,7 +52,7 @@ template(name="allBoardsHomeSidebar")
a.js-all-boards-sidebar-search
i.fa.fa-search
span.sidebar-list-item-description {{_ 'search-boards'}}
if canModifyBoards
if canMultiSelectBoards
li
a.js-all-boards-sidebar-multiselection
i.fa.fa-check-square-o
@@ -83,23 +83,34 @@ template(name="allBoardsSearchSidebar")
//- them. They were four icons crowded into the header bar; here each one has
//- its name beside it.
template(name="allBoardsMultiSelectionSidebar")
if hasBoardsSelected
if isArchiveSelection
if canPermanentlyDeleteArchivedBoards
p.sidebar-multiselection-hint {{_ 'multi-selection-active'}}
button.sidebar-btn.negate.wide.js-delete-selected-boards(type="button")
i.fa.fa-trash
span {{_ 'delete'}}
else
p.sidebar-multiselection-hint {{_ 'archive-permanent-delete-disabled-hint'}}
else
p.sidebar-multiselection-hint {{_ 'multi-selection-active'}}
a.sidebar-btn.js-star-selected
i.fa.fa-star
span {{selectedStarTitle}}
a.sidebar-btn.js-home-selected
i.fa.fa-home
span {{_ 'set-selected-home'}}
hr
a.sidebar-btn.js-archive-selected-boards
i.fa.fa-archive
span {{_ 'archive-board'}}
a.sidebar-btn.js-duplicate-selected-boards
i.fa.fa-clipboard
span {{_ 'duplicate-board'}}
hr
a.sidebar-btn.js-multiselection-reset
i.fa.fa-times-thin
span {{_ 'multi-selection-off'}}
else
p.sidebar-multiselection-hint {{_ 'multi-selection-active'}}
if isHomeSelection
span {{_ 'unset-selected-home'}}
else
span {{_ 'set-selected-home'}}
unless isHomeSelection
hr
a.sidebar-btn.js-archive-selected-boards
i.fa.fa-archive
span {{_ 'archive-board'}}
a.sidebar-btn.js-duplicate-selected-boards
i.fa.fa-clipboard
span {{_ 'duplicate-board'}}
hr
a.sidebar-btn.js-multiselection-reset
i.fa.fa-times-thin
span {{_ 'multi-selection-off'}}
+66 -17
View File
@@ -1,9 +1,13 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
const { allBoardsPath, SECTION_ARCHIVE } = require('/models/lib/allBoardsUrls');
const {
allBoardsPath,
SECTION_HOME,
SECTION_ARCHIVE,
} = require('/models/lib/allBoardsUrls');
import { TAPi18n } from '/imports/i18n';
import { BoardMultiSelection } from '/client/lib/boardMultiSelection';
import { allBoardsSearchVar } from '/client/lib/allBoardsView';
import { allBoardsSearchVar, allBoardsMenuVar } from '/client/lib/allBoardsView';
import {
allBoardsSidebarView,
isAllBoardsSidebarOpen,
@@ -20,8 +24,8 @@ import {
import { selectedStarTitleKey, selectedStarAction } from '/models/lib/selectedStars';
// The All Boards right sidebar. Its shell mirrors the board sidebar's; what it
// shows is this page's own. docs/Design/Page/Search.md,
// docs/Design/Page/Multi-Selection.md
// shows is this page's own. docs/Features/Page/Search.md,
// docs/Features/Page/Multi-Selection.md
Template.allBoardsSidebar.helpers({
isSidebarOpen() {
@@ -53,6 +57,9 @@ Template.allBoardsSidebar.helpers({
Template.allBoardsSidebar.events({
'click .js-close-all-boards-sidebar'(evt) {
evt.preventDefault();
// Selection mode deliberately keeps this sidebar visible, so its X must
// turn the mode off before closeAllBoardsSidebar() will accept the close.
if (BoardMultiSelection.isActive()) BoardMultiSelection.disable();
closeAllBoardsSidebar();
},
'click .js-all-boards-sidebar-home'(evt) {
@@ -62,7 +69,7 @@ Template.allBoardsSidebar.events({
});
Template.allBoardsHomeSidebar.helpers({
canModifyBoards() {
canMultiSelectBoards() {
const currentUser = ReactiveCache.getCurrentUser();
return currentUser && !currentUser.isCommentOnly();
},
@@ -85,7 +92,7 @@ Template.allBoardsHomeSidebar.events({
evt.preventDefault();
closeAllBoardsSidebar();
// The SECTION of this page, not the full-width page it replaced.
// docs/Design/Page/Archive.md
// docs/Features/Page/Archive.md
FlowRouter.go(allBoardsPath(SECTION_ARCHIVE, []));
},
});
@@ -137,36 +144,77 @@ function currentSelectedStarAction() {
}
Template.allBoardsMultiSelectionSidebar.helpers({
hasBoardsSelected() {
return BoardMultiSelection.count() > 0;
},
selectedStarTitle() {
return TAPi18n.__(selectedStarTitleKey(currentSelectedStarAction().action));
},
isArchiveSelection() {
return allBoardsMenuVar.get() === SECTION_ARCHIVE;
},
isHomeSelection() {
return allBoardsMenuVar.get() === SECTION_HOME;
},
canPermanentlyDeleteArchivedBoards() {
const user = ReactiveCache.getCurrentUser();
const setting = ReactiveCache.getCurrentSetting();
return allBoardsMenuVar.get() === SECTION_ARCHIVE
&& user?.isAdmin === true
&& !!(setting && setting.enablePermanentDelete);
},
});
function selectedBoardIdsOrWarn() {
const ids = BoardMultiSelection.getSelectedBoardIds();
if (!ids.length) {
alert(TAPi18n.__('no-boards-selected'));
return null;
}
return ids;
}
function selectedHomeBoardIdOrWarn() {
const ids = BoardMultiSelection.getSelectedBoardIds();
if (ids.length !== 1) {
alert(TAPi18n.__('select-only-one-board'));
return null;
}
return ids[0];
}
Template.allBoardsMultiSelectionSidebar.events({
'click .js-delete-selected-boards'(evt) {
evt.preventDefault();
const ids = selectedBoardIdsOrWarn();
if (!ids || !confirm(TAPi18n.__('delete-board-confirm-popup'))) return;
Meteor.call('permanentlyDeleteArchivedBoards', ids, (err) => {
if (err) {
alert(err.reason || err.message || 'Failed to permanently delete boards');
return;
}
BoardMultiSelection.reset();
});
},
// Only the boards that must CHANGE are toggled: toggleBoardStar flips one
// board, so calling it for an already-starred one in the mixed case would
// un-star it.
'click .js-star-selected'(evt) {
evt.preventDefault();
const ids = selectedBoardIdsOrWarn();
if (!ids) return;
currentSelectedStarAction().boardIds.forEach((id) => {
Meteor.call('toggleBoardStar', id);
});
},
'click .js-home-selected'(evt) {
evt.preventDefault();
const ids = BoardMultiSelection.getSelectedBoardIds();
if (ids.length) {
Meteor.call('toggleDefaultBoard', ids[0]);
}
const boardId = selectedHomeBoardIdOrWarn();
if (!boardId) return;
Meteor.call('toggleDefaultBoard', boardId);
},
'click .js-archive-selected-boards'(evt) {
evt.preventDefault();
const selectedBoards = BoardMultiSelection.getSelectedBoardIds();
const selectedBoards = selectedBoardIdsOrWarn();
if (!selectedBoards) return;
if (
selectedBoards.length > 0 &&
confirm(TAPi18n.__('archive-board-confirm'))
) {
selectedBoards.forEach((boardId) => {
@@ -179,9 +227,9 @@ Template.allBoardsMultiSelectionSidebar.events({
},
'click .js-duplicate-selected-boards'(evt) {
evt.preventDefault();
const selectedBoards = BoardMultiSelection.getSelectedBoardIds();
const selectedBoards = selectedBoardIdsOrWarn();
if (!selectedBoards) return;
if (
selectedBoards.length > 0 &&
confirm(TAPi18n.__('duplicate-board-confirm'))
) {
selectedBoards.forEach((boardId) => {
@@ -207,5 +255,6 @@ Template.allBoardsMultiSelectionSidebar.events({
'click .js-multiselection-reset'(evt) {
evt.preventDefault();
BoardMultiSelection.disable();
closeAllBoardsSidebar();
},
});
@@ -11,9 +11,6 @@ template(name="archivedBoards")
each archivedBoards
li.archived-lists-item
div.board-header-btns
button.primary.board-header-btn.js-delete-board(type="submit")
i.fa.fa-trash
| {{_ 'delete-board'}}
button.primary.board-header-btn.js-restore-board(type="submit")
i.fa.fa-undo
| {{_ 'restore-board'}}
@@ -21,7 +18,3 @@ template(name="archivedBoards")
span {{ displayDate archivedAt 'LLL' }}
else
li.no-items-message {{_ 'no-archived-boards'}}
template(name="boardDeletePopup")
p {{_ 'delete-board-confirm-popup'}}
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
+1 -15
View File
@@ -1,10 +1,9 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import Boards from '/models/boards';
import { Utils } from '/client/lib/utils';
import { TABLE_PAGE_ROWS_PER_PAGE } from '/models/lib/tablePage';
// The app's one rows-per-page (docs/Design/Page/Table.md): the archive pages
// The app's one rows-per-page (docs/Features/Page/Table.md): the archive pages
// like every other paginated page, whatever pager it draws.
const ARCHIVED_BOARDS_PER_PAGE = TABLE_PAGE_ROWS_PER_PAGE;
@@ -80,17 +79,4 @@ Template.archivedBoards.events({
await board.restore();
Utils.goBoardId(board._id);
},
'click .js-delete-board': Popup.afterConfirm('boardDelete', async function() {
Popup.back();
const isSandstorm =
Meteor.settings &&
Meteor.settings.public &&
Meteor.settings.public.sandstorm;
if (isSandstorm && Utils.getCurrentBoardId()) {
const currentBoard = Utils.getCurrentBoard();
await Boards.removeAsync(currentBoard._id);
}
await Boards.removeAsync(this._id);
FlowRouter.go('home');
}),
});
+2 -2
View File
@@ -578,7 +578,7 @@ body.desktop-mode .board-wrapper .board-canvas .board-overlay {
justify-content: center;
align-items: center;
margin: 0;
font-size: 18px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
}
.modal-footer {
display: flex;
@@ -594,7 +594,7 @@ body.desktop-mode .board-wrapper .board-canvas .board-overlay {
position: absolute;
top: 5px;
inset-inline-end: 5px;
font-size: 25px;
font-size: calc(25px * var(--wekan-ui-font-scale, 1));
cursor: pointer;
}
+22 -24
View File
@@ -21,6 +21,7 @@ import { EscapeActions } from '/client/lib/escapeActions';
import { Utils } from '/client/lib/utils';
import { Filter } from '/client/lib/filter';
import { migrationProgressManager } from '/client/components/settings/migrationProgress';
import { focusFirstControl } from '/client/lib/accessibility';
// SubsManager removed for Meteor 3 migration
const { calculateIndex } = Utils;
@@ -117,12 +118,23 @@ Template.board.onCreated(function () {
// Pattern: https://kadira.io/academy/meteor-routing-guide/content/subscriptions-and-data-management/using-subs-manager
this.autorun(() => {
const currentBoardId = Session.get('currentBoard');
// Creating a linked card changes the set of source cards, boards and field
// definitions that the composite publication must expose. Reading this
// generation lets the link dialog restart the subscription immediately;
// otherwise those child cursors are only recalculated after a page reload.
const subscriptionGeneration =
Session.get('boardSubscriptionGeneration') || 0;
if (!currentBoardId) {
this.isBoardReady.set(false);
return;
}
const handle = Meteor.subscribe('board', currentBoardId, false);
const handle = Meteor.subscribe(
'board',
currentBoardId,
false,
subscriptionGeneration,
);
// Learn this board's card-loading mode (lazy vs eager) so 'auto' can decide
// per board by size; the flag drives isLazyCards(boardId). #6480.
Meteor.subscribe('boardCardsLoadingMode', currentBoardId);
@@ -302,43 +314,27 @@ Template.boardBody.onRendered(function () {
document.body.classList.add('iphone-device');
}
// Accessibility: Focus management for popups and menus
function focusFirstInteractive(container) {
if (!container) return;
// Find first focusable element
const focusable = container.querySelectorAll(
'button, [role="button"], a[href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
);
for (let i = 0; i < focusable.length; i++) {
if (!focusable[i].disabled && focusable[i].offsetParent !== null) {
focusable[i].focus();
break;
}
}
}
// Observe for new popups/menus and set focus (but exclude swimlane content)
const popupObserver = new MutationObserver(function (mutations) {
// Popups and modals use their shared focus lifecycle. Board-only menus that
// are inserted outside that lifecycle still need their first control focused.
this._accessibilityMenuObserver = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
mutation.addedNodes.forEach(function (node) {
if (
node.nodeType === 1 &&
(node.classList.contains('popup') ||
node.classList.contains('modal') ||
node.classList.contains('menu')) &&
node.classList.contains('menu') &&
!node.closest('.js-swimlanes') &&
!node.closest('.swimlane') &&
!node.closest('.list') &&
!node.closest('.minicard')
) {
setTimeout(function () {
focusFirstInteractive(node);
focusFirstControl(node);
}, 10);
}
});
});
});
popupObserver.observe(document.body, { childList: true, subtree: true });
this._accessibilityMenuObserver.observe(document.body, { childList: true, subtree: true });
// Remove tabindex from non-interactive elements (e.g., user abbreviations, labels)
document
@@ -455,7 +451,7 @@ Template.boardBody.onRendered(function () {
outline: none;
}
/* Sidebar hamburger menu button in header. Renamed with the button when
it moved to the first top header bar: docs/Design/Page/Header.md */
it moved to the first top header bar: docs/Features/Page/Header.md */
.js-toggle-page-sidebar .fa-bars {
color: #fff !important;
}
@@ -680,6 +676,8 @@ Template.boardBody.onRendered(function () {
});
Template.boardBody.onDestroyed(function () {
this._accessibilityMenuObserver?.disconnect();
this._accessibilityMenuObserver = null;
// The reveal watcher owns a Tracker computation and a retry interval, and
// neither stops on its own when this template goes.
if (this.boardItemReveals) {
File diff suppressed because it is too large. Load diff
+50 -50
View File
@@ -41,18 +41,18 @@
}
.mobile-mode .minicard {
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
padding: 11px 21px 5px;
min-height: 24px;
}
.mobile-mode .list-header-name {
font-size: 18px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
}
.mobile-mode .board-header-btn {
padding: 9px 21px;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
}
/* Comprehensive Mobile Mode Styles - Works on all screen sizes */
@@ -111,7 +111,7 @@
width: 100% !important;
min-width: 100% !important;
max-width: 100% !important;
font-size: 18px !important;
font-size: calc(18px * var(--wekan-ui-font-scale, 1)) !important;
padding: 1rem !important;
/* No margin under the header bar: it is a fixed band of the swimlane's lighter
grey between the bar and the first list, with nothing in it. */
@@ -150,7 +150,7 @@
min-width: 100% !important;
max-width: 100% !important;
padding: 1rem !important;
font-size: 18px !important;
font-size: calc(18px * var(--wekan-ui-font-scale, 1)) !important;
display: grid !important;
/* title | caret | add-list + | drag handle. The grid follows the INLINE axis, so
under dir="rtl" column 1 is on the right and the whole row mirrors by itself. */
@@ -174,7 +174,7 @@
}
.mobile-mode .list-header .list-header-name {
font-size: 18px !important;
font-size: calc(18px * var(--wekan-ui-font-scale, 1)) !important;
font-weight: bold !important;
grid-row: 1 !important;
grid-column: 2 !important;
@@ -185,7 +185,7 @@
}
.mobile-mode .list-header .cardCount {
font-size: 14px !important;
font-size: calc(14px * var(--wekan-ui-font-scale, 1)) !important;
grid-row: 2 !important;
grid-column: 2 !important;
align-self: center !important;
@@ -202,7 +202,7 @@
grid-row: 1/3 !important;
grid-column: 3 !important;
padding: 14px !important;
font-size: 24px !important;
font-size: calc(24px * var(--wekan-ui-font-scale, 1)) !important;
text-align: center !important;
display: block !important;
visibility: visible !important;
@@ -215,7 +215,7 @@
grid-row: 1/3 !important;
grid-column: 4 !important;
padding: 14px !important;
font-size: 24px !important;
font-size: calc(24px * var(--wekan-ui-font-scale, 1)) !important;
text-align: center !important;
display: block !important;
}
@@ -228,7 +228,7 @@
grid-row: 1/3 !important;
grid-column: 5 !important;
padding: 14px !important;
font-size: 28px !important;
font-size: calc(28px * var(--wekan-ui-font-scale, 1)) !important;
text-align: center !important;
display: block !important;
visibility: visible !important;
@@ -247,7 +247,7 @@
width: 100% !important;
min-width: 100% !important;
max-width: 100% !important;
font-size: 16px !important;
font-size: calc(16px * var(--wekan-ui-font-scale, 1)) !important;
padding: 11px 21px 5px !important;
min-height: 24px !important;
margin-bottom: 0.5rem !important;
@@ -256,16 +256,16 @@
}
.mobile-mode .minicard .minicard-title {
font-size: 16px !important;
font-size: calc(16px * var(--wekan-ui-font-scale, 1)) !important;
font-weight: bold !important;
}
.mobile-mode .minicard .minicard-members {
font-size: 12px !important;
font-size: calc(12px * var(--wekan-ui-font-scale, 1)) !important;
}
.mobile-mode .minicard .minicard-lists {
font-size: 12px !important;
font-size: calc(12px * var(--wekan-ui-font-scale, 1)) !important;
}
/* Desktop Mode Styles */
@@ -293,12 +293,12 @@
width: auto !important;
min-width: auto !important;
padding: 23px 21px 5px !important;
font-size: 14px !important;
font-size: calc(14px * var(--wekan-ui-font-scale, 1)) !important;
display: block !important;
}
.desktop-mode .list-header .list-header-name {
font-size: 14px !important;
font-size: calc(14px * var(--wekan-ui-font-scale, 1)) !important;
display: inline !important;
grid-row: auto !important;
grid-column: auto !important;
@@ -306,7 +306,7 @@
}
.desktop-mode .list-header .cardCount {
font-size: 12px !important;
font-size: calc(12px * var(--wekan-ui-font-scale, 1)) !important;
grid-row: auto !important;
grid-column: auto !important;
align-self: auto !important;
@@ -320,7 +320,7 @@
grid-row: auto !important;
grid-column: auto !important;
padding: 14px !important;
font-size: 40px !important;
font-size: calc(40px * var(--wekan-ui-font-scale, 1)) !important;
}
.desktop-mode .list-header .list-header-handle {
@@ -331,7 +331,7 @@
grid-row: auto !important;
grid-column: auto !important;
padding: 7px !important;
font-size: 16px !important;
font-size: calc(16px * var(--wekan-ui-font-scale, 1)) !important;
}
.desktop-mode .list-body {
@@ -343,27 +343,27 @@
.desktop-mode .minicard {
width: auto !important;
min-width: auto !important;
font-size: 12px !important;
font-size: calc(12px * var(--wekan-ui-font-scale, 1)) !important;
padding: 5px 11px !important;
min-height: auto !important;
margin-bottom: 9px !important;
}
.desktop-mode .minicard .minicard-title {
font-size: 12px !important;
font-size: calc(12px * var(--wekan-ui-font-scale, 1)) !important;
}
.desktop-mode .minicard .minicard-members {
font-size: 10px !important;
font-size: calc(10px * var(--wekan-ui-font-scale, 1)) !important;
}
.desktop-mode .minicard .minicard-lists {
font-size: 10px !important;
font-size: calc(10px * var(--wekan-ui-font-scale, 1)) !important;
}
/* Additional Mobile Mode Styles for Other Elements - Works on all screen sizes */
.mobile-mode .swimlane-header .swimlane-title {
font-size: 18px !important;
font-size: calc(18px * var(--wekan-ui-font-scale, 1)) !important;
font-weight: bold !important;
display: block !important;
visibility: visible !important;
@@ -371,21 +371,21 @@
}
.mobile-mode .swimlane-header .swimlane-description {
font-size: 14px !important;
font-size: calc(14px * var(--wekan-ui-font-scale, 1)) !important;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
}
.mobile-mode .board-header {
font-size: 18px !important;
font-size: calc(18px * var(--wekan-ui-font-scale, 1)) !important;
padding: 1rem !important;
width: 100% !important;
max-width: 100% !important;
}
.mobile-mode .board-header .board-header-title {
font-size: 18px !important;
font-size: calc(18px * var(--wekan-ui-font-scale, 1)) !important;
font-weight: bold !important;
display: block !important;
visibility: visible !important;
@@ -393,14 +393,14 @@
}
.mobile-mode .board-header .board-header-description {
font-size: 14px !important;
font-size: calc(14px * var(--wekan-ui-font-scale, 1)) !important;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
}
.mobile-mode .board-header .board-header-btn {
font-size: 14px !important;
font-size: calc(14px * var(--wekan-ui-font-scale, 1)) !important;
padding: 9px 21px !important;
display: inline-block !important;
visibility: visible !important;
@@ -408,7 +408,7 @@
}
.mobile-mode .board-header .board-header-btn i {
font-size: 14px !important;
font-size: calc(14px * var(--wekan-ui-font-scale, 1)) !important;
display: inline !important;
visibility: visible !important;
opacity: 1 !important;
@@ -430,7 +430,7 @@
grid-row: 1/3 !important;
grid-column: 3 !important;
padding: 14px !important;
font-size: 24px !important;
font-size: calc(24px * var(--wekan-ui-font-scale, 1)) !important;
text-align: center !important;
}
@@ -438,7 +438,7 @@
grid-row: 1/3 !important;
grid-column: 4 !important;
padding: 14px !important;
font-size: 28px !important;
font-size: calc(28px * var(--wekan-ui-font-scale, 1)) !important;
text-align: center !important;
}
@@ -513,7 +513,7 @@
min-width: 100% !important;
margin: 0 !important;
padding: 1rem !important;
font-size: 18px !important;
font-size: calc(18px * var(--wekan-ui-font-scale, 1)) !important;
font-weight: bold !important;
border-bottom: 2px solid #ccc !important;
}
@@ -819,12 +819,12 @@
.mobile-mode .board-header .board-header-btn {
height: 32px !important;
line-height: 32px !important;
font-size: 15px !important;
line-height: calc(32px * var(--wekan-ui-font-scale, 1)) !important;
font-size: calc(15px * var(--wekan-ui-font-scale, 1)) !important;
}
.mobile-mode .board-header .board-header-btn i.fa {
line-height: 32px !important;
line-height: calc(32px * var(--wekan-ui-font-scale, 1)) !important;
}
/* Copy mobile media query styles to mobile mode for consistent appearance */
@@ -844,8 +844,8 @@
.mobile-mode .board-header .board-header-btn {
height: 32px !important;
line-height: 32px !important;
font-size: 15px !important;
line-height: calc(32px * var(--wekan-ui-font-scale, 1)) !important;
font-size: calc(15px * var(--wekan-ui-font-scale, 1)) !important;
margin: 0 2px !important;
padding: 4px 8px !important;
border-radius: 4px !important;
@@ -866,8 +866,8 @@
}
.mobile-mode .board-header .board-header-btn i.fa {
line-height: 32px !important;
font-size: 15px !important;
line-height: calc(32px * var(--wekan-ui-font-scale, 1)) !important;
font-size: calc(15px * var(--wekan-ui-font-scale, 1)) !important;
margin: 0 !important;
padding: 0 !important;
}
@@ -978,7 +978,7 @@ body.board-view.mobile-mode #header #header-main-bar .board-header-sidebar-toggl
margin: 0 2px;
min-width: 44px;
height: 44px;
line-height: 44px;
line-height: calc(44px * var(--wekan-ui-font-scale, 1));
display: inline-flex;
align-items: center;
justify-content: center;
@@ -1003,33 +1003,33 @@ body.board-view.mobile-mode #header #header-main-bar .board-header-sidebar-toggl
/* Additional Desktop Mode Styles for Other Elements */
.desktop-mode .swimlane-header .swimlane-title {
font-size: 14px !important;
font-size: calc(14px * var(--wekan-ui-font-scale, 1)) !important;
}
.desktop-mode .swimlane-header .swimlane-description {
font-size: 12px !important;
font-size: calc(12px * var(--wekan-ui-font-scale, 1)) !important;
}
.desktop-mode .board-header {
font-size: 14px !important;
font-size: calc(14px * var(--wekan-ui-font-scale, 1)) !important;
padding: 23px 21px 5px !important;
}
.desktop-mode .board-header .board-header-title {
font-size: 14px !important;
font-size: calc(14px * var(--wekan-ui-font-scale, 1)) !important;
}
.desktop-mode .board-header .board-header-description {
font-size: 12px !important;
font-size: calc(12px * var(--wekan-ui-font-scale, 1)) !important;
}
.desktop-mode .board-header .board-header-btn {
font-size: 12px !important;
font-size: calc(12px * var(--wekan-ui-font-scale, 1)) !important;
padding: 5px 11px !important;
}
.desktop-mode .board-header .board-header-btn i {
font-size: 12px !important;
font-size: calc(12px * var(--wekan-ui-font-scale, 1)) !important;
}
/* #6419: make the board-header action buttons touch-friendly by VIEWPORT WIDTH,
@@ -1042,9 +1042,9 @@ body.board-view.mobile-mode #header #header-main-bar .board-header-sidebar-toggl
min-height: 40px;
padding-top: 8px;
padding-bottom: 8px;
font-size: 15px;
font-size: calc(15px * var(--wekan-ui-font-scale, 1));
}
.board-header-btns .board-header-btn i.fa {
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
}
}
+93 -147
View File
@@ -1,13 +1,7 @@
//- The pencil that renames the board, beside the board's name in the FIRST
//- header bar. The name is there, so the thing that edits it belongs next to it
//- rather than in a bar of its own. Board admins only; everybody else sees the
//- name without a pencil. docs/Design/Page/Header.md
template(name="boardEditTitleButton")
if currentUser.isBoardAdmin
with currentBoard
a.board-header-btn.js-edit-board-title(title="{{_ 'edit'}}" value=title)
i.fa.fa-pencil-square-o
//- There is no pencil beside the board's name any more: the NAME renames the
//- board. It is one target instead of a word and an icon that did the same
//- thing, and the icon was the smaller of the two. client/components/main/header.jade
//-
//- Everything else the board's second header bar held, as icons in the FIRST
//- bar, to the left of the notification bell.
//-
@@ -17,74 +11,86 @@ template(name="boardEditTitleButton")
//-
//- There is no desktop/mobile split any more either. The second bar wrote the
//- whole set out TWICE, once for each, because a labelled row does not fit on a
//- phone; icons do, so one set serves both. docs/Design/Page/Header.md
//- phone; icons do, so one set serves both. docs/Features/Page/Header.md
template(name="boardHeaderButtons")
if currentBoard
if currentUser
.board-header-btns.board-header-controls
with currentBoard
a.board-header-btn(
class="{{#if currentUser.isBoardAdmin}}js-change-visibility{{else}}is-disabled{{/if}}"
title="{{_ currentBoard.permission}}")
i.fa(class="{{#if currentBoard.isPublic}}fa-globe{{else}}fa-lock{{/if}}")
//- The SAME expression the tooltip uses, so the two cannot say
different things - and both follow the board from Private to
Public without a second list of words to keep in step.
span.board-header-btn-label {{_ currentBoard.permission}}
//- The board's controls fold into one caret at the leading edge of the
group - left of the lock in a left-to-right page, right of it in Arabic,
because the caret's direction comes from the same rule as every other
one. Folded, the seven controls after it are gone; open, they are back.
The buttons a board's own Rules put in this bar are NOT folded away with
them: somebody added those to this board on purpose.
client/lib/foldState.js
a.board-header-btn.js-toggle-fold(data-fold="board-controls"
title="{{#if isFoldOpen 'board-controls'}}{{_ 'collapse'}}{{else}}{{_ 'uncollapse'}}{{/if}}" role="button" tabindex="0")
i.fa(class="{{foldCaret 'board-controls'}}")
if isFoldOpen 'board-controls'
with currentBoard
a.board-header-btn(
class="{{#if currentUser.isBoardAdmin}}js-change-visibility{{else}}is-disabled{{/if}}"
title="{{_ currentBoard.permission}}")
i.fa(class="{{#if currentBoard.isPublic}}fa-globe{{else}}fa-lock{{/if}}")
//- The SAME expression the tooltip uses, so the two cannot say
different things - and both follow the board from Private to
Public without a second list of words to keep in step.
span.board-header-btn-label {{_ currentBoard.permission}}
unless watchDisabled
a.board-header-btn.js-watch-board(title="{{_ watchLevel }}")
if $eq watchLevel "watching"
i.fa.fa-eye
if $eq watchLevel "tracking"
i.fa.fa-bell
if $eq watchLevel "muted"
i.fa.fa-bell-slash
span.board-header-btn-label {{_ watchLevel }}
unless watchDisabled
a.board-header-btn.js-watch-board(title="{{_ watchLevel }}")
if $eq watchLevel "watching"
i.fa.fa-eye
if $eq watchLevel "tracking"
i.fa.fa-bell
if $eq watchLevel "muted"
i.fa.fa-bell-slash
span.board-header-btn-label {{_ watchLevel }}
//- The name beside the icon where the bar has room for it, dropped
when it does not - one rule in header.css hides every label of this
bar below the width they stop fitting at.
docs/Design/Page/Header.md
a.board-header-btn(title="{{#if isSortActive}}{{_ 'sort-is-on'}}{{else}}{{_ 'sort-cards'}}{{/if}}" class="js-sort-cards {{#if isSortActive }}emphasis{{/if}}")
i.fa.fa-sort
span.board-header-btn-label {{#if isSortActive}}{{_ 'sort-is-on'}}{{else}}{{_ 'sort-cards'}}{{/if}}
if isSortActive
a.board-header-btn-close.js-sort-reset(title="{{_ 'remove-sort'}}")
i.fa.fa-times-thin
//- The name beside the icon where the bar has room for it, dropped
when it does not - one rule in header.css hides every label of this
bar below the width they stop fitting at.
docs/Features/Page/Header.md
a.board-header-btn(title="{{#if isSortActive}}{{_ 'sort-is-on'}}{{else}}{{_ 'sort-cards'}}{{/if}}" class="js-sort-cards {{#if isSortActive }}emphasis{{/if}}")
i.fa.fa-sort
span.board-header-btn-label {{#if isSortActive}}{{_ 'sort-is-on'}}{{else}}{{_ 'sort-cards'}}{{/if}}
if isSortActive
a.board-header-btn-close.js-sort-reset(title="{{_ 'remove-sort'}}")
i.fa.fa-times-thin
+boardButtons
a.board-header-btn.js-open-filter-view(
title="{{#if Filter.isActive}}{{_ 'filter-on-desc'}}{{else}}{{_ 'filter'}}{{/if}}"
class="{{#if Filter.isActive}}js-filter-active{{/if}}")
i.fa.fa-filter
span.board-header-btn-label {{#if Filter.isActive}}{{_ 'filter-on-desc'}}{{else}}{{_ 'filter'}}{{/if}}
if Filter.isActive
a.board-header-btn-close.js-filter-reset(title="{{_ 'filter-clear'}}")
i.fa.fa-times-thin
a.board-header-btn.js-open-search-view(title="{{_ 'search'}}")
i.fa.fa-search
span.board-header-btn-label {{_ 'search'}}
a.board-header-btn.js-toggle-dependencies(
title="{{#if currentBoard.showDependencies}}{{_ 'hide-dependencies'}}{{else}}{{_ 'show-dependencies'}}{{/if}}"
class="{{#if currentBoard.showDependencies}}emphasis{{/if}}")
i.fa.fa-link
span.board-header-btn-label {{#if currentBoard.showDependencies}}{{_ 'hide-dependencies'}}{{else}}{{_ 'show-dependencies'}}{{/if}}
if canModifyBoard
a.board-header-btn.js-multiselection-activate(
title="{{#if MultiSelection.isActive}}{{_ 'multi-selection-on'}}{{else}}{{_ 'multi-selection'}}{{/if}}"
class="{{#if MultiSelection.isActive}}emphasis{{/if}}")
i.fa.fa-check-square-o
//- Named, like the board's Multi-Selection on All Boards: a
check-box outline says nothing about what the button does.
span.board-header-btn-label {{_ 'multi-selection'}}
if MultiSelection.isActive
a.board-header-btn-close.js-multiselection-reset(title="{{_ 'multi-selection-off'}}")
if isFoldOpen 'board-controls'
a.board-header-btn.js-open-filter-view(
title="{{#if Filter.isActive}}{{_ 'filter-on-desc'}}{{else}}{{_ 'filter'}}{{/if}}"
class="{{#if Filter.isActive}}js-filter-active{{/if}}")
i.fa.fa-filter
span.board-header-btn-label {{#if Filter.isActive}}{{_ 'filter-on-desc'}}{{else}}{{_ 'filter'}}{{/if}}
if Filter.isActive
a.board-header-btn-close.js-filter-reset(title="{{_ 'filter-clear'}}")
i.fa.fa-times-thin
a.board-header-btn.js-open-search-view(title="{{_ 'search'}}")
i.fa.fa-search
span.board-header-btn-label {{_ 'search'}}
a.board-header-btn.js-toggle-dependencies(
title="{{#if currentBoard.showDependencies}}{{_ 'hide-dependencies'}}{{else}}{{_ 'show-dependencies'}}{{/if}}"
class="{{#if currentBoard.showDependencies}}emphasis{{/if}}")
i.fa.fa-link
span.board-header-btn-label {{#if currentBoard.showDependencies}}{{_ 'hide-dependencies'}}{{else}}{{_ 'show-dependencies'}}{{/if}}
if canModifyBoard
a.board-header-btn.js-multiselection-activate(
title="{{#if MultiSelection.isActive}}{{_ 'multi-selection-on'}}{{else}}{{_ 'multi-selection'}}{{/if}}"
class="{{#if MultiSelection.isActive}}emphasis{{/if}}")
i.fa.fa-check-square-o
//- Named, like the board's Multi-Selection on All Boards: a
check-box outline says nothing about what the button does.
span.board-header-btn-label {{_ 'multi-selection'}}
if MultiSelection.isActive
a.board-header-btn-close.js-multiselection-reset(title="{{_ 'multi-selection-off'}}")
i.fa.fa-times-thin
else
a.board-header-btn.js-log-in(title="{{_ 'log-in'}}")
i.fa.fa-sign-in
@@ -187,7 +193,16 @@ template(name="boardChangeViewPopup")
if $eq Utils.boardView "board-view-stats"
i.fa.fa-check
template(name="createBoard")
//- The Create Board form: a title, what the board is visible to, and Create.
Four templates drew it letter for letter - the one on the All Boards page and
three popups, one of which creates a TEMPLATE board and says so with a Session
flag rather than with different markup.
//-
//- The two things it shows about visibility are passed IN. The state itself
stays with each of the four owners and is passed in explicitly. Blaze scopes
events to this included template, so this form owns the handlers.
client/components/boards/boardHeader.js
template(name="createBoardForm")
form
label
| {{_ 'title'}}
@@ -214,87 +229,18 @@ template(name="createBoard")
| /
a.js-board-template {{_ 'template'}}
template(name="createBoard")
+createBoardForm(owner=createBoardOwner visibility=visibility visibilityMenuIsOpen=visibilityMenuIsOpen)
template(name="createBoardPopup")
form
label
| {{_ 'title'}}
input.js-new-board-title(type="text" placeholder="{{_ 'bucket-example'}}" autofocus required)
if visibilityMenuIsOpen
+boardVisibilityList
else
p.quiet
if $eq visibility 'public'
span.fa.fa-globe.colorful
= " "
| {{{_ 'board-public-info'}}}
else
span.fa.fa-lock.colorful
= " "
| {{{_ 'board-private-info'}}}
a.js-change-visibility {{_ 'change'}}.
input.primary.wide(type="submit" value="{{_ 'create'}}")
unless importDisabled
span.quiet
| {{_ 'or'}}
a.js-import-board {{_ 'import'}}
span.quiet
| /
a.js-board-template {{_ 'template'}}
+createBoardForm(owner=createBoardOwner visibility=visibility visibilityMenuIsOpen=visibilityMenuIsOpen)
template(name="headerBarCreateBoardPopup")
form
label
| {{_ 'title'}}
input.js-new-board-title(type="text" placeholder="{{_ 'bucket-example'}}" autofocus required)
if visibilityMenuIsOpen
+boardVisibilityList
else
p.quiet
if $eq visibility 'public'
span.fa.fa-globe.colorful
= " "
| {{{_ 'board-public-info'}}}
else
span.fa.fa-lock.colorful
= " "
| {{{_ 'board-private-info'}}}
a.js-change-visibility {{_ 'change'}}.
input.primary.wide(type="submit" value="{{_ 'create'}}")
unless importDisabled
span.quiet
| {{_ 'or'}}
a.js-import-board {{_ 'import'}}
span.quiet
| /
a.js-board-template {{_ 'template'}}
+createBoardForm(owner=createBoardOwner visibility=visibility visibilityMenuIsOpen=visibilityMenuIsOpen starAfterCreate=true)
// New popup for Template Container creation; shares the same form content
template(name="createTemplateContainerPopup")
form
label
| {{_ 'title'}}
input.js-new-board-title(type="text" placeholder="{{_ 'bucket-example'}}" autofocus required)
if visibilityMenuIsOpen
+boardVisibilityList
else
p.quiet
if $eq visibility 'public'
span.fa.fa-globe.colorful
= " "
| {{{_ 'board-public-info'}}}
else
span.fa.fa-lock.colorful
= " "
| {{{_ 'board-private-info'}}}
a.js-change-visibility {{_ 'change'}}.
input.primary.wide(type="submit" value="{{_ 'create'}}")
unless importDisabled
span.quiet
| {{_ 'or'}}
a.js-import-board {{_ 'import'}}
span.quiet
| /
a.js-board-template {{_ 'template'}}
+createBoardForm(owner=createBoardOwner visibility=visibility visibilityMenuIsOpen=visibilityMenuIsOpen)
//template(name="listsortPopup")
//
@@ -366,7 +312,7 @@ template(name="cardsSortPopup")
//- always on screen - the same place the All Boards Lists/Table menu is.
//-
//- `unless currentBoard.isTemplatesBoard`: there is nothing to look at six
//- ways on a templates board. docs/Design/Page/Header.md
//- ways on a templates board. docs/Features/Page/Header.md
//- Whether THIS board is starred, and the button that changes it. Its own
//- template because the first header bar places it beside the starred-boards
//- dropdown rather than among the board's other controls: the two are one pair -
@@ -374,7 +320,7 @@ template(name="cardsSortPopup")
//-
//- The outline star means not starred and the solid one means starred, so the
//- button shows its state rather than the action - the tooltip says the action.
//- docs/Design/Page/Header.md
//- docs/Features/Page/Header.md
template(name="boardStarButton")
if currentBoard
a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}"
@@ -389,7 +335,7 @@ template(name="boardViewMenu")
icons are six glyphs to learn, and a tooltip is the one place a name
cannot be read without hovering - so the button says which view is on.
The bar wraps to a second row when it runs out of width, which is what
makes a word beside the icon affordable. docs/Design/Page/Header.md
makes a word beside the icon affordable. docs/Features/Page/Header.md
a.board-header-btn.js-toggle-board-view(title="{{boardViewName}}")
i.fa.fa-caret-down
if $eq boardView 'board-view-swimlanes'
+86 -61
View File
@@ -17,6 +17,7 @@ import {
import { MultiSelection } from '/client/lib/multiSelection';
import { getSidebarInstance } from '/client/features/sidebar/service';
import { Utils } from '/client/lib/utils';
import { toggleFold } from '/client/lib/foldState';
/*
const DOWNCLS = 'fa-sort-down';
@@ -53,7 +54,7 @@ Template.boardChangeTitlePopup.events({
if (newTitle) {
// The board this popup was opened FOR, when it was opened for one: the
// All Boards Table view opens this same popup from a row whose data
// context is that row's board (docs/Design/Page/All-Boards.md), and on
// context is that row's board (docs/Features/Page/All-Boards.md), and on
// that page there is no "current board" at all. The board header opens it
// with the current board as its context, so that side is unchanged.
const context = Template.currentData();
@@ -173,12 +174,23 @@ function toggleSidebarView(view, mustStayOpen) {
}
Template.boardHeaderButtons.events({
'click .js-edit-board-title': Popup.open('boardChangeTitle'),
// The board's controls fold into the caret at the start of the group. Named
// by the words the app already has for it - Collapse and Uncollapse - rather
// than a key of its own in 147 language files. client/lib/foldState.js
'click .js-toggle-fold'(event) {
event.preventDefault();
toggleFold(event.currentTarget.dataset.fold);
},
'keydown .js-toggle-fold'(event) {
if (event.key !== 'Enter' && event.key !== ' ') return;
event.preventDefault();
toggleFold(event.currentTarget.dataset.fold);
},
'click .js-change-visibility': Popup.open('boardChangeVisibility'),
'click .js-watch-board': Popup.open('boardChangeWatch'),
// Boards in Archive is a SECTION of All Boards, not a page of its own: the
// row in its left menu. `/archive` is the full-width page that section
// replaced. docs/Design/Page/Archive.md
// replaced. docs/Features/Page/Archive.md
'click .js-open-archived-board'() {
FlowRouter.go(allBoardsPath(SECTION_ARCHIVE, []));
},
@@ -288,6 +300,9 @@ function setupCreateBoardState(tpl) {
function createBoardHelpers() {
return {
createBoardOwner() {
return Template.instance();
},
visibilityMenuIsOpen() {
return Template.instance().visibilityMenuIsOpen.get();
},
@@ -305,7 +320,9 @@ function createBoardHelpers() {
async function createBoardSubmit(tpl, event) {
event.preventDefault();
const title = tpl.find('.js-new-board-title').value;
const titleInput = event.currentTarget.querySelector('.js-new-board-title');
if (!titleInput) return;
const title = titleInput.value;
const slug = getSlug(title) || 'board';
// #5850: template boards are created via the dedicated "Add Template Board"
@@ -366,23 +383,36 @@ async function createBoardSubmit(tpl, event) {
}
}
function createBoardEvents() {
return {
'click .js-select-visibility'(event, tpl) {
tpl.visibility.set(this);
tpl.visibilityMenuIsOpen.set(false);
},
'click .js-change-visibility'(event, tpl) {
tpl.visibilityMenuIsOpen.set(!tpl.visibilityMenuIsOpen.get());
},
async 'submit'(event, tpl) {
await createBoardSubmit(tpl, event);
},
'click .js-import-board': Popup.open('chooseBoardSource'),
'click .js-board-template': Popup.open('searchElement'),
};
function createBoardOwner(tpl) {
return tpl.data && tpl.data.owner;
}
// Blaze events are scoped to the template whose rendered DOM contains the
// selector. The form is an included child template, so attaching submit to its
// parent popup leaves the Create button inert. Keep state on the parent, but
// attach the shared form's events here and explicitly pass its owner in.
Template.createBoardForm.events({
'click .js-select-visibility'(event, tpl) {
const owner = createBoardOwner(tpl);
owner.visibility.set(this);
owner.visibilityMenuIsOpen.set(false);
},
'click .js-change-visibility'(event, tpl) {
const owner = createBoardOwner(tpl);
owner.visibilityMenuIsOpen.set(!owner.visibilityMenuIsOpen.get());
},
async submit(event, tpl) {
const owner = createBoardOwner(tpl);
const starAfterCreate = tpl.data.starAfterCreate === true;
await createBoardSubmit(owner, event);
if (starAfterCreate) {
await ReactiveCache.getCurrentUser().toggleBoardStar(owner.boardId.get());
}
},
'click .js-import-board': Popup.open('chooseBoardSource'),
'click .js-board-template': Popup.open('searchElement'),
});
// createBoard (non-popup version)
Template.createBoard.onCreated(function () {
setupCreateBoardState(this);
@@ -390,8 +420,6 @@ Template.createBoard.onCreated(function () {
Template.createBoard.helpers(createBoardHelpers());
Template.createBoard.events(createBoardEvents());
// createBoardPopup
Template.createBoardPopup.onCreated(function () {
setupCreateBoardState(this);
@@ -399,8 +427,6 @@ Template.createBoardPopup.onCreated(function () {
Template.createBoardPopup.helpers(createBoardHelpers());
Template.createBoardPopup.events(createBoardEvents());
// createTemplateContainerPopup
Template.createTemplateContainerPopup.onCreated(function () {
setupCreateBoardState(this);
@@ -413,8 +439,6 @@ Template.createTemplateContainerPopup.onRendered(function () {
Template.createTemplateContainerPopup.helpers(createBoardHelpers());
Template.createTemplateContainerPopup.events(createBoardEvents());
// headerBarCreateBoardPopup
Template.headerBarCreateBoardPopup.onCreated(function () {
setupCreateBoardState(this);
@@ -422,23 +446,6 @@ Template.headerBarCreateBoardPopup.onCreated(function () {
Template.headerBarCreateBoardPopup.helpers(createBoardHelpers());
Template.headerBarCreateBoardPopup.events({
'click .js-select-visibility'(event, tpl) {
tpl.visibility.set(this);
tpl.visibilityMenuIsOpen.set(false);
},
'click .js-change-visibility'(event, tpl) {
tpl.visibilityMenuIsOpen.set(!tpl.visibilityMenuIsOpen.get());
},
async submit(event, tpl) {
await createBoardSubmit(tpl, event);
// Immediately star boards created with the headerbar popup.
await ReactiveCache.getCurrentUser().toggleBoardStar(tpl.boardId.get());
},
'click .js-import-board': Popup.open('chooseBoardSource'),
'click .js-board-template': Popup.open('searchElement'),
});
Template.boardVisibilityList.helpers({
notAllowPrivateVisibilityOnly() {
return !TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly')?.booleanValue;
@@ -484,18 +491,38 @@ Template.boardChangeWatchPopup.helpers({
Template.boardChangeWatchPopup.events({
'click .js-select-watch'() {
const level = this;
if (typeof level === 'string') {
Meteor.call(
'watch',
'board',
Session.get('currentBoard'),
level,
(err, ret) => {
if (!err && ret) Popup.back();
},
);
}
// `this` is the data context of the clicked row - the string from the
// enclosing {{#with "watching"}}. Blaze can hand that back as a boxed
// String, so read it through String() rather than refusing anything that is
// not typeof 'string'.
const level = this === null || this === undefined ? '' : String(this);
if (!level) return;
Meteor.call(
'watch',
'board',
Session.get('currentBoard'),
level,
(err, ret) => {
if (!err && ret) {
Popup.back();
return;
}
// AN ERROR MUST NOT BE SILENT. It was: the callback closed the popup on
// success and did nothing at all otherwise, so a refusal looked exactly
// like a broken button - "Silent does not respond. If we try to change
// it does not change. Nothing happens." (email, 2026-08-13). The server
// refuses for two reasons an admin can act on: the watch feature is
// turned off in the Admin Panel, and the board is not visible to this
// user.
const reason = err && (err.error || err.reason || err.message);
const message = reason === 'error-watch-disabled'
? TAPi18n.__('error-watch-disabled')
: TAPi18n.__('error-board-notAMember');
// eslint-disable-next-line no-alert
if (typeof window !== 'undefined' && window.alert) window.alert(message);
if (process.env.DEBUG === 'true') console.error('watch failed:', err);
},
);
},
});
@@ -610,7 +637,7 @@ Template.cardsSortPopup.events({
// The board's view menu is its own template in the FIRST header bar now, so its
// handler is here rather than on the second bar that used to draw it.
// docs/Design/Page/Header.md
// docs/Features/Page/Header.md
Template.boardViewMenu.events({
'click .js-toggle-board-view': Popup.open('boardChangeView'),
});
@@ -620,7 +647,7 @@ Template.boardViewMenu.helpers({
return Utils.boardView();
},
// The tooltip: the name of the view that is ON. The button is icon only, so
// this is the only place the name appears. docs/Design/Page/Header.md
// this is the only place the name appears. docs/Features/Page/Header.md
boardViewName() {
const names = {
'board-view-swimlanes': 'swimlanes',
@@ -634,8 +661,6 @@ Template.boardViewMenu.helpers({
},
});
// The pencil is its own template in the first header bar, beside the board's
// name, so its click is handled here. docs/Design/Page/Header.md
Template.boardEditTitleButton.events({
'click .js-edit-board-title': Popup.open('boardChangeTitle'),
});
// The board is renamed by clicking its NAME in the first header bar, which is
// handled where that bar is: client/components/main/header.js. The pencil that
// used to be here is gone with the template it lived in.
+320 -127
View File
@@ -45,7 +45,7 @@
/* `auto`, not a fixed 260px: the menu carries its own width (below), so the
track is whatever the menu is - and folded, when the grid drops to a single
column, there is no second track left holding a gap open.
docs/Design/Page/Left-Menu.md */
docs/Features/Page/Left-Menu.md */
grid-template-columns: auto 1fr;
/* Between the columns only. A `gap` is between tracks, not around them, so
this never insets the menu from the window edge - the padding that used to
@@ -64,12 +64,12 @@
min-height: calc(100vh - var(--wekan-header-height, 0px));
/* The width grip is positioned against the GRID: absolutely, so it is no grid
item and adds no track, and outside the menu, which scrolls its own
entries. docs/Design/Page/Left-Menu.md */
entries. docs/Features/Page/Left-Menu.md */
position: relative;
}
/* The left menu is styled like the Admin Panel's (settingBody.css,
docs/Design/Page/Left-Menu.md): a PANEL with its own background, border and
docs/Features/Page/Left-Menu.md): a PANEL with its own background, border and
rounded corners, not a column separated from the page by a hairline. WeKan
has one kind of left menu and it should look like one kind of left menu.
@@ -101,7 +101,7 @@
/* Flush on three sides, like the Admin Panel's: it starts at the window's
left edge, directly under the header, and runs to the bottom - so it has a
border and a corner only on the side that faces the page. A rounded corner
on a flush edge shows the grey behind it. docs/Design/Page/Left-Menu.md */
on a flush edge shows the grey behind it. docs/Features/Page/Left-Menu.md */
border: 1px solid #f0f0f0;
border-inline-start: 0;
border-top: 0;
@@ -154,7 +154,7 @@
background: #ddd;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
font-weight: bold;
margin-inline-start: 8px;
}
@@ -235,9 +235,9 @@
.board-list .board-list-item-bookmark .bookmark-url {
display: block;
margin-top: 4px;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
font-weight: 400;
line-height: 16px;
line-height: calc(16px * var(--wekan-ui-font-scale, 1));
opacity: 0.85;
/* An address is one string and must not be hyphenated into something that
reads like a different address. */
@@ -291,13 +291,13 @@
border: 2px dashed #d0d0d0;
border-radius: 4px;
color: #808080;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
/* Nothing to grab: this is where a drag ENDS. */
cursor: default;
transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.home-remove-target .fa {
font-size: 18px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
}
/* Under the icon. The colour is the answer to "what happens if I let go", so it
is the destructive one - and it is only ever shown while something is over
@@ -313,15 +313,26 @@
pane says what to drag rather than showing nothing at all. Sized and spaced
like a board tile so it sits where the first tile would.
docs/Features/Board/Home.md */
/* Home's "Drag only one board here to open it after login" placeholder.
It stands where a board tile will be, so it is the SIZE of a board tile:
114px like `.board-list .board-list-item` and `.js-add-board .label`, with
the dashed border folded into that height rather than added outside it. It
was 20px of padding around one line of text, which came to about 85px - a
short dashed box on a page whose whole content is that box, next to nothing
to compare it with except the tiles it is a placeholder FOR. */
.board-list-item-empty {
display: block;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
min-height: 114px;
width: 270px;
padding: 20px;
margin: 7px;
border: 2px dashed #b3b3b3;
border-radius: 4px;
color: #808080;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
line-height: 1.4;
text-align: center;
}
@@ -366,7 +377,7 @@
background: #888;
/* The rows' own inline margin, so the rule lines up with them instead of
running past their ends - the same `4px` the Admin Panel's separator uses.
docs/Design/Page/Left-Menu.md */
docs/Features/Page/Left-Menu.md */
margin: 8px 4px;
}
@@ -381,7 +392,7 @@
padding-block: 8px 4px;
padding-inline-start: 18px;
padding-inline-end: 10px;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
@@ -406,7 +417,7 @@
whatever has been dragged into whatever.
`padding-inline-start`, so a right-to-left language indents from the right by
itself rather than needing a second rule kept in step.
docs/Design/Page/Workspaces.md */
docs/Features/Page/Workspaces.md */
.workspace-tree {
list-style: none;
padding-inline-start: 10px;
@@ -435,7 +446,7 @@
text-decoration: none;
cursor: pointer;
opacity: 0.7;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
}
.workspace-collapse-indicator:hover {
opacity: 1;
@@ -455,7 +466,7 @@
drop-inside - the row lights up and the slot opens INDENTED under it, which
is where the workspace will appear: as its child.
docs/Design/Page/Workspaces.md */
docs/Features/Page/Workspaces.md */
.workspace-node.drop-before > .workspace-node-content,
.workspace-node.drop-after > .workspace-node-content,
.workspace-node.drop-inside > .workspace-node-content {
@@ -512,7 +523,7 @@
.workspace-drag-handle {
cursor: grab;
color: #999;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
padding: 0 4px;
user-select: none;
}
@@ -537,7 +548,7 @@
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
line-height: 1;
vertical-align: middle;
}
@@ -556,7 +567,7 @@
The cursor stays `pointer`, not `grab`: clicking this row is what opens the
workspace, and that is what a reader does with it nearly every time. Dragging
is the second gesture on the same element, exactly as it is on a board tile
with handles off. docs/Design/Page/All-Boards.md */
with handles off. docs/Features/Page/All-Boards.md */
.workspace-node .js-select-space[draggable="true"] {
user-select: none;
-webkit-user-select: none;
@@ -592,12 +603,12 @@
`flex: 1`, and without this a long workspace name shrinks its two neighbours
instead of ellipsing itself, which pushes the count off the row that is
supposed to be showing it. The phone rules said this already; the desktop
ones had not. docs/Design/Page/All-Boards.md */
ones had not. docs/Features/Page/All-Boards.md */
.workspace-node .workspace-count {
background: #ddd;
padding: 2px 6px;
border-radius: 10px;
font-size: 11px;
font-size: calc(11px * var(--wekan-ui-font-scale, 1));
font-weight: bold;
min-width: 20px;
text-align: center;
@@ -613,7 +624,7 @@
border-radius: 3px;
cursor: pointer;
text-decoration: none;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
opacity: 0.6;
transition: opacity 0.2s;
}
@@ -629,7 +640,7 @@
border-radius: 3px;
cursor: pointer;
text-decoration: none;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
opacity: 0.7;
transition: opacity 0.2s;
}
@@ -703,7 +714,7 @@
which boards are in it.
So it keeps the same grey pill every other row has, and the rule that made it
white is gone rather than re-tinted: there is no accent behind it to contrast
with. docs/Design/Page/All-Boards.md */
with. docs/Features/Page/All-Boards.md */
.boards-right-grid {
min-height: 200px;
@@ -724,7 +735,7 @@
out of the grid, but the TRACK it was placed in stays, so the boards would
have been left in the `auto` column with the 1fr one empty beside them. One
column, and the menu hidden by the same rule.
docs/Design/Page/Left-Menu.md */
docs/Features/Page/Left-Menu.md */
.boards-layout.collapsed {
/* `!important` because the phone rules below set the two tracks with an
`!important` of their own (#6488/#6523, so a narrow screen keeps the menu
@@ -747,7 +758,7 @@
this the title would sit directly on top of the first row of board tiles.
The board tiles carry a 7px margin of their own, so 12px of margin here plus
that 7px is the same gap the Admin Panel's 12px gives above a flush pane.
docs/Design/Page/All-Boards.md */
docs/Features/Page/All-Boards.md */
.boards-right-grid > .admin-pane-title {
margin-bottom: 12px;
/* The pager and the tiles below start at the tiles' own 7px inset, so the
@@ -766,15 +777,68 @@
min-height: 45px;
}
/* THE CONTROLS THEMSELVES. Every page's controls template - Rules, My Cards,
Due Cards, Global Search - draws its entries as `.board-header-btn`, because
that is what they were when they lived in the page's own second header bar.
Every rule for that class is scoped to `#header-quick-access` (header.css),
so in here they had NO styling at all: three bare links jammed together on
one line, icons run into words, nothing to click but the text.
They are sidebar rows now - one per line, the width of the panel, with the
icon in a fixed column so every label starts at the same x. The board's own
sidebar rows are `.sidebar-list li > a` and `.sidebar-btn`; this gives the
same shape to the class these pages actually use. */
.all-boards-sidebar .sidebar-content .board-header-btn,
.page-sidebar .sidebar-content .board-header-btn {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
box-sizing: border-box;
margin: 0 0 4px;
padding: 8px 10px;
border-radius: 3px;
color: inherit;
cursor: pointer;
}
.all-boards-sidebar .sidebar-content .board-header-btn:hover,
.page-sidebar .sidebar-content .board-header-btn:hover {
background: rgba(0, 0, 0, 0.06);
}
/* A fixed column for the icon, so a row with a caret and a row without one
still line their words up - the same reason the board sidebar gives its
checkbox glyphs a width. */
.all-boards-sidebar .sidebar-content .board-header-btn i.fa,
.page-sidebar .sidebar-content .board-header-btn i.fa {
width: 16px;
text-align: center;
flex-shrink: 0;
margin: 0;
}
/* The sidebar's own rows. `.sidebar-btn` and `.sidebar-list` come from the
board sidebar's stylesheet, which is loaded on every page, so only what is
particular to this one is here. */
.all-boards-sidebar .sidebar-multiselection-hint {
color: #666;
font-size: 13px;
font-size: calc(13px * var(--wekan-ui-font-scale, 1));
margin: 8px 0;
}
/* Archive bulk-selection belongs to the board icons it changes, immediately
above them. It is visible only while Multi-Selection is on. */
.board-multiselection-controls {
display: flex;
gap: 8px;
margin: 0 0 10px;
}
.board-multiselection-controls button {
margin: 0;
}
/* ── Where the All Boards sidebar sits, on a DESKTOP ────────────────────────
It first inherited the board sidebar's `position: absolute`, which resolves
against the nearest positioned ancestor. On a board that ancestor is the
@@ -887,9 +951,21 @@
overflow: hidden;
background-color: inherit; /* Inherit board color from parent li.js-board */
color: #f6f6f6;
min-height: 72px; /* #6465: thinner tiles so more boards fit in the overview */
font-size: 16px;
line-height: 22px;
/* The number IS the rendered height, padding and border folded in - the same
thing the mobile rules below do for the same reason (#6523).
It used to be a 72px floor with 42px of padding added outside it, which came
to the same 114px for an ordinary tile and to 122px for a BORDERED one: the
bookmarks in All Boards / Starred and the Template Container tile both carry
`border: 4px solid #fff`, and on a content-box element those 8px are added
to the height rather than taken out of it. So a starred page sat 8px taller
than the board beside it and than the grey "+ Add Board" tile, and the row
it was in inherited the extra height.
114px is what an ordinary tile already rendered at (72 + 24 + 18), so
nothing moves except the bordered tiles, which now match. */
box-sizing: border-box;
min-height: 114px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
line-height: calc(22px * var(--wekan-ui-font-scale, 1));
border-radius: 0; /* No border-radius - parent .js-board has it */
display: block;
font-weight: 700;
@@ -908,7 +984,7 @@
transform: translateX(-50%);
z-index: 3;
color: #fff;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
line-height: 1;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
pointer-events: none;
@@ -929,27 +1005,44 @@
.board-list .board-list-item-sub-name {
color: rgba(255,255,255,0.5);
display: block;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
font-weight: 400;
line-height: 22px;
line-height: calc(22px * var(--wekan-ui-font-scale, 1));
}
.board-list .board-list-item-desc {
color: #fff;
display: block;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
font-weight: 400;
line-height: 18px;
line-height: calc(18px * var(--wekan-ui-font-scale, 1));
}
/* The "+ Add Board" tile is as tall as the boards beside it.
`min-height: 114px` is a FLOOR, not a height, and a board whose title wraps to
three lines - "WeKan ® Open Source Kanban board with MIT license" - grows past
it. The list is a grid, so every other tile in that row stretches to match;
the grey tile did not, because the colour of a board tile comes from the `li`
(which the grid stretches) while the grey comes from the `.label` INSIDE it,
which kept its own 114px. So the row was 146px of board and 114px of grey,
with the "+ Add Board" box hanging short at the bottom.
A flex `li` with a growing child gives the label the row's height, whatever
the tallest title in that row turns out to be. */
.board-list .js-add-board {
text-align: center;
display: flex;
}
.board-list .js-add-board > .label {
flex: 1 1 auto;
}
.board-list .js-add-board .label {
font-weight: normal;
/* Match the board tiles (#6465 thinner tiles) so the grey "+ Add Board" /
"+ Add Template Container" box is the same height as a board icon:
same min-height, padding and line-height as .board-list-item above. */
line-height: 22px;
min-height: 72px;
line-height: calc(22px * var(--wekan-ui-font-scale, 1));
/* Border-box and the full height, like .board-list-item above: the two are
compared by eye on the same row, so they are stated the same way. */
box-sizing: border-box;
min-height: 114px;
display: flex;
align-items: center;
justify-content: center;
@@ -978,9 +1071,9 @@
.board-list .is-star-active,
.board-list .is-not-star-active {
top: 0;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
height: 18px;
line-height: 18px;
line-height: calc(18px * var(--wekan-ui-font-scale, 1));
opacity: 0;
padding: 9px 9px;
position: absolute;
@@ -990,9 +1083,9 @@
}
.board-list .fa-circle {
bottom: 0;
font-size: 10px;
font-size: calc(10px * var(--wekan-ui-font-scale, 1));
height: 10px;
line-height: 10px;
line-height: calc(10px * var(--wekan-ui-font-scale, 1));
padding: 9px 9px;
position: absolute;
inset-inline-end: 0;
@@ -1011,9 +1104,9 @@
.board-list .fa-clone {
position: absolute;
bottom: 0;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
height: 18px;
line-height: 18px;
line-height: calc(18px * var(--wekan-ui-font-scale, 1));
opacity: 0;
inset-inline-end: 0;
padding: 9px 9px;
@@ -1023,9 +1116,9 @@
.board-list .fa-archive {
position: absolute;
bottom: 0;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
height: 18px;
line-height: 18px;
line-height: calc(18px * var(--wekan-ui-font-scale, 1));
opacity: 0;
inset-inline-start: 0;
padding: 9px 9px;
@@ -1049,7 +1142,7 @@
.board-list li:hover a .fa-clone:hover,
.board-list li:hover a .fa-archive:hover,
.board-list li:hover a .is-not-star-active:hover {
font-size: 18px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
opacity: 1;
}
.board-list li:hover a .is-star-active,
@@ -1067,7 +1160,7 @@
top: 50%;
inset-inline-end: 6px;
transform: translateY(-50%);
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
color: #fff;
/* Transparent background so only the drag-handle icon shows (not a box). */
background: transparent;
@@ -1136,7 +1229,7 @@
font-family: FontAwesome;
content: "\f00c";
color: #fff;
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
font-weight: bold;
}
@@ -1183,7 +1276,7 @@
justify-content: center;
}
.board-backgrounds-list .board-background-select .background-box i.fa-check {
font-size: 25px;
font-size: calc(25px * var(--wekan-ui-font-scale, 1));
color: #3cb500;
}
/* Mobile view styles - applied when isMiniScreen is true (iPhone, etc.) */
@@ -1193,13 +1286,16 @@
(e.g. a laptop in mobile mode) gets more columns instead of two stretched ones. */
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(46%, 200px), 1fr));
gap: 8px;
/* The same single spacing value as the phone grid above, in both directions. */
gap: 10px;
/* #6488: sized by the column it lives in (the flex chain in the phone block
below), not by the viewport minus a guess at the bars above it. */
flex: 1 1 auto;
min-height: 0;
height: auto;
overflow-y: auto !important;
/* #6488: #content is the page's single vertical scroll owner. A nested
board-list scroller makes a phone swipe depend on where it starts. */
overflow-y: visible !important;
overflow-x: hidden;
padding: 0 0.5rem;
margin: 0;
@@ -1210,7 +1306,8 @@
width: auto; /* the grid controls the column width */
float: none;
display: block;
margin-bottom: 0.5rem;
/* The grid gap is the spacing; this added a second one, downwards only. */
margin-bottom: 0;
/* NO handle reserve here. The drag handle is positioned INSIDE the tile, and
the tile already keeps its text clear of it - so 32px here was a second,
invisible reserve on top of that one. On a phone the tile is only ~97px
@@ -1246,7 +1343,7 @@
top: 50%;
transform: translateY(-50%);
inset-inline-end: 6px;
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
color: #fff;
background: rgba(0,0,0,0.3);
border-radius: 50%;
@@ -1340,18 +1437,42 @@
.board-list {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
/* ONE spacing value, in both directions. It used to be an 8px gap PLUS a
`margin-bottom: 0.5rem` on each li, so the tiles were 16px apart vertically
and 8px apart horizontally - which reads as "the icons are too close
together" side to side even though there is space between the rows. The
margin is gone (below) and the gap is the whole answer. */
gap: 10px;
height: 100%;
overflow-y: auto;
overflow-y: visible !important;
overflow-x: hidden;
padding: 0 0.25rem;
margin: 0;
/* #6488. Without these a grid's rows default to `align-content: stretch` and
its items to `align-items: stretch`, so when the list is SHORTER than the
column it is given - one or two rows of boards - the leftover height is
divided among the rows and each tile is grown to fill it. A board tile
paints its board colour over the whole cell, so a single row of boards
became one band several times the height of a normal icon: "some board
icons are 4x normal height or more".
This was fixed once, but only on `.board-list.mobile-view` - the class the
template sets from Utils.isMiniScreen(). A narrow WINDOW that is not a mini
screen (a desktop browser resized under 800px, a tablet in desktop mode)
matches this media query, becomes a grid here, and never got the fix. It
belongs on the rule that creates the grid. */
align-content: start;
align-items: start;
}
.board-list li {
width: auto; /* the grid controls the column width */
float: none;
display: block;
margin-bottom: 0.5rem;
/* The grid gap spaces the tiles; a margin here spaced them a second time,
and only downwards. */
margin-bottom: 0;
/* ...and the tile is its own height, not the height of a stretched cell. */
align-self: start;
height: auto;
/* Same as `.board-list.mobile-view li` above: the tile keeps its own text
clear of the handle, so reserving the width a second time out here only
narrowed the title. */
@@ -1426,7 +1547,7 @@
top: 50%;
transform: translateY(-50%);
inset-inline-end: 6px;
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
color: #fff;
background: rgba(0,0,0,0.3);
border-radius: 50%;
@@ -1462,7 +1583,7 @@
top: 50%;
transform: translateY(-50%);
inset-inline-end: 6px;
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
color: #fff;
background: rgba(0,0,0,0.3);
border-radius: 50%;
@@ -1523,7 +1644,7 @@
border-radius: 4px;
padding: 6px 12px;
cursor: pointer;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
display: inline-flex;
align-items: center;
gap: 6px;
@@ -1535,7 +1656,7 @@
}
#resetBtn.filter-reset-btn .reset-icon {
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
}
.js-board {
@@ -1559,24 +1680,66 @@
margin: 0 !important; /* grid gap spaces the tiles now (#6523) */
}
/* Apply board colors to li.js-board parent instead of just the link */
.board-list .board-color-nephritis { background-color: #27ae60; }
.board-list .board-color-pomegranate { background-color: #c0392b; }
.board-list .board-color-belize { background-color: #2980b9; }
.board-list .board-color-wisteria { background-color: #8e44ad; }
.board-list .board-color-midnight { background-color: #2c3e50; }
.board-list .board-color-pumpkin { background-color: #e67e22; }
.board-list .board-color-moderatepink { background-color: #cd5a91; }
.board-list .board-color-strongcyan { background-color: #00aecc; }
.board-list .board-color-limegreen { background-color: #4bbf6b; }
.board-list .board-color-dark { background-color: #2c3e51; }
.board-list .board-color-relax { background-color: #27ae61; }
.board-list .board-color-corteza { background-color: #568ba2; }
.board-list .board-color-clearblue { background-color: #3498db; }
.board-list .board-color-natural { background-color: #596557; }
.board-list .board-color-modern { background-color: #2a80b8; }
.board-list .board-color-moderndark { background-color: #2a2a2a; }
.board-list .board-color-exodark { background-color: #222; }
/* The board colour of a tile is painted by the THEME, not from here.
This used to be a hand-copied list of seventeen `.board-list
.board-color-<name> { background-color: ... }` lines, and a copied list is a
list that goes stale: the five colour SLIDE themes added later - clearpink,
cleargreen, clearorange, clearpurple, clearred - were never added to it, so
their tiles stayed the default #999 grey while the slide was drawn on the
inner link, which is inset by the tile's own padding. That is what a
clearpink board looked like in All Boards: a pink rectangle floating in a
grey tile. clearblue WAS in the list, as a flat #3498db, so it did not show
its slide either.
Each theme now paints `.board-list li.board-color-<name>` in
boardColors.css, beside everything else that theme colours, and a tile is
the whole tile. tests/boardTileTheme.test.cjs holds every theme to it. */
/* Member Settings / Change color, beside "Default (no override)": "All Boards".
Every tile in the overview takes the THEME's lighter colour instead of the
colour its own board was given, so the page reads as one list rather than as a
palette - which is what #6593 asked for by making every tile white for
everybody, and this is the same thing as a per-user choice, because "the tile
colours are noise" and "the tile colours are how I find my board" are both
true, of different people.
THE ACCENT ITSELF, the same colour and the same white text as the selected row
in the left menu beside it - `.boards-left-menu .menu-item.active a` reads the
same variable. It was the accent under a flat white veil, one shade up, which
made the tiles a lighter version of the highlight they sit beside rather than
the same colour as it; two shades of one accent on one page read as two
colours, not as one page.
`--theme-accent-fill` rather than `--theme-accent`, and that is the whole
difference on a colour-SLIDE theme: the accent is ONE colour - the solid end
of the slide - so a tile painted from it came out flat beside a left-menu row
that slid. The fill is the theme's slide where it has one and its accent
where it does not (models/lib/themeAccents.js), which is what every other
themed control here reads.
It is the `background` SHORTHAND on purpose: a slide theme paints the tile
with a shorthand of its own, and setting only the colour would leave that
gradient sitting on top of the accent.
A board with a background IMAGE keeps its image: that is a picture somebody
chose, not a colour, and painting over it would be losing something rather
than calming it down. */
body.has-theme-board-tiles .board-list > li.js-board {
background: var(--theme-accent-fill, var(--theme-accent, #2980b9));
}
body.has-theme-board-tiles .board-list > li.js-board .board-list-item:not(.has-background-image) {
background-color: transparent;
background-image: none;
color: #fff;
}
body.has-theme-board-tiles .board-list > li.js-board .board-list-item-name,
body.has-theme-board-tiles .board-list > li.js-board .board-list-item-desc {
color: #fff;
}
/* The count pill keeps its contrast on the accent the same way the left menu's
does: a translucent white pill rather than the mid-grey that only works on
white. */
body.has-theme-board-tiles .board-list > li.js-board .board-list-card-count {
background: rgba(255, 255, 255, 0.3);
color: #fff;
}
.minicard-members {
padding: 6px 0 6px 8px;
@@ -1628,15 +1791,20 @@
the middle of the bar. (The `height: 100vh` this replaced was landing on the
bar too.) The page wrapper is inside #content; the header bar is not. */
#content .wrapper {
overflow: hidden;
height: 100%;
/* #6488: #content is already the page's native vertical scroller. Keeping
another overflow boundary here made the board list depend on a chain of
percentage heights, and an invited board (whose Accept / Decline controls
make the tile taller) broke that chain. Let the page wrapper grow with
every tile; one swipe then always belongs to #content. */
overflow: visible;
height: auto;
min-height: 0;
display: flex;
flex-direction: column;
}
#content .wrapper > .boards-layout {
flex: 1 1 auto;
flex: 0 0 auto;
min-height: 0;
}
@@ -1646,7 +1814,7 @@
.boards-layout > .boards-left-menu,
.boards-layout > .boards-right-grid {
min-height: 0;
height: 100%;
height: auto;
}
.boards-right-grid {
@@ -1661,6 +1829,23 @@
flex: 0 0 auto;
}
/* ...and so does the pane's heading. Without this it is a flex item with the
default `flex-shrink: 1`, so in a column too short for its contents the
TITLE is what gets squeezed instead of the scroller below it growing a
scrollbar. */
.boards-right-grid > .admin-pane-title {
flex: 0 0 auto;
}
/* The right column has TWO branches: board icons and `+tablePage`. They obey
the same one-scroller contract: natural vertical height inside #content.
Horizontal overflow remains handled by tablePage's own table wrapper. */
.boards-right-grid > .table-page {
flex: 0 0 auto;
min-height: 0;
overflow: visible;
}
/* The left menu (Starred / Templates / Remaining / Workspaces / Add board) is its
own vertical scroll area, so when it is taller than the screen you can drag-scroll
down to its lower items — the board icons already scroll in their own column on the
@@ -1668,38 +1853,25 @@
inline borders mirror automatically, so in RTL the menu is on the right and the
boards on the left. */
.boards-left-menu {
overflow-y: auto;
overflow-y: visible;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
/* Bounded by the layout above, not by a guess at how tall the bars are. */
height: 100%;
/* It grows with its rows. #content scrolls both columns together, so a
gesture never gets trapped between a menu scroller and a board scroller. */
height: auto;
min-height: 0;
max-height: none;
/* A vertical swipe here is a scroll of THIS menu: never a page scroll behind
it, and never a gesture the browser hands to something else. */
overscroll-behavior: contain;
touch-action: pan-y;
}
.board-list {
overflow-y: auto !important;
.board-list,
.board-list.mobile-view {
/* One vertical scroller on the page: #content. In particular, do not clip
invited-board tiles whose buttons make them taller than an ordinary tile. */
overflow-y: visible !important;
overflow-x: hidden !important;
-webkit-overflow-scrolling: touch;
scrollbar-width: thin;
scrollbar-color: #888 #f1f1f1;
/* #6488. The list is the part of the column that scrolls, and it ends where
the column ends - which is the bottom of the screen. It used to be
`calc(100dvh - 120px)`: a guess at everything above it (two header bars
plus a search header that is TWO rows on a phone, ~226px), so the box
reached about 100px below the screen. It scrolled internally, but its last
rows sat under the fold where no gesture could bring them - "you cannot
scroll the board list". No viewport arithmetic here at all now. */
flex: 1 1 auto;
flex: 0 0 auto;
min-height: 0;
height: auto;
max-height: none;
overscroll-behavior: contain;
touch-action: pan-y;
}
/* Force scrollbar to always be visible */
@@ -1740,21 +1912,6 @@
list scroll; forcing it 100vh tall would defeat that (see #6488). */
}
/* Ensure only one scrollbar is visible */
/* The page is exactly the visible viewport, and nothing scrolls but the two
inner scrollers. `100dvh` is what is on screen right now; the `vh` line is
the fallback where `dvh` is unknown. Without a definite height here the
`height: 100%` chain below has nothing to resolve against. */
body {
overflow: hidden;
height: 100vh;
height: 100dvh;
}
#content {
overflow: hidden;
}
/* Hide archive and clone board buttons in mobile view */
.board-list .js-archive-board,
.board-list .js-clone-board {
@@ -1829,8 +1986,8 @@
`align-content: start` keeps rows at their content height instead of dividing
the leftover space between them, and `align-items: start` stops each tile from
stretching to its row. The list still scrolls: its height comes from the flex
column it sits in, and `overflow-y: auto` makes it the scroll container. */
stretching to its row. On a phone the list grows naturally and #content is
the one vertical scroll container; this rule changes only grid alignment. */
.board-list.mobile-view {
align-content: start;
align-items: start;
@@ -1910,6 +2067,37 @@
min-height: 4rem;
}
/* #6488 follow-up: an invitation is not an ordinary one-line board tile. It
also contains "You are just invited to this board" and two touch-size action
buttons. The phone equal-tile rule above forced that whole control to 4rem;
`.js-board { overflow: hidden }` then clipped everything below the title.
Keep the 4rem floor, but let this explicitly marked tile grow around its
message and actions. This applies both to mobile mode (`.mobile-view`) and to
a narrow desktop-mode viewport (the media query below). */
.board-list.mobile-view li.js-board.is-invited .board-list-item {
height: auto;
min-height: 4rem;
}
.board-list li.js-board.is-invited .board-list-item-desc {
margin: 4px 0 8px;
}
@media screen and (max-width: 800px),
screen and (max-device-width: 932px) and (-webkit-min-device-pixel-ratio: 3) {
.board-list li.js-board.is-invited .board-list-item,
.board-list.mobile-view li.js-board.is-invited .board-list-item {
height: auto;
min-height: 4rem;
}
.board-list li.js-board.is-invited button {
display: block;
width: 100%;
margin: 4px 0 0;
}
}
/* With drag handles OFF there is no handle on a board tile, so the title should
use the width that was reserved for it. `:has()` keeps this to one rule
@@ -1977,10 +2165,10 @@
}
.workspace-node .workspace-drag-handle {
padding: 0 2px;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
}
.workspace-node .workspace-icon {
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
}
.workspace-node .js-open-workspace-menu {
padding: 2px 4px;
@@ -1988,7 +2176,7 @@
.workspace-node .workspace-count {
min-width: 16px;
padding: 2px 4px;
font-size: 10px;
font-size: calc(10px * var(--wekan-ui-font-scale, 1));
}
}
@@ -2005,7 +2193,7 @@
min-width: 44px;
min-height: 44px;
padding: 8px;
font-size: 20px;
font-size: calc(20px * var(--wekan-ui-font-scale, 1));
line-height: 1;
}
}
@@ -2042,7 +2230,7 @@
}
/* All Boards: the Table view.
* Design: docs/Design/Page/All-Boards.md, which is docs/Design/Page/Table.md.
* Design: docs/Features/Page/All-Boards.md, which is docs/Features/Page/Table.md.
* Only what is particular to these rows; the table itself is tablePage.css. */
.all-boards-row {
cursor: default;
@@ -2068,3 +2256,8 @@
white-space: normal;
word-break: break-word;
}
.all-boards-table-actions {
display: flex;
justify-content: flex-end;
margin-bottom: 10px;
}
+31 -20
View File
@@ -10,7 +10,7 @@ template(name="boardList")
a 260px-wide nothing beside them. The grid drops to one column and hides
the menu in the same rule. The Admin Panel marks its menu instead, because
a flex row with a hidden item simply closes up.
docs/Design/Page/Left-Menu.md
docs/Features/Page/Left-Menu.md
.boards-layout(class="{{#if isLeftMenuCollapsed}}collapsed{{/if}}")
// Left menu
.boards-left-menu
@@ -18,7 +18,7 @@ template(name="boardList")
menus cannot end up with two different carets, and the same control a
list has on a board. Folded, the menu is not drawn at all and the
caret that brings it back rides on the pane title below.
docs/Design/Page/Left-Menu.md
docs/Features/Page/Left-Menu.md
+leftMenuCollapse
ul.menu
//- ONE row, rendered four times, rather than four copies of the same
@@ -99,29 +99,44 @@ template(name="boardList")
child of the GRID and not of the menu: it is positioned absolutely, so it
is no grid item and adds no track, and it does not scroll away with a
long list of workspaces the way a handle inside the menu would.
docs/Design/Page/Left-Menu.md
docs/Features/Page/Left-Menu.md
+leftMenuResize
// Right boards grid
//- There is NO bar above the boards, and no second header bar either.
Every control of this page - Sort, Search, Multi-Selection, the view
menu and the actions on a selection - is in the right sidebar, and the
page is named by the FIRST header bar. docs/Design/Page/All-Boards.md
page is named by the FIRST header bar. docs/Features/Page/All-Boards.md
.boards-right-grid
//- The heading of the pane: which list of boards this is. It sits above
the view branch, so it is drawn ONCE whichever view is on - the board
icons and the Table are two ways of showing the same section, not two
sections.
The Admin Panel's own `paneTitle`, so the two pages have one heading
at one size and colour. docs/Design/Page/All-Boards.md
at one size and colour. docs/Features/Page/All-Boards.md
+paneTitle(allBoardsPaneTitle)
if showsBoardSelectionControls
.board-multiselection-controls
button.primary.js-board-select-all(type="button") {{_ 'select-all'}}
button.primary.js-board-select-none(type="button") {{_ 'select-none'}}
//- The Archive is a SECTION of this page, drawn here beside the left
menu, not a page of its own that replaces it - and drawn with the same
board ICONS every other section uses. It had a table of its own, which
meant the one place you go to bring a board back was the one place
multi-selection, dragging and the tile layout did not work.
docs/Design/Page/Archive.md
docs/Features/Page/Archive.md
if isAllBoardsView 'table'
//- The same create action as the Lists tile, in the same sections.
Archive and Home deliberately expose neither action.
if showsAddBoardTile
.all-boards-table-actions
button.primary.js-add-board(type="button")
span.emoji-icon
i.fa.fa-plus
if isSelectedMenu 'templates'
| {{_ 'add-template-container'}}
else
| {{_ 'add-board'}}
+tablePage(tablePageData)
else
if boardsPaginationActive
@@ -192,7 +207,7 @@ template(name="boardList")
a.js-unstar-bookmark.bookmark-unstar(href="#", title="{{_ 'click-to-unstar-page'}}")
i.fa.fa-times
each boards
li.js-board.nodragscroll(class="{{_id}} {{#if isStarred}}starred{{/if}} {{colorClass}} {{#if BoardMultiSelection.isSelected _id}}is-checked{{/if}}", draggable="true")
li.js-board.nodragscroll(class="{{_id}} {{#if isStarred}}starred{{/if}} {{colorClass}} {{#if isInvited}}is-invited{{/if}} {{#if BoardMultiSelection.isSelected _id}}is-checked{{/if}}", draggable="true")
if isInvited
.board-list-item
if BoardMultiSelection.isActive
@@ -238,9 +253,7 @@ template(name="boardList")
client/components/boards/boardsList.js
if isSelectedMenu 'archive'
p.board-list-item-archived-at
span.emoji-icon
i.fa.fa-archive
| {{_ 'archived-at'}}
| {{_ 'archived-at'}}
= ' '
= archivedAtText
if hasSpentTimeCards
@@ -307,9 +320,7 @@ template(name="boardList")
client/components/boards/boardsList.js
if isSelectedMenu 'archive'
p.board-list-item-archived-at
span.emoji-icon
i.fa.fa-archive
| {{_ 'archived-at'}}
| {{_ 'archived-at'}}
= ' '
= archivedAtText
if hasSpentTimeCards
@@ -413,7 +424,7 @@ template(name="workspaceTree")
NOT the whole row either: the row also holds the ⋯ menu and the board
count, and a drag started on those is a drag of something else.
`dragstart` bubbles, so the handler stays on the row in both cases.
docs/Design/Page/All-Boards.md
docs/Features/Page/All-Boards.md
li.workspace-node(class="{{#if $eq id ../selectedWorkspaceId}}active{{/if}}" data-workspace-id="{{id}}")
.workspace-node-content
//- The caret that folds this workspace's own workspaces away, at
@@ -424,7 +435,7 @@ template(name="workspaceTree")
A workspace with no workspaces under it has nothing to fold, so it
gets a spacer of the same width instead: a row that shifted
sideways when it gained a child would be a moving target.
docs/Design/Page/Workspaces.md
docs/Features/Page/Workspaces.md
if workspaceHasChildren
a.workspace-collapse-indicator.js-collapse-workspace(
title="{{workspaceCollapseLabel}}"
@@ -467,7 +478,7 @@ template(name="workspaceTree")
`..` counts block levels, and a second block between the `each` and
this inclusion is another level for `../selectedWorkspaceId` to
climb - which is how the highlight was lost once already.
docs/Design/Page/Workspaces.md
docs/Features/Page/Workspaces.md
if workspaceShowsChildren
+workspaceTree(nodes=children selectedWorkspaceId=../selectedWorkspaceId)
@@ -508,7 +519,7 @@ template(name="workspaceActionsPopup")
//-
//- It names the CURRENT view, not itself: the board header says "Swimlanes" or
//- "Lists" rather than "Board View", and this matches it.
//- docs/Design/Page/Header.md
//- docs/Features/Page/Header.md
//- The page's controls, as icons, in the first top header bar - left of the
//- bell, exactly where a board's own controls are. They were rows inside the
//- right sidebar's home view, which meant two clicks and a panel covering the
@@ -518,7 +529,7 @@ template(name="workspaceActionsPopup")
//-
//- Each names itself beside its icon where the bar has room, and the one rule
//- in header.css drops every label below 1100px, leaving the tooltips.
//- docs/Design/Page/All-Boards.md
//- docs/Features/Page/All-Boards.md
template(name="allBoardsHeaderButtons")
a.board-header-btn.js-open-boards-sort(title="{{_ 'sort-boards'}}" class="{{#unless isBoardsSort 'custom'}}emphasis{{/unless}}")
i.fa.fa-sort
@@ -526,12 +537,12 @@ template(name="allBoardsHeaderButtons")
a.board-header-btn.js-all-boards-sidebar-search(title="{{_ 'search-boards'}}")
i.fa.fa-search
span.board-header-btn-label {{_ 'search-boards'}}
if canModifyBoards
if canMultiSelectBoards
//- Says whether it is ON, and offers the way OFF - the same pair the
board's own Multi-Selection has. It looked identical whether or not a
selection was running, so the one control that changes what a click on a
board does gave no sign it had changed it, and the only way out was to
find the row inside the sidebar. docs/Design/Page/Multi-Selection.md
find the row inside the sidebar. docs/Features/Page/Multi-Selection.md
a.board-header-btn.js-all-boards-sidebar-multiselection(
title="{{#if BoardMultiSelection.isActive}}{{_ 'multi-selection-on'}}{{else}}{{_ 'multi-selection'}}{{/if}}"
class="{{#if BoardMultiSelection.isActive}}emphasis{{/if}}")
+169 -37
View File
@@ -8,7 +8,7 @@ import getSlug from 'limax';
// The archived-at line on a tile in the Archive, in the reader's own format.
import { formatDateByUserPreference } from '/imports/lib/dateUtils';
// The All Boards URLs, and the slug path of a workspace in the tree.
// docs/Design/Page/All-Boards-URLs.md
// docs/Features/Page/All-Boards-URLs.md
import {
ALL_BOARDS_SECTIONS,
defaultSection,
@@ -16,6 +16,8 @@ import {
workspaceIdForSlugPath,
allBoardsPathForMenu,
sectionTitleKey,
SECTION_ARCHIVE,
SECTION_REMAINING,
SECTION_WORKSPACES,
} from '/models/lib/allBoardsUrls';
import TableVisibilityModeSettings from '/models/tableVisibilityModeSettings';
@@ -38,7 +40,7 @@ import { Utils } from '/client/lib/utils';
import '/client/lib/dragDropTouch'; // touch -> HTML5 DnD so board icons drag by finger
// What a drag does to the workspaces tree - move before, after, or INTO another
// workspace - as pure functions, so the rules are testable without a browser.
// docs/Design/Page/Workspaces.md
// docs/Features/Page/Workspaces.md
const {
BEFORE: DROP_BEFORE,
AFTER: DROP_AFTER,
@@ -54,7 +56,7 @@ import {
} from '/models/lib/boardSortReorder';
// The sidebar the Search and Multi-Selection controls open. Its state is module
// scope, not a template instance: this bar and the sidebar are separate Blaze
// instances. docs/Design/Page/Search.md, docs/Design/Page/Multi-Selection.md
// instances. docs/Features/Page/Search.md, docs/Features/Page/Multi-Selection.md
import {
openAllBoardsSidebar,
closeAllBoardsSidebar,
@@ -229,6 +231,9 @@ function homeBoardId() {
// has to REFUSE the drop while it is still in the air, which means answering
// before the drop happens. docs/Features/Board/Home.md
const DRAG_FROM_HOME = 'application/x-board-from-home';
const DRAG_FROM_REMAINING = 'application/x-board-from-remaining';
const DRAG_FROM_WORKSPACE = 'application/x-board-from-workspace';
const ARCHIVED_MULTI_BOARD_DRAG = 'application/x-archived-board-multi';
// Reordering a bookmark carries its own type, so a bookmark and a board cannot
// be dropped on each other. Readable in `dragover` for the same reason
@@ -274,6 +279,40 @@ function isDragFromHome(evt) {
}
}
function isArchivedMultiBoardDrag(evt) {
try {
const types = evt.originalEvent.dataTransfer.types;
if (!types) return false;
return Array.prototype.indexOf.call(types, ARCHIVED_MULTI_BOARD_DRAG) !== -1;
} catch (e) {
return false;
}
}
function isDragFromRemaining(evt) {
try {
const types = evt.originalEvent.dataTransfer.types;
if (!types) return false;
return Array.prototype.indexOf.call(types, DRAG_FROM_REMAINING) !== -1;
} catch (e) {
return false;
}
}
function isDragFromWorkspace(evt) {
try {
const types = evt.originalEvent.dataTransfer.types;
if (!types) return false;
return Array.prototype.indexOf.call(types, DRAG_FROM_WORKSPACE) !== -1;
} catch (e) {
return false;
}
}
function isDragFromRemainingOrWorkspace(evt) {
return isDragFromRemaining(evt) || isDragFromWorkspace(evt);
}
function menuItemCountOf(type) {
const currentUser = ReactiveCache.getCurrentUser();
const assignments =
@@ -321,7 +360,7 @@ Template.boardList.helpers({
},
// Whether to move the boards left, out from under the right sidebar. The
// sidebar is a separate Blaze instance, so the state is module scope - the
// page cannot read a ReactiveVar on it. docs/Design/Page/Search.md
// page cannot read a ReactiveVar on it. docs/Features/Page/Search.md
isSidebarOpen() {
return isAllBoardsSidebarOpen();
},
@@ -342,11 +381,11 @@ Template.boardList.helpers({
// map catches events from the templates rendered inside it, so these fire for
// this bar's copy and the board header's map fires for its own. That is what
// lets one piece of markup mean "search cards" on a board and "search boards"
// here. docs/Design/Page/Search.md, docs/Design/Page/Multi-Selection.md
// here. docs/Features/Page/Search.md, docs/Features/Page/Multi-Selection.md
// Sort and the view menu, which were buttons in this page's second header bar
// and are rows of the sidebar's home view now. A Blaze event map only sees
// events inside its OWN template, so they had to move with their markup.
// docs/Design/Page/All-Boards.md
// docs/Features/Page/All-Boards.md
Template.allBoardsHomeSidebar.events({
// Titled "Sort Boards", from the key the app already has for that phrase -
// the same reasoning as the starred-boards popup: a `boardsSortPopup-title`
@@ -379,7 +418,7 @@ Template.allBoardsHomeSidebar.helpers({
// A Blaze event map only sees events inside its OWN template, so these are
// their own map rather than shared with the sidebar's: the same
// `js-all-boards-sidebar-search` markup exists in both places and each map
// fires for its own copy. docs/Design/Page/All-Boards.md
// fires for its own copy. docs/Features/Page/All-Boards.md
Template.allBoardsHeaderButtons.helpers({
// Every template registers the helpers IT uses. `BoardMultiSelection` is also
// a helper of boardList, but a Blaze template cannot see a sibling's helpers -
@@ -398,7 +437,7 @@ Template.allBoardsHeaderButtons.helpers({
},
// Multi-Selection archives and duplicates boards, so somebody who may only
// comment is not offered it.
canModifyBoards() {
canMultiSelectBoards() {
const currentUser = ReactiveCache.getCurrentUser();
return currentUser && !currentUser.isCommentOnly();
},
@@ -431,6 +470,7 @@ Template.allBoardsHeaderButtons.events({
evt.preventDefault();
evt.stopPropagation();
BoardMultiSelection.disable();
closeAllBoardsSidebar();
},
});
@@ -439,7 +479,7 @@ Template.boardList.events({});
// Put the selected menu entry in the address bar. A section is its own name; a
// workspace is the slugs of its names down the tree, so the URL says where you
// are rather than carrying a random id. `go`, not `replace`, so Back returns to
// the previous entry. docs/Design/Page/All-Boards-URLs.md
// the previous entry. docs/Features/Page/All-Boards-URLs.md
function goToAllBoards(tpl, menuValue) {
const path = allBoardsPathForMenu(menuValue, tpl.workspacesTreeVar.get(), getSlug);
if (path && FlowRouter.current().path !== path) FlowRouter.go(path);
@@ -448,7 +488,7 @@ function goToAllBoards(tpl, menuValue) {
// The boards the page shows: the selected section, filtered by the search
// field, sorted and paged. Extracted from the `boards` helper so the Table
// view draws the SAME set - two copies of this would be two answers to
// "which boards am I looking at". docs/Design/Page/All-Boards.md
// "which boards am I looking at". docs/Features/Page/All-Boards.md
// How many archived boards the section shows at once. The publication is
// paginated and this is the page size it is asked for; the Archive is a place
// you go to find one board, not a list to scroll for ever.
@@ -456,15 +496,18 @@ const ARCHIVED_BOARDS_LIMIT = 60;
function boardsForView(tpl) {
// The Archive shows ARCHIVED boards - the one section whose query is the
// opposite of every other one's. docs/Design/Page/Archive.md
// opposite of every other one's. docs/Features/Page/Archive.md
const showsArchive = tpl.selectedMenu.get() === 'archive';
let query = {
$and: [
{ archived: showsArchive },
{ type: { $in: ['board', 'template-container'] } },
{ title: notHelperBoardTitle() },
],
};
// Active helper boards stay out of ordinary board lists, but an archived
// helper board must remain reachable here so its owner can restore or
// permanently delete it (#6643).
if (!showsArchive) query.$and.push({ title: notHelperBoardTitle() });
const membershipOrs = [];
let allowPrivateVisibilityOnly = TableVisibilityModeSettings.findOne(
@@ -659,7 +702,7 @@ Template.boardList.onCreated(function () {
// Shared with the right sidebar, which is a SEPARATE Blaze instance (it is
// rendered beside the page, not inside it) and carries this page's controls. Assigned onto the instance so every `tpl.selectedMenu` /
// `tpl.boardSearchVar` already written here keeps working unchanged.
// docs/Design/Page/All-Boards.md
// docs/Features/Page/All-Boards.md
this.selectedMenu = allBoardsMenuVar;
// Whatever the address named, else the section this user should land on:
// Starred when anything is starred, Remaining when nothing is - opening on an
@@ -687,7 +730,7 @@ Template.boardList.onCreated(function () {
// It runs whenever either changes, so a link followed while the page is
// already open switches workspace, and a slug path that names nothing leaves
// the Workspaces section selected rather than an empty board list.
// docs/Design/Page/All-Boards-URLs.md
// docs/Features/Page/All-Boards-URLs.md
this.autorun(() => {
const slugPath = Session.get('boardListWorkspacePath') || [];
const tree = this.workspacesTreeVar.get();
@@ -719,6 +762,10 @@ Template.boardList.onCreated(function () {
this.subscribe('archivedBoards', this.boardSearchVar.get() || '',
ARCHIVED_BOARDS_LIMIT, 0);
});
this.autorun(() => {
if (this.selectedMenu.get() !== 'templates' && !this.boardSearchVar.get()) return;
this.subscribe('boardTemplates');
});
// The Table view's page. Client-side: the boards are already in minimongo for
// the Lists view beside it, so paging them again on the server would be a round
// trip for data the page is holding anyway.
@@ -754,7 +801,7 @@ Template.boardList.onCreated(function () {
// A workspace was dropped: before, after, or INTO another one. The tree it
// becomes is worked out by the pure module - the guards that keep a subtree
// attached to the root live there, with their own tests - and this only saves
// the answer. docs/Design/Page/Workspaces.md
// the answer. docs/Features/Page/Workspaces.md
this.moveWorkspaceInTree = (draggedId, targetId, position) => {
const tree = this.workspacesTreeVar.get();
// A drop that puts a workspace back where it already is writes the same tree
@@ -944,7 +991,7 @@ Template.boardList.helpers({
// The Table view of the same boards the Lists view draws. Ten per page, the
// shared TABLE_PAGE_ROWS_PER_PAGE, and a rowTemplate because the Edit cell is a
// control and the row carries the board's colours.
// docs/Design/Page/All-Boards.md
// docs/Features/Page/All-Boards.md
tablePageData() {
const tpl = Template.instance();
const all = boardsForView(tpl);
@@ -968,6 +1015,13 @@ Template.boardList.helpers({
boards() {
return boardsForView(Template.instance());
},
showsBoardSelectionControls() {
const tpl = Template.instance();
const namedSection = ['remaining', 'starred', 'home', 'templates', 'archive']
.includes(tpl.selectedMenu.get());
const workspace = Boolean(tpl.selectedWorkspaceIdVar.get());
return (namedSection || workspace) && BoardMultiSelection.isActive();
},
// #5174 / #4825: the board tiles' per-list card-count line and member avatar
// row. Data comes from the one-shot getAllBoardsTileData method fetch in
// onCreated (see there) — NOT from reactive getLists/getCards cursors, which
@@ -1099,7 +1153,7 @@ Template.boardList.helpers({
// colour rather than two that drift apart. Its words are the section's own
// title key - the same key the first header bar names the page with, and the
// same one the highlighted menu row carries - so all three say the same
// thing. docs/Design/Page/All-Boards.md
// thing. docs/Features/Page/All-Boards.md
//
// `{ titleKey }` for a section, `{ label }` for a workspace: a workspace's
// name is what somebody typed, and a workspace called "starred" is not the
@@ -1258,7 +1312,7 @@ Template.boardList.helpers({
// it, or light the row up to say "into this one". The class is also what the
// drop reads back: the pointer's position is worked out once, while the row is
// showing it, rather than a second time from a drop event that may land a pixel
// away from where the slot was drawn. docs/Design/Page/Workspaces.md
// away from where the slot was drawn. docs/Features/Page/Workspaces.md
const WORKSPACE_DROP_CLASSES = {
[DROP_BEFORE]: 'drop-before',
[DROP_INSIDE]: 'drop-inside',
@@ -1316,7 +1370,7 @@ Template.workspaceTree.helpers({
// The caret, and what it says. `Template.currentData()` is the NODE inside the
// `each`, which is what makes these read the row they are drawn on rather than
// needing the id passed to each of them.
// docs/Design/Page/Workspaces.md
// docs/Features/Page/Workspaces.md
workspaceHasChildren() {
return hasChildren(Template.currentData());
},
@@ -1562,6 +1616,7 @@ Template.boardList.events({
// HTML5 DnD from boards to spaces
// #5850: drag a (template) board onto an Org/Team/Domain target to share it.
'dragover .js-share-target'(evt) {
if (isArchivedMultiBoardDrag(evt)) return;
evt.preventDefault();
if (evt.originalEvent.dataTransfer) {
evt.originalEvent.dataTransfer.dropEffect = 'copy';
@@ -1572,6 +1627,7 @@ Template.boardList.events({
evt.currentTarget.classList.remove('board-drag-hint');
},
'drop .js-share-target'(evt) {
if (isArchivedMultiBoardDrag(evt)) return;
evt.preventDefault();
evt.stopPropagation();
const target = evt.currentTarget;
@@ -1622,6 +1678,32 @@ Template.boardList.events({
return;
}
if (tpl && tpl.selectedMenu && tpl.selectedMenu.get() === SECTION_REMAINING) {
try {
evt.originalEvent.dataTransfer.setData(DRAG_FROM_REMAINING, '1');
} catch (e) {}
}
if (
tpl && tpl.selectedWorkspaceIdVar
&& tpl.selectedWorkspaceIdVar.get()
) {
try {
evt.originalEvent.dataTransfer.setData(DRAG_FROM_WORKSPACE, '1');
} catch (e) {}
}
// While Multi-Selection is on in Archive, every board drag is a restore
// gesture. Mark even an unselected tile dragged on its own, so Home cannot
// become an accidental target merely because that tile was not checked.
if (
tpl && tpl.selectedMenu && tpl.selectedMenu.get() === SECTION_ARCHIVE
&& BoardMultiSelection.isActive()
) {
try {
evt.originalEvent.dataTransfer.setData(ARCHIVED_MULTI_BOARD_DRAG, '1');
} catch (e) {}
}
// Support multi-drag
if (
BoardMultiSelection.isActive() &&
@@ -1667,11 +1749,23 @@ Template.boardList.events({
el.classList.add('board-drag-hint');
});
document.querySelectorAll('.js-select-menu').forEach((el) => {
// Remaining takes a board out of a workspace and Home makes it the board
// that opens after login: both are places this drag can end, so both say
// so while it is in the air.
// An archived multi-selection may be restored to Remaining or an
// existing Workspace, but cannot become Home. Other live-board drags can
// still use both Remaining and Home.
const type = el.getAttribute('data-type');
if (type === 'remaining' || type === 'home') {
const archivedMulti =
tpl && tpl.selectedMenu && tpl.selectedMenu.get() === SECTION_ARCHIVE
&& BoardMultiSelection.isActive();
const fromRemaining =
tpl && tpl.selectedMenu && tpl.selectedMenu.get() === SECTION_REMAINING;
const fromWorkspace =
tpl && tpl.selectedWorkspaceIdVar && tpl.selectedWorkspaceIdVar.get();
if (
type === 'remaining'
|| (!archivedMulti && type === 'home')
|| ((fromRemaining || fromWorkspace) &&
(type === 'starred' || type === 'archive'))
) {
el.classList.add('board-drag-hint');
}
});
@@ -1807,8 +1901,10 @@ Template.boardList.events({
evt.preventDefault();
if (BoardMultiSelection.isActive()) {
BoardMultiSelection.disable();
closeAllBoardsSidebar();
} else {
BoardMultiSelection.activate();
openAllBoardsSidebar(SIDEBAR_MULTISELECTION);
}
},
'click .js-multiselection-reset'(evt) {
@@ -1818,6 +1914,7 @@ Template.boardList.events({
// would otherwise immediately re-activate what we just disabled.
evt.stopPropagation();
BoardMultiSelection.disable();
closeAllBoardsSidebar();
},
'click .js-toggle-board-multi-selection'(evt) {
evt.preventDefault();
@@ -1825,6 +1922,17 @@ Template.boardList.events({
const boardId = this._id;
BoardMultiSelection.toogle(boardId);
},
'click .js-board-select-all'(evt, tpl) {
evt.preventDefault();
// `boardsForView` is the exact list of icons being drawn: the section and
// search have already narrowed it. Do not silently select a board that is
// not visible on this page.
BoardMultiSelection.add(boardsForView(tpl).map(board => board._id));
},
'click .js-board-select-none'(evt) {
evt.preventDefault();
BoardMultiSelection.reset();
},
'click #resetBtn'(event) {
let allBoards = document.getElementsByClassName('js-board');
let currBoard;
@@ -1907,7 +2015,7 @@ Template.boardList.events({
// the drop handler below reads as "a board was dropped here", so the drop
// did nothing and the row snapped back. Handles ON started the drag from a
// span, which carries no text, which is why the same drop worked there.
// docs/Design/Page/Workspaces.md
// docs/Features/Page/Workspaces.md
if (typeof dt.clearData === 'function') dt.clearData();
dt.setData('application/x-workspace-id', workspaceId);
@@ -2005,6 +2113,8 @@ Template.boardList.events({
try {
const boardIds = JSON.parse(boardData);
boardIds.forEach((boardId) => {
const board = ReactiveCache.getBoard(boardId);
if (board && board.archived) Meteor.call('restoreBoard', boardId);
Meteor.call('assignBoardToWorkspace', boardId, targetWorkspaceId);
});
} catch (e) {
@@ -2012,6 +2122,8 @@ Template.boardList.events({
}
} else {
// Single board drag
const board = ReactiveCache.getBoard(boardData);
if (board && board.archived) Meteor.call('restoreBoard', boardData);
Meteor.call('assignBoardToWorkspace', boardData, targetWorkspaceId);
}
},
@@ -2021,15 +2133,19 @@ Template.boardList.events({
// so the cursor says no while the board is still in the air rather than the
// drop landing and quietly doing nothing. docs/Features/Board/Home.md
if (isDragFromHome(evt)) return;
const menuType = evt.currentTarget.getAttribute('data-type');
// Remaining accepts board drags generally. Starred additionally accepts a
// board from Remaining or an existing Workspace. Home and Archive have
// their own handlers.
if (
menuType !== 'remaining'
&& !(menuType === 'starred' && isDragFromRemainingOrWorkspace(evt))
) return;
evt.preventDefault();
evt.stopPropagation();
const menuType = evt.currentTarget.getAttribute('data-type');
// Only allow drop on "remaining" menu to unassign boards from spaces
if (menuType === 'remaining') {
evt.originalEvent.dataTransfer.dropEffect = 'move';
evt.currentTarget.classList.add('drag-over');
}
evt.originalEvent.dataTransfer.dropEffect = 'move';
evt.currentTarget.classList.add('drag-over');
},
'dragleave .js-select-menu'(evt) {
evt.currentTarget.classList.remove('drag-over');
@@ -2045,6 +2161,7 @@ Template.boardList.events({
// being dragged.
'dragover .js-open-archived-board'(evt) {
if (isDragFromHome(evt)) return;
if (isArchivedMultiBoardDrag(evt)) return;
evt.preventDefault();
evt.stopPropagation();
evt.originalEvent.dataTransfer.dropEffect = 'move';
@@ -2055,6 +2172,7 @@ Template.boardList.events({
},
'drop .js-open-archived-board'(evt) {
if (isDragFromHome(evt)) return;
if (isArchivedMultiBoardDrag(evt)) return;
evt.preventDefault();
evt.stopPropagation();
evt.currentTarget.classList.remove('drag-over');
@@ -2220,6 +2338,7 @@ Template.boardList.events({
// out of the Home section - and clicking the row's Multi-Selection toggle
// still toggles. docs/Features/Board/Home.md
'dragover .js-home-menu'(evt) {
if (isArchivedMultiBoardDrag(evt)) return;
evt.preventDefault();
evt.stopPropagation();
evt.originalEvent.dataTransfer.dropEffect = 'link';
@@ -2229,6 +2348,7 @@ Template.boardList.events({
evt.currentTarget.classList.remove('drag-over');
},
'drop .js-home-menu'(evt) {
if (isArchivedMultiBoardDrag(evt)) return;
evt.preventDefault();
evt.stopPropagation();
evt.currentTarget.classList.remove('drag-over');
@@ -2247,13 +2367,14 @@ Template.boardList.events({
return;
}
}
if (!boardIds.length) return;
if (boardIds.length !== 1) {
alert(TAPi18n.__('select-only-one-board'));
return;
}
// One board opens after login, so a multi-selection dropped here sets the
// FIRST of them - the same one the Multi-Selection sidebar's own Home row
// sets - rather than doing nothing or silently keeping the last. The
// selection is dropped afterwards, so it cannot look as though all of them
// went somewhere.
// Login can open one Home board. A multi-selection therefore cannot
// silently choose its first id; it has to be narrowed to one before this
// drop can change anything.
Meteor.call('setDefaultBoard', boardIds[0], (err) => {
if (err) alert(err?.reason || err?.message || 'Failed to set Home board');
});
@@ -2289,6 +2410,17 @@ Template.boardList.events({
}
}
if (menuType === 'starred') {
if (!isDragFromRemainingOrWorkspace(evt)) return;
const user = ReactiveCache.getCurrentUser();
boardIds.forEach((boardId) => {
if (!user || !user.hasStarred(boardId)) {
Meteor.call('toggleBoardStar', boardId);
}
});
return;
}
// Everything below is what a drop on REMAINING means.
if (menuType !== 'remaining') return;
@@ -2409,7 +2541,7 @@ Template.workspaceActionsPopup.onRendered(function() {
// The All Boards view menu, in the FIRST header bar. Its handler and the helper
// that draws it follow it out of the sidebar. docs/Design/Page/Header.md
// that draws it follow it out of the sidebar. docs/Features/Page/Header.md
Template.allBoardsViewMenu.events({
// Titled, so it has a header with the close ✕ in it - the same popup the
// BOARD's view menu opens, and it is the same question: which view of this
+17 -4
View File
@@ -68,16 +68,29 @@
/* #3392: card detail "Dependencies" rows */
.card-details-dependencies-list .card-dependency {
display: flex;
align-items: center;
/* The title WRAPS now, so the row can be two or three lines tall. Centring
the icon and the controls against that block would float them halfway down
it; they belong beside the first line. */
align-items: flex-start;
gap: 6px;
margin-bottom: 4px;
}
.card-details-dependencies-list .card-dependency-link {
flex: 1 1 auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
/* A card's title is a sentence, and the card details pane is narrow. It used
to be one line with an ellipsis, so everything past the width of the pane -
which is most of a real title, sitting right of the coloured icon - could
not be read at all. There is no reason to hide it: the row can be tall.
`min-width: 0` is what makes that work. A flex item's default is
`min-width: auto`, which refuses to shrink below its content, so the text
overflowed the row instead of wrapping inside it - and no amount of
`white-space: normal` alone would have fixed that. */
min-width: 0;
white-space: normal;
/* A title can be a URL or an id with no spaces in it; break inside the word
rather than let one long token push the controls off the row. */
overflow-wrap: anywhere;
}
.card-details-dependencies-list .js-dependency-type {
+111
View File
@@ -0,0 +1,111 @@
//- The checkbox list - WHAT an export includes, and what an import brings in.
Its own template so the board popup can borrow it into a pane of its own
layout while the swimlane, list and card popups get it inside the pane
exportScopeBody builds for them, with no copy of the markup in either place.
The helpers are registered on BOTH templates from one object (exportScope.js);
the click handlers stay on exportScopeBody, because an event from here bubbles
to the template that includes it.
//- EACH ROW CARRIES ITS OWN CHECKBOX - `.materialCheckBox`, the one Admin
Panel / Settings / Announcement uses for "Active System-Wide Announcement",
and the one every checkbox in the Admin Panel uses. A reader has seen it
before, and it says "on" and "off" at a glance.
It used to be an unconditional `i.fa.fa-check` on a `li.active`, which is a
DIFFERENT convention: popup.css hides that tick and shows it only for an
active row - but its selector is `.pop-over-list .pop-over-list.checkable
.fa-check`, so it needs a NESTED list carrying `checkable`, and this list was
neither. The tick was therefore never hidden and `active` was never styled, so
every row looked ticked whatever it was and clicking one changed nothing on
screen. Reported as \#6586 comment 5308548585, "I can't select/deselect those
arrows here".
A checkbox that carries its own state needs no ancestor to be right, which is
why it is this rather than the missing class.
template(name="exportScopeSelect")
p.quiet {{_ 'export-select-what-to-include'}}
ul.pop-over-list.export-scope-select
li
a.flex.js-export-card-details-toggle(href="#" role="checkbox" aria-checked="{{#if cardDetailsChecked}}true{{else}}false{{/if}}")
.materialCheckBox(class="{{#if cardDetailsChecked}}is-checked{{/if}}")
span {{_ 'export-card-details'}}
each exportFields
li
a.flex.js-export-field-toggle(data-field="{{field}}" href="#" role="checkbox" aria-checked="{{#if checked}}true{{else}}false{{/if}}")
.materialCheckBox(class="{{#if checked}}is-checked{{/if}}")
span {{_ label}}
//- The body of an export popup - ALL of them (#1173). What to include in one
pane and what to export it to in the other, side by side when the window has
room and stacked when it does not.
It was two menus: the board popup wrote nineteen formats of its own in
sidebar.jade under four subheadings, and the swimlane, list and card popups
wrote five here in one narrow column - so "the export popup" looked like two
different features depending on which menu opened it, and a format added to
one was missing from the other. The formats are one table now
(EXPORT_FORMAT_GROUPS, exportScope.js); each entry says which scopes it
applies to and whether it is a link or an action, and this lays them out.
template(name="exportScopeBody")
.export-board-panes
.export-board-pane.export-board-pane-select
+exportScopeSelect
.export-board-pane.export-board-pane-formats
if isImportMode
//- #1173: the same popup, the other direction. The checkboxes in the
other pane say what goes OUT of an export and what comes IN from an
import - one selection, so "no comments" means the same thing
whichever way the file is moving.
Asked HERE as well as on the menu row that opens it: the row is hidden
from a reader who may not write, and a popup that trusts its caller is
a popup that offers the file chooser the first time somebody reaches
it another way.
if canImport
p.quiet {{_ 'import-here-instruction'}}
ul.pop-over-list
li
label.js-import-file-label
i.fa.fa-download
| {{_ 'import'}} / JSON, .zip
input.js-import-file(type="file" accept=".json,application/json,.zip,application/zip" hidden)
if importBusy
p.quiet {{_ 'importing'}}
if importError
.warning {{_ importError}}
if importDone
p.quiet {{_ 'import-done'}} {{importDone}}
else
each formatGroups
if heading
hr
h4.pop-over-list-subheading {{heading}}
ul.pop-over-list
each entries
li
if action
a(class="{{action}}")
i.fa(class="{{icon}}")
| {{label}}
else
//- The server supplies the localized, scope-aware filename.
a(href="{{url}}" download)
i.fa(class="{{icon}}")
| {{label}}
template(name="exportSwimlanePopup")
+exportScopeBody(swimlaneId=_id title=title)
template(name="exportListPopup")
+exportScopeBody(listId=_id title=title)
template(name="exportChecklistPopup")
+exportScopeBody(checklistId=_id title=title)
//- The IMPORT half of each menu. Same body, same table, same selection - the
mode decides whether the second pane offers the formats to write out or the
file to read in. A menu says what it is about, so its two rows are named
"Export" and "Import" and nothing more.
template(name="importSwimlanePopup")
+exportScopeBody(swimlaneId=_id title=title mode="import")
template(name="importListPopup")
+exportScopeBody(listId=_id title=title mode="import")
template(name="importCardPopup")
+exportScopeBody(cardId=_id listId=listId title=title mode="import")
+457
View File
@@ -0,0 +1,457 @@
// ITS OWN TEMPLATE FIRST. This module is imported by sidebar.js, by
// cardDetails.js and by the import page, and any of them can be evaluated
// before client/features/boards.js gets to the .jade - at which point
// `Template.exportScopeBody` is undefined and `.helpers()` on it throws at
// module scope. That does not break this popup, it breaks THE WHOLE BUNDLE:
// evaluation stops, every template registered after it never registers, and
// the sign-in page comes up with "no template passwordInput found".
//
// A component whose .js is imported by other components cannot rely on the
// central import order in client/features/*.js. Importing the template here
// makes the order a fact rather than a hope; the module cache means the second
// import costs nothing.
import './exportScope.jade';
import { ReactiveDict } from 'meteor/reactive-dict';
import { Template } from 'meteor/templating';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import { Meteor } from 'meteor/meteor';
import { Session } from 'meteor/session';
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import { Accounts } from 'meteor/accounts-base';
import { BOARD_EXPORT_FIELDS } from '/models/lib/exportFields';
import { exportLocaleParams } from '/client/lib/exportLocale';
// The "what do you want in it" half of an export, for a board, a swimlane or a
// list (#1173 "Add Feature: Print Board with Params").
//
// One body, used by three popups. A board export, a swimlane export and a list
// export differ by ONE query parameter - which cards are in scope - so writing
// the checkbox list and the two download links three times would be three places
// for a section to go missing from. `exportScopeBody` is included by each popup
// with its own scope, and everything else is shared.
//
// The SELECTION is shared too, and deliberately: it is module-level, so ticking
// "no attachments, no comments" once applies to the next export as well, in the
// same session. Somebody printing a board rarely wants a different shape for
// each list of it.
const selection = new ReactiveDict();
BOARD_EXPORT_FIELDS.forEach(({ field }) => selection.set(field, true));
// `card-details` is what makes an export the card layout rather than the
// spreadsheet table; it starts on, like everything else.
selection.set('card-details', true);
function selectedFields() {
return ['card-details', ...BOARD_EXPORT_FIELDS.map(({ field }) => field)]
.filter((field, index, all) => all.indexOf(field) === index)
.filter(field => selection.get(field));
}
// The scope a popup was opened with: {} for a board, {swimlaneId} or {listId}
// for the other two.
function currentScope() {
const data = Template.currentData() || {};
const scope = {};
if (data.swimlaneId) scope.swimlaneId = data.swimlaneId;
if (data.listId) scope.listId = data.listId;
return scope;
}
// ONE url builder for every format and every scope. A second one would be a
// second place for a query parameter to go missing from - which is how the card
// popup's checkboxes ended up driving the Excel download and not the PDF.
// A CARD has export routes of its own - /lists/:listId/cards/:cardId/exportPDF
// and exportExcel - which produce the one card rather than a board document with
// one card in it. They are the same renderers underneath, so the popup uses them
// when it is a card being exported and the board routes otherwise, instead of
// there being two ways to ask for the same file.
function routeFor(path) {
const data = Template.currentData() || {};
if (!data.cardId || !data.listId) return path;
if (path.endsWith('exportPDF')) {
return '/api/boards/:boardId/lists/:listId/cards/:cardId/exportPDF';
}
if (path.endsWith('exportExcel')) {
return '/api/boards/:boardId/lists/:listId/cards/:cardId/exportExcel';
}
return path;
}
function exportUrl(path, extra = {}) {
const boardId = Session.get('currentBoard');
const data = Template.currentData() || {};
const route = routeFor(path);
const params = { boardId };
if (route.includes(':listId')) params.listId = data.listId;
if (route.includes(':cardId')) params.cardId = data.cardId;
return FlowRouter.path(route, params, {
authToken: Accounts._storedLoginToken(),
fields: selectedFields().join(','),
...currentScope(),
...exportLocaleParams(),
...extra,
});
}
// The import half's state. Module-level like the selection, because the popup is
// destroyed and recreated as it is reopened and a half-finished import should
// still be able to say how it went.
const importState = new ReactiveDict();
importState.set('busy', false);
importState.set('error', '');
importState.set('done', '');
// A WeKan export is either the document itself or a .zip with the document
// inside it. Reading the .zip in the BROWSER is what keeps the server out of it:
// the method takes the same object either way, so there is one import path and
// not two.
async function readExportFile(file) {
const name = String(file.name || '').toLowerCase();
if (!name.endsWith('.zip')) {
// A .json already carries its attachments as base64 under `file`, which is
// what the importer reads.
return JSON.parse(await file.text());
}
const JSZip = (await import('jszip')).default;
const zip = await JSZip.loadAsync(file);
const entry = zip.file('wekan.json') || zip.file(/wekan\.json$/)[0];
if (!entry) throw new Error('import-not-wekan-export');
const doc = JSON.parse(await entry.async('string'));
// The FILES beside the document. The export writes them as
// `attachments/<attachmentId>-<name>`, and the id is what ties a file back to
// its metadata row - two attachments called "photo.png" are two entries.
//
// They go onto the SAME `file` field a .json export uses, so the server has
// one import path rather than one per container. That is also the tradeoff to
// know about: the archive is unpacked in the browser and sent as base64, so a
// .zip full of large attachments costs memory on the way in. It is the shape
// the board import has always used, and the reason to reach for a .zip is
// still the same - the JSON of a board with many attachments may be too large
// for one string, while its archive is not.
const byId = new Map(
(Array.isArray(doc.attachments) ? doc.attachments : [])
.filter(attachment => attachment && attachment._id)
.map(attachment => [attachment._id, attachment]),
);
const files = zip.file(/^attachments\//);
for (const archived of files || []) {
const base = archived.name.slice('attachments/'.length);
const dash = base.indexOf('-');
const attachmentId = dash === -1 ? base : base.slice(0, dash);
const attachment = byId.get(attachmentId);
// A file whose row is not in the document is one the selection left out, or
// a hand-made archive: skipping it is better than inventing a row for it.
if (!attachment) continue;
attachment.file = await archived.async('base64');
}
return doc;
}
// A .json is small enough to travel as a document: it is parsed here and sent
// over DDP, which is the path the per-menu import has always used.
function importJsonDocument(doc, target) {
return new Promise((resolve, reject) => {
Meteor.call('importScoped', target, doc, selectedFields(),
(err, res) => (err ? reject(err) : resolve(res)));
});
}
// POST the archive itself to /api/import/zip. `fetch` streams a File body, so
// the browser does not hold a copy either.
async function uploadZipImport(file, target) {
const params = new URLSearchParams({
authToken: Accounts._storedLoginToken() || '',
boardId: target.boardId,
fields: selectedFields().join(','),
});
for (const key of ['swimlaneId', 'listId', 'cardId', 'checklistId']) {
if (target[key]) params.set(key, target[key]);
}
const response = await fetch(`/api/import/zip?${params.toString()}`, {
method: 'POST',
headers: { 'Content-Type': 'application/zip' },
body: file,
});
const answer = await response.json().catch(() => ({}));
if (!response.ok || !answer.ok) {
throw new Error(answer.error || 'import-scoped-failed');
}
return answer.counts;
}
// EVERY format this popup offers, in one table, for every scope.
//
// It was two markups: the board popup wrote its own list of nineteen formats in
// sidebar.jade under four subheadings, and the swimlane / list / card popups
// wrote five in exportScope.jade - so "the export popup" meant two different
// menus with two different looks, and a format added to one was missing from
// the other. One table, rendered by one template, is the answer to both.
//
// `scopes: ['board']` marks a format that only makes sense for a whole board -
// the HTML archive, the dependency graph, the CSV columns, and the exports
// written for another tool. Everything else is offered wherever the popup is,
// because `exportUrl` already carries the scope: a swimlane, a list and a card
// each differ from a board by a query parameter, not by a route.
//
// An entry is either a LINK (`path`, downloaded straight from the API) or an
// ACTION (`action`, a class an event handler is bound to). Nothing else varies.
const BOARD_ONLY = ['board'];
const EXPORT_FORMAT_GROUPS = [
{
key: 'files',
entries: [
{ key: 'pdf', icon: 'fa-file-pdf-o', label: 'PDF', path: 'exportPDF', ext: 'pdf' },
{ key: 'excel', icon: 'fa-file-excel-o', label: 'Excel', path: 'exportExcel', ext: 'xlsx' },
{ key: 'html', icon: 'fa-archive', label: 'HTML', action: 'html-export-board', scopes: BOARD_ONLY },
],
},
{
key: 'dependencies',
headingKey: 'card-dependencies',
scopes: BOARD_ONLY,
entries: [
{ key: 'dep-json', icon: 'fa-link', label: 'JSON', action: 'js-export-dependencies-json' },
{ key: 'dep-svg', icon: 'fa-link', label: 'SVG', action: 'js-export-dependencies-svg' },
],
},
{
key: 'csv',
heading: 'CSV',
scopes: BOARD_ONLY,
entries: [
{ key: 'csv', icon: 'fa-upload', label: '(,)', path: 'export/csv', ext: 'csv', query: { delimiter: ',' } },
{ key: 'scsv', icon: 'fa-upload', label: '(;)', path: 'export/csv', ext: 'csv', query: { delimiter: ';' } },
{ key: 'tsv', icon: 'fa-upload', label: 'TSV', path: 'export/csv', ext: 'tsv', query: { delimiter: '\t' } },
],
},
{
key: 'json',
heading: 'JSON',
entries: [
{ key: 'json', icon: 'fa-file-code-o', label: 'JSON', path: 'export', ext: 'json' },
{
key: 'json-no-attachments',
icon: 'fa-file-code-o',
labelKey: 'export-board-without-attachments',
labelPrefix: 'JSON',
path: 'export',
ext: 'json',
query: { attachments: 'false' },
},
{
key: 'zip',
icon: 'fa-file-archive-o',
labelKey: 'attachments',
labelPrefix: '.zip',
path: 'exportZip',
ext: 'zip',
},
{ key: 'kanboard', icon: 'fa-upload', label: 'Kanboard', path: 'export/kanboard', ext: 'json', scopes: BOARD_ONLY },
...[
['trello', 'Trello'], ['jira', 'Jira'], ['deck', 'NextCloud Deck'],
['openproject', 'OpenProject'], ['github', 'GitHub'], ['gitlab', 'GitLab'],
['gitea', 'Gitea'], ['forgejo', 'Forgejo'], ['asana', 'Asana'],
['zenkit', 'Zenkit'],
].map(([format, label]) => ({
key: format,
icon: 'fa-upload',
label,
path: `export/${format}`,
ext: 'json',
scopes: BOARD_ONLY,
})),
],
},
];
// Is this popup a whole board, or a swimlane / list / card inside one?
function isBoardScope() {
const data = Template.currentData() || {};
// checklistId too: the checklist popup is a scope of its own, and a scope
// that is not named here would be read as "a whole board" and offered the
// HTML archive, the dependency graph and the CSV columns of one.
return !data.swimlaneId && !data.listId && !data.cardId && !data.checklistId;
}
function entryApplies(entry) {
return !entry.scopes || entry.scopes.includes('board') === isBoardScope();
}
// The table above, resolved for the scope this popup was opened with: each entry
// carries its URL; the response supplies its localized, scope-aware filename.
// A group whose every entry is out of scope is dropped, heading and all.
function resolvedFormatGroups() {
return EXPORT_FORMAT_GROUPS
.filter(group => !group.scopes || group.scopes.includes('board') === isBoardScope())
.map(group => ({
key: group.key,
heading: group.headingKey ? TAPi18n.__(group.headingKey) : group.heading || '',
entries: group.entries.filter(entryApplies).map(entry => ({
...entry,
label: entry.labelKey
? `${entry.labelPrefix} (${TAPi18n.__(entry.labelKey)})`
: entry.label,
url: entry.path ? exportUrl(`/api/boards/:boardId/${entry.path}`, entry.query || {}) : '',
})),
}))
.filter(group => group.entries.length);
}
// Import writes to the board, so unlike export it is offered only to somebody
// who may change it. A named function rather than a helper body, because the
// menus that offer the Import row have to ask the same question - four copies of
// four permission checks is how one of them ends up offering a row that then
// refuses to do anything.
function canImportIntoBoard() {
const user = ReactiveCache.getCurrentUser();
return Boolean(user && !user.isWorker && !user.isCommentOnly
&& !user.isReadOnly && !user.isReadAssignedOnly);
}
Template.registerHelper('canImportIntoBoard', canImportIntoBoard);
const scopeHelpers = {
canImport() {
return canImportIntoBoard();
},
// Which half of the popup this is. One body, two modes: EXPORT shows the
// formats to download, IMPORT shows the file to read - and both keep the
// "what to include" pane, because that selection means the same thing in
// both directions (#1173).
isImportMode() {
const data = Template.currentData() || {};
return data.mode === 'import';
},
importBusy() { return importState.get('busy'); },
importError() { return importState.get('error'); },
importDone() { return importState.get('done'); },
exportFields() {
return BOARD_EXPORT_FIELDS.map(({ field, label }) => ({
field,
label,
checked: selection.get(field),
}));
},
cardDetailsChecked() {
return selection.get('card-details');
},
// Every format, resolved for this popup's scope - see EXPORT_FORMAT_GROUPS.
// #5870's "JSON without the base64 attachment data" is one of them, offered
// wherever an export is offered rather than only on the board menu.
formatGroups() {
return resolvedFormatGroups();
},
};
// One helper object, registered on both halves: the checkbox list is its own
// template so the layout can put it in a pane, and it asks the same questions.
Template.exportScopeBody.helpers(scopeHelpers);
Template.exportScopeSelect.helpers(scopeHelpers);
// THE TOGGLES, BOUND ON THE DOCUMENT - and deliberately not as a template event
// map.
//
// #6586 comment 5308548585: "I can't select/deselect those arrows here", and
// then "clicking a checked option, like labels, does not uncheck it". The
// handlers were a `Template.exportScopeBody.events({...})` map, and a click on a
// row did nothing. Registering the same map on `exportScopeSelect` as well - the
// template that actually draws the rows - did not help either: the fix was
// built, shipped in both the release and the development bundle, and the list
// still could not be changed.
//
// What IS known, from the built bundle: the templates are registered, their
// helpers run (the rows render, with their labels and their state), and both
// event maps are attached. The click simply never arrives. This list is drawn
// inside five different popups, each rendered by Popup into its own Blaze view
// tree, so rather than keep guessing which link in that chain drops the event,
// the toggle is bound where nothing can: one delegated handler on the document,
// the same mechanism client/lib/escapeActions.js uses to catch clicks inside
// popups.
//
// ONE binding, not one per template - two would toggle twice and cancel out,
// which is the same "nothing happens" from a different direction.
//
// The display stays reactive through `selection`, which is what draws the
// checkbox, so nothing here touches the DOM by hand.
function toggleSelection(field) {
if (!field) return;
selection.set(field, !selection.get(field));
}
// A NATIVE listener, in the CAPTURE phase, and no jQuery.
//
// Capture, because it runs on the way DOWN to the row: a `stopPropagation()`
// anywhere between the row and the document - which is one of the things that
// could have been eating this click - cannot prevent it. And native, because a
// `window.jQuery` that turned out to be undefined would fail silently, which is
// indistinguishable from the bug being fixed.
Meteor.startup(() => {
if (typeof document === 'undefined') return;
document.addEventListener('click', event => {
const target = event.target && event.target.closest
? event.target.closest('.js-export-field-toggle, .js-export-card-details-toggle')
: null;
if (!target) return;
// The popup stays open: choosing five sections should not be five reopens.
event.preventDefault();
toggleSelection(target.classList.contains('js-export-card-details-toggle')
? 'card-details'
: (target.dataset && target.dataset.field));
}, true);
});
Template.exportScopeBody.events({
async 'change .js-import-file'(event) {
const file = event.currentTarget.files && event.currentTarget.files[0];
if (!file) return;
const data = Template.currentData() || {};
const target = {
boardId: Session.get('currentBoard'),
...currentScope(),
};
// A card's menu imports below THAT card, so the target is the card the popup
// was opened on - the same id the export side uses as its scope.
if (data.cardId) target.cardId = data.cardId;
importState.set('error', '');
importState.set('done', '');
importState.set('busy', true);
try {
const counts = String(file.name || '').toLowerCase().endsWith('.zip')
// A .zip goes to the server AS A FILE. Unpacking it here and sending
// base64 over DDP is what made a large archive expensive: 2 GB of
// attachments became 2.7 GB in one message. The route streams it to
// disk, reads the archive's directory, and pipes one attachment at a
// time into storage.
? await uploadZipImport(file, target)
: await importJsonDocument(await readExportFile(file), target);
importState.set('done', Object.entries(counts || {})
.filter(([, n]) => n > 0)
.map(([what, n]) => `${n} ${what}`)
.join(', ') || '0');
} catch (error) {
const message = error && error.message === 'import-not-wekan-export'
? 'import-not-wekan-export'
: 'import-scoped-failed';
importState.set('error', message);
// The reason, for somebody reading the console - the popup has room for a
// sentence, not for a stack.
console.error('scoped import failed', error);
} finally {
importState.set('busy', false);
event.currentTarget.value = '';
}
},
});
// `exportUrlFor` is what the board popup uses to build the same URLs with the
// same selection - one query string, built in one place, whichever popup asks.
export {
selectedFields,
selection,
readExportFile,
exportUrl as exportUrlFor,
};
@@ -100,7 +100,7 @@
color: white;
padding: 2px 6px;
border-radius: 3px;
font-size: 11px;
font-size: calc(11px * var(--wekan-ui-font-scale, 1));
font-weight: 500;
text-transform: uppercase;
}
@@ -112,7 +112,7 @@
.entity-id {
color: #6c757d;
font-size: 11px;
font-size: calc(11px * var(--wekan-ui-font-scale, 1));
font-family: monospace;
}
@@ -123,12 +123,12 @@
.original-position-description {
color: #495057;
margin-bottom: 6px;
font-size: 13px;
font-size: calc(13px * var(--wekan-ui-font-scale, 1));
}
.original-title {
color: #6c757d;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
margin-bottom: 6px;
padding: 4px 6px;
background-color: #e9ecef;
@@ -141,7 +141,7 @@
.original-position-date {
color: #6c757d;
font-size: 11px;
font-size: calc(11px * var(--wekan-ui-font-scale, 1));
}
.no-original-positions {
@@ -152,7 +152,7 @@
}
.no-original-positions i {
font-size: 24px;
font-size: calc(24px * var(--wekan-ui-font-scale, 1));
margin-bottom: 10px;
display: block;
color: #adb5bd;
+1 -1
View File
@@ -1,5 +1,5 @@
/* /public — the Public Boards table.
* Design: docs/Design/Page/Public.md, which is docs/Design/Page/Table.md.
* Design: docs/Features/Page/Public.md, which is docs/Features/Page/Table.md.
*
* ONLY the row's board colouring lives here. The table itself — its width, its
* columns, its cells, its controls row and its stacking below 800px — is
+2 -2
View File
@@ -1,6 +1,6 @@
//- /public — the boards anybody may open.
//- Design: docs/Design/Page/Public.md, which is the Table page design
//- (docs/Design/Page/Table.md).
//- Design: docs/Features/Page/Public.md, which is the Table page design
//- (docs/Features/Page/Table.md).
//-
//- The whole page is the shared table. It used to render `boardList`, the All
//- Boards page, with its query swapped for `{ permission: 'public' }` — which
+2 -2
View File
@@ -10,7 +10,7 @@ import {
} from '/models/lib/tablePage';
// /public — the boards anybody may open, as a table page.
// Design: docs/Design/Page/Public.md (which is docs/Design/Page/Table.md).
// Design: docs/Features/Page/Public.md (which is docs/Features/Page/Table.md).
//
// The page is ONLY the table. It used to render `boardList`, the All Boards page,
// with its query swapped for `{ permission: 'public' }` — which brought the
@@ -88,7 +88,7 @@ Template.publicBoards.helpers({
// `boardListHeaderBar`, whose `h1` says "Public". The shared table page
// prints a title only when one is supplied, precisely so a page that
// already has a heading does not show two - the same reason the Admin
// Panel panes supply none (docs/Design/Page/Table.md).
// Panel panes supply none (docs/Features/Page/Table.md).
header: buildHeader(COLUMNS),
// A row template, not `rows`: see COLUMNS above.
rowTemplate: 'publicBoardRow',
+2 -2
View File
@@ -16,7 +16,7 @@
.stats-view-title {
margin: 0 0 16px;
font-size: 22px;
font-size: calc(22px * var(--wekan-ui-font-scale, 1));
font-weight: bold;
}
@@ -26,7 +26,7 @@
.stats-view-heading {
margin: 0 0 8px;
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
font-weight: bold;
border-bottom: 2px solid #dbdbdb;
padding-bottom: 4px;
+77 -3
View File
@@ -63,6 +63,57 @@
color: #333;
}
.table-view-controls .js-table-view-toggle-card-title-wrap,
.table-view-controls .js-table-view-toggle-swimlane-groups {
width: 34px;
height: 30px;
border: 1px solid #bbb;
background: #fff;
border-radius: 6px;
cursor: pointer;
}
.table-view-controls .js-table-view-toggle-card-title-wrap.active,
.table-view-controls .js-table-view-toggle-swimlane-groups.active {
background: #01628c;
color: #fff;
}
.my-cards-board-table tr.table-view-swimlane-group {
border-bottom: 0;
background: rgba(0, 0, 0, 0.08);
}
.my-cards-board-table tr.table-view-swimlane-group th {
padding: 8px 10px;
text-align: start;
font-size: 1.05rem;
}
/* Consume the width the board view actually has. Column percentages are
preferences, not caps: optional date/member columns may still claim the room
their content needs. */
.my-cards-board-table.table-view-table {
width: 100%;
max-width: none;
table-layout: auto;
}
.table-view-table th .js-table-view-sort {
border: 0;
background: transparent;
color: inherit;
cursor: pointer;
font: inherit;
font-weight: bold;
padding: 6px;
white-space: nowrap;
}
.table-view-table th .js-table-view-sort .fa {
margin-inline-start: 5px;
}
/* Leftmost "Edit" cell: an Edit link (pencil icon + text) styled like the one
on the Admin Panel / People page, opening the card when clicked. */
.my-cards-board-table td.table-view-cell-edit {
@@ -77,15 +128,38 @@
margin-inline-end: 4px;
}
/* Word-wrap the Card / List / Swimlane cells so long text breaks onto the next
line instead of overflowing across the other columns. */
/* List and swimlane text may wrap, but no longer impose a fixed 250px ceiling
on a wide browser. */
.my-cards-board-table td.table-view-cell-wrap {
max-width: 250px;
white-space: normal;
overflow-wrap: break-word;
word-break: break-word;
}
.my-cards-board-table td.table-view-cell-card-title {
width: 40%;
}
.my-cards-board-table td.table-view-cell-card-title .my-cards-card-title-table {
max-width: none;
min-width: 0;
}
.my-cards-board-table.table-view-ellipsis-card-titles
td.table-view-cell-card-title {
max-width: 0;
}
.my-cards-board-table.table-view-ellipsis-card-titles
td.table-view-cell-card-title .viewer,
.my-cards-board-table.table-view-ellipsis-card-titles
td.table-view-cell-card-title .viewer p {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* The +viewer template wraps its markdown in a block-level .viewer div. Inside
a table cell it should not add extra vertical margins, and inside a label it
must stay inline so the label keeps its pill shape. */
+37 -13
View File
@@ -13,34 +13,58 @@ template(name="tableView")
span.table-view-page-info {{currentPage}} / {{totalPages}}
button.js-table-view-next-page(type="button" class="{{#unless hasNextPage}}disabled{{/unless}}" aria-label="{{_ 'next'}}")
i.fa.fa-chevron-right
table.my-cards-board-table
button.js-table-view-toggle-card-title-wrap(type="button" class="{{#if wrapCardTitles}}active{{/if}}" title="{{_ 'card'}}" aria-label="{{_ 'card'}}" aria-pressed="{{wrapCardTitles}}")
i.fa(class="{{#if wrapCardTitles}}fa-align-left{{else}}fa-ellipsis-h{{/if}}")
button.js-table-view-toggle-swimlane-groups(type="button" class="{{#if groupBySwimlane}}active{{/if}}" title="{{_ 'swimlane'}}" aria-label="{{_ 'swimlane'}}" aria-pressed="{{groupBySwimlane}}")
i.fa(class="{{#if groupBySwimlane}}fa-object-group{{else}}fa-list{{/if}}")
table.my-cards-board-table.table-view-table(class="{{#if wrapCardTitles}}table-view-wrap-card-titles{{else}}table-view-ellipsis-card-titles{{/if}}")
caption.sr-only {{currentBoard.title}}
thead
tr
th(scope="col")
th(scope="col") {{_ "card"}}
th(scope="col") {{_ "list"}}
th(scope="col") {{_ "swimlane"}}
th(scope="col")
button.js-table-view-sort(type="button" data-field="title")
| {{_ "card"}}
i.fa(class="{{sortIcon 'title'}}")
th(scope="col")
button.js-table-view-sort(type="button" data-field="listTitle")
| {{_ "list"}}
i.fa(class="{{sortIcon 'listTitle'}}")
th(scope="col")
button.js-table-view-sort(type="button" data-field="swimlaneTitle")
| {{_ "swimlane"}}
i.fa(class="{{sortIcon 'swimlaneTitle'}}")
unless isMiniScreen
th(scope="col") {{_ "assignees"}}
th(scope="col") {{_ "members"}}
th(scope="col") {{_ "labels"}}
th(scope="col")
button.js-table-view-sort(type="button" data-field="assigneesKey") {{_ "assignees"}}
th(scope="col")
button.js-table-view-sort(type="button" data-field="membersKey") {{_ "members"}}
th(scope="col")
button.js-table-view-sort(type="button" data-field="labelsKey") {{_ "labels"}}
if showReceivedColumn
th(scope="col") {{_ "card-received"}}
th(scope="col")
button.js-table-view-sort(type="button" data-field="receivedAt") {{_ "card-received"}}
if showStartColumn
th(scope="col") {{_ "card-start"}}
th(scope="col")
button.js-table-view-sort(type="button" data-field="startAt") {{_ "card-start"}}
if showDueColumn
th(scope="col") {{_ "card-due"}}
th(scope="col")
button.js-table-view-sort(type="button" data-field="dueAt") {{_ "card-due"}}
if showEndColumn
th(scope="col") {{_ "card-end"}}
th(scope="col")
button.js-table-view-sort(type="button" data-field="endAt") {{_ "card-end"}}
tbody
each row in rows
tr
if row.isGroupHeader
tr.table-view-swimlane-group
th(colspan="11" scope="rowgroup")= row.swimlaneTitle
else
tr
td.table-view-cell-edit
a.edit-card.js-table-view-edit-card(href=row.card.originRelativeUrl data-card-id=row.card._id title="{{_ 'edit'}}")
i.fa.fa-pencil-square-o
| {{_ 'edit'}}
td.table-view-cell-wrap
td.table-view-cell-wrap.table-view-cell-card-title(title=row.title)
unless isMiniScreen
.my-cards-board-badge(class=row.colorClass)
.my-cards-card-title-table
+99 -9
View File
@@ -1,5 +1,20 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { Utils } from '/client/lib/utils';
import { Filter } from '/client/lib/filter';
import { tableViewCardsSelector } from '/models/lib/tableViewFilter';
import {
readTableViewTitleWrap,
writeTableViewTitleWrap,
} from '/models/lib/tableViewTitleMode';
import {
compareTableViewRows,
nextTableViewSort,
} from '/models/lib/tableViewSort';
import {
readTableViewGrouping,
writeTableViewGrouping,
addSwimlaneGroupHeaders,
} from '/models/lib/tableViewGrouping';
// Board "Table" view: lists every card of the current board in a table that
// reuses the My Cards table styling (the .my-cards-board-table CSS classes in
@@ -16,10 +31,18 @@ Template.tableView.onCreated(function () {
this.searchQuery = new ReactiveVar('');
this.page = new ReactiveVar(1);
this.filteredRows = new ReactiveVar([]);
this.wrapCardTitles = new ReactiveVar(
readTableViewTitleWrap(window.localStorage, Meteor.userId()),
);
this.sortField = new ReactiveVar('title');
this.sortDirection = new ReactiveVar('asc');
this.groupBySwimlane = new ReactiveVar(
readTableViewGrouping(window.localStorage, Meteor.userId()),
);
// Recompute the flat, filtered and sorted row list whenever the board cards,
// search query or sort order change. Pagination is applied separately in the
// rows() helper so paging does not rebuild the whole list.
// board Filter or search query changes. Pagination is applied separately in
// the rows() helper so paging does not rebuild the whole list.
this.autorun(() => {
const board = Utils.getCurrentBoard();
if (!board) {
@@ -28,9 +51,16 @@ Template.tableView.onCreated(function () {
}
const query = this.searchQuery.get().trim().toLowerCase();
const filterSelector = Filter.isActive()
? Filter._getMongoSelector()
: undefined;
const cards = ReactiveCache.getCards(
tableViewCardsSelector(board._id, filterSelector),
{ sort: { title: 1 } },
);
const rows = [];
board.cards().forEach(card => {
cards.forEach(card => {
const swimlane = card.getSwimlane();
const list = card.getList();
if (!swimlane || swimlane.archived || !list || list.archived) return;
@@ -47,12 +77,17 @@ Template.tableView.onCreated(function () {
title: card.title || '',
listTitle: list.title || '',
swimlaneTitle: swimlane.title || '',
swimlaneId: swimlane._id,
swimlaneSort: swimlane.sort || 0,
colorClass: board.colorClass(),
receivedAt: card.getReceived() || null,
startAt: card.getStart() || null,
dueAt: card.getDue() || null,
endAt: card.getEnd() || null,
labels,
assigneesKey: (card.assignees || []).join(' '),
membersKey: (card.members || []).join(' '),
labelsKey: labels.map(label => label.name).join(' '),
});
});
@@ -71,11 +106,20 @@ Template.tableView.onCreated(function () {
});
}
// Fixed order: by card title, ascending. (Column-header click-to-sort was
// removed; the Table view now always shows this stable order.)
filtered = filtered.slice().sort((a, b) =>
a.title.localeCompare(b.title, undefined, { numeric: true, sensitivity: 'base' }),
);
const sortField = this.sortField.get();
const sortDirection = this.sortDirection.get();
const groupBySwimlane = this.groupBySwimlane.get();
filtered = filtered.slice().sort((a, b) => {
if (groupBySwimlane) {
const laneOrder = a.swimlaneSort - b.swimlaneSort;
if (laneOrder !== 0) return laneOrder;
const laneTitle = a.swimlaneTitle.localeCompare(b.swimlaneTitle);
if (laneTitle !== 0) return laneTitle;
const laneId = a.swimlaneId.localeCompare(b.swimlaneId);
if (laneId !== 0) return laneId;
}
return compareTableViewRows(a, b, sortField, sortDirection);
});
this.filteredRows.set(filtered);
});
@@ -94,7 +138,10 @@ Template.tableView.helpers({
// page; no write here, to avoid a reactive loop.
const page = Math.min(tpl.page.get(), totalPages);
const start = (page - 1) * rowsPerPage;
return all.slice(start, start + rowsPerPage);
const pageRows = all.slice(start, start + rowsPerPage);
return tpl.groupBySwimlane.get()
? addSwimlaneGroupHeaders(pageRows)
: pageRows;
},
currentPage() {
@@ -119,6 +166,20 @@ Template.tableView.helpers({
return tpl.page.get() < totalPages;
},
wrapCardTitles() {
return Template.instance().wrapCardTitles.get();
},
sortIcon(field) {
const tpl = Template.instance();
if (tpl.sortField.get() !== field) return 'fa-sort';
return tpl.sortDirection.get() === 'asc' ? 'fa-sort-asc' : 'fa-sort-desc';
},
groupBySwimlane() {
return Template.instance().groupBySwimlane.get();
},
// A date column is shown unless BOTH its "Show at Card" (allowsXxxDate) and
// "Show at Minicard" (allowsXxxDateOnMinicard) board settings are unchecked.
showReceivedColumn() {
@@ -173,6 +234,35 @@ Template.tableView.events({
if (current < totalPages) tpl.page.set(current + 1);
},
'click .js-table-view-toggle-card-title-wrap'(event, tpl) {
event.preventDefault();
const wrap = !tpl.wrapCardTitles.get();
tpl.wrapCardTitles.set(wrap);
writeTableViewTitleWrap(window.localStorage, Meteor.userId(), wrap);
},
'click .js-table-view-sort'(event, tpl) {
event.preventDefault();
const selectedField = event.currentTarget.dataset.field;
if (!selectedField) return;
const next = nextTableViewSort(
tpl.sortField.get(),
tpl.sortDirection.get(),
selectedField,
);
tpl.sortField.set(next.field);
tpl.sortDirection.set(next.direction);
tpl.page.set(1);
},
'click .js-table-view-toggle-swimlane-groups'(event, tpl) {
event.preventDefault();
const enabled = !tpl.groupBySwimlane.get();
tpl.groupBySwimlane.set(enabled);
writeTableViewGrouping(window.localStorage, Meteor.userId(), enabled);
tpl.page.set(1);
},
// Clicking the leftmost "Edit" link opens the Card Details popup on top of the
// Board Table view (same mechanism as opening a card from search results).
'click .js-table-view-edit-card'(event) {
+22 -17
View File
@@ -80,6 +80,8 @@
color: #666;
}
#viewer-overlay {
display: flex;
flex-direction: column;
width: 100%;
height: 100vh;
position: fixed;
@@ -88,11 +90,16 @@
z-index: 9999 !important;
background: rgba(13, 13, 13, 0.95);
}
#viewer-overlay.hidden {
display: none;
}
#viewer-container {
display: flex;
flex-direction: row;
justify-content: space-between;
height: 100%;
flex: 1 1 auto;
min-height: 0;
width: 100%;
}
#viewer-top-bar {
display: flex;
@@ -100,6 +107,8 @@
justify-content: space-between;
width: 100%;
padding: 16px;
flex: 0 0 auto;
box-sizing: border-box;
}
#attachment-name {
color: white;
@@ -125,7 +134,7 @@
border-radius: 4px;
padding: 12px;
margin: 8px 0;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
}
.upload-progress-header {
@@ -187,7 +196,7 @@
.upload-progress-success {
display: flex;
align-items: center;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
font-weight: 500;
}
@@ -208,7 +217,7 @@
.minicard-upload-progress {
margin: 4px 0;
padding: 8px;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
}
.minicard-upload-progress .upload-progress-item {
@@ -217,7 +226,7 @@
}
.minicard-upload-progress .upload-progress-filename {
font-size: 11px;
font-size: calc(11px * var(--wekan-ui-font-scale, 1));
}
/* Card details specific styles */
@@ -227,7 +236,7 @@
}
.card-details-upload-progress .upload-progress-header {
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
margin-bottom: 12px;
}
@@ -237,7 +246,7 @@
}
.card-details-upload-progress .upload-progress-filename {
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
}
/* Drag over state for minicards */
@@ -276,8 +285,10 @@
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: calc(100% - 50px);
flex: 1 1 auto;
min-width: 0;
min-height: 0;
height: 100%;
}
#image-viewer {
background: repeating-conic-gradient(#808080 0% 25%, transparent 0% 50%) 50% /
@@ -293,12 +304,12 @@
max-width: 100%;
}
#pdf-viewer {
width: 560px;
width: 100%;
height: 100%;
}
#txt-viewer {
background-color: white;
width: 560px;
width: 100%;
height: 100%;
}
.pdf-preview-error {
@@ -308,12 +319,6 @@
color: white;
}
@media screen and (max-width: 1600px) {
#pdf-viewer {
width: 840px;
}
}
@media screen and (max-width: 800px) {
#viewer-container {
display: block;
+3 -3
View File
@@ -23,7 +23,7 @@ template(name="cardAttachmentsPopup")
template(name="previewClipboardImagePopup")
p <kbd>Ctrl</kbd>+<kbd>V</kbd> {{_ "paste-or-dragdrop"}}
img.preview-clipboard-image()
img.preview-clipboard-image(alt="{{_ 'preview'}}")
button.primary.js-upload-pasted-image {{_ 'upload'}}
template(name="attachmentDeletePopup")
@@ -62,9 +62,9 @@ template(name="attachmentGallery")
.attachment-thumbnail-container.open-preview(data-attachment-id="{{_id}}" data-card-id="{{ meta.cardId }}")
if link
if(isImage)
img.attachment-thumbnail(src="{{link}}" title="{{cleanFilename name}}")
img.attachment-thumbnail(src="{{link}}" title="{{cleanFilename name}}" alt="{{cleanFilename name}}")
else if($eq extension 'svg')
img.attachment-thumbnail(src="{{link}}" title="{{cleanFilename name}}" type="image/svg+xml")
img.attachment-thumbnail(src="{{link}}" title="{{cleanFilename name}}" alt="{{cleanFilename name}}" type="image/svg+xml")
else if($eq extension 'mp3')
video.attachment-thumbnail(title="{{cleanFilename name}}")
source(src="{{link}}" type="audio/mpeg")
+15 -42
View File
@@ -7,6 +7,7 @@ import uploadProgressManager from '../../lib/uploadProgressManager';
import { attachmentMigrationManager } from '/client/lib/attachmentMigrationManager';
import Attachments from '/models/attachments';
import { Utils } from '/client/lib/utils';
import { buildAttachmentUploadConfig } from '/client/lib/attachmentUploadConfig';
const { cleanFileName } = require('/imports/lib/fileNameDisplay');
import { formatDateTime } from '/imports/lib/dateUtils';
import { EscapeActions } from '/client/lib/escapeActions';
@@ -445,38 +446,16 @@ export async function handleFileUpload(card, files) {
continue;
}
const fileId = new ObjectId().toString();
let fileName = sanitizeText(file.name);
// If sanitized filename is not same as original filename,
// it could be XSS that is already fixed with sanitize,
// or just normal mistake, so it is not a problem.
// That is why here is no warning.
if (fileName !== file.name) {
// If filename is empty, only in that case add some filename
if (fileName.length === 0) {
fileName = 'Empty-filename-after-sanitize.txt';
}
}
const config = {
file: file,
fileId: fileId,
fileName: fileName,
// The id, the sanitized name, the chunk size and the transport all come
// from one builder - the file id has to be stamped into `meta` for
// Attachments' namingFunction to find it, and the transport has to be DDP
// on Sandstorm and HTTP everywhere else. Both are easy to leave out by hand
// and neither says so until an upload silently does not arrive.
// client/lib/attachmentUploadConfig.js
const config = buildAttachmentUploadConfig({
file,
meta: Utils.getCommonAttachmentMetaFrom(card),
chunkSize: 'dynamic',
// Use HTTP transport (a dedicated fetch POST per chunk) rather than DDP,
// which floods the WebSocket channel and causes repeated reconnects in
// Safari (the "Loading, please wait" offline banner, stalling at ~95%).
// EXCEPT on Sandstorm: the grain's sandstorm-http-bridge strips Meteor-Files'
// custom upload headers — x-start/x-mtok/x-chunkid/x-fileid/x-eof are not on
// Sandstorm's request-header whitelist — so the server never sees x-start,
// treats every request as a chunk continuation, and fails with "Can't
// continue upload, session expired" [408]. DDP uploads go over method calls
// (no custom HTTP headers), so they work through the bridge.
transport: Meteor.settings?.public?.sandstorm ? 'ddp' : 'http',
};
config.meta.fileId = fileId;
});
try {
const uploader = await Attachments.insertAsync(
@@ -485,7 +464,7 @@ export async function handleFileUpload(card, files) {
);
// Add to progress manager for tracking
const uploadId = uploadProgressManager.addUpload(card._id, uploader, file);
const uploadId = uploadProgressManager.addUpload(card.getRealId(), uploader, file);
uploader.on('uploaded', (error, fileRef) => {
if (!error) {
@@ -559,18 +538,12 @@ Template.previewClipboardImagePopup.events({
if (pastedResults && pastedResults.file) {
const file = pastedResults.file;
window.oPasted = pastedResults;
const fileId = new ObjectId().toString();
const config = {
// Same builder as the file-picker upload above; a pasted image has no
// name of its own, which the builder answers with the image's type.
const config = buildAttachmentUploadConfig({
file,
fileId: fileId,
meta: Utils.getCommonAttachmentMetaFrom(card),
fileName: file.name || file.type.replace('image/', 'clipboard.'),
chunkSize: 'dynamic',
// DDP on Sandstorm (its bridge strips Meteor-Files' x-* upload headers),
// HTTP elsewhere — see the note in the other upload handler above.
transport: Meteor.settings?.public?.sandstorm ? 'ddp' : 'http',
};
config.meta.fileId = fileId;
});
const uploader = await Attachments.insertAsync(
config,
false,
+84 -47
View File
@@ -1,35 +1,61 @@
//- The card's custom fields, from the hamburger at the end of the Custom Fields
heading: every field the BOARD has, with a tick when it is on this card, a
pencil to edit the field itself, and - under a rule - the one that makes a new
one. Everything about custom fields, in the place they are shown.
//-
//- Edit and Add open in this same pop-over, on top of this list, so the back
arrow returns to it. They are the board's own forms
(client/components/sidebar/sidebarCustomFields.jade), not second copies.
template(name="cardCustomFieldsPopup")
ul.pop-over-list
each board.customFields
li.item(class="")
a.name.js-select-field(href="#")
li.item
a.name.js-select-field(href="#" title="{{_ 'show-field-on-card'}}")
//- The same animated checkbox used by Admin Panel /
Settings / Announcement: the square itself morphs into
the green tick through forms.css.
.materialCheckBox(class="{{#if hasCustomField}}is-checked{{/if}}")
span.full-name
= name
if hasCustomField
i.fa.fa-check
a.js-edit-custom-field(title="{{_ 'edit'}}" aria-label="{{_ 'edit'}}")
i.fa.fa-pencil-square-o(aria-hidden="true")
hr
a.quiet-button.full.js-settings
i.fa.fa-cog
span {{_ 'settings'}}
a.quiet-button.full.js-open-create-custom-field
i.fa.fa-plus
span {{_ 'createCustomField'}}
template(name="cardCustomField")
h3.card-details-item-title.js-edit-card-custom-field-value(
class="{{#if canModifyCard}}is-editable{{/if}}"
title="{{#if canModifyCard}}{{_ 'edit'}}{{/if}}"
role="{{#if canModifyCard}}button{{/if}}"
tabindex="{{#if canModifyCard}}0{{else}}-1{{/if}}")
i.fa.fa-list
= definition.name
+Template.dynamic(template=getTemplate)
template(name="customFieldCopyButton")
span.custom-field-copy-control
a.fa.fa-copy.js-copy-custom-field(title="{{_ 'copy-text-to-clipboard'}}")
span.copied-tooltip {{_ 'copied'}}
template(name="cardCustomField-text")
if canModifyCard
+inlinedForm(classNames="js-card-customfield-text")
+editor(autofocus=true)
= value
+customFieldCopyButton(value=editValue)
+editor(autofocus=true hideCopy=true)
= editValue
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.js-close-inlined-form
a.fa.fa-times-thin.js-close-inlined-form(title="{{_ 'close'}}")
else
a.js-open-inlined-form
button.js-open-inlined-form.js-custom-field-edit-trigger(type="button" hidden)
a.js-edit-card-custom-field-value(title="{{_ 'edit'}}" aria-label="{{_ 'edit'}}")
if value
+viewer
= value
else
| {{_ 'edit'}}
i.fa.fa-pencil-square-o(aria-hidden="true")
else
+viewer
= value
@@ -38,48 +64,67 @@ template(name="cardCustomField-text")
template(name="cardCustomField-number")
if canModifyCard
+inlinedForm(classNames="js-card-customfield-number")
input(type="number" value=data.value)
+customFieldCopyButton(value=editValue)
input(type="number" value=editValue)
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.js-close-inlined-form
a.fa.fa-times-thin.js-close-inlined-form(title="{{_ 'close'}}")
else
a.js-open-inlined-form
button.js-open-inlined-form.js-custom-field-edit-trigger(type="button" hidden)
a.js-edit-card-custom-field-value(title="{{_ 'edit'}}" aria-label="{{_ 'edit'}}")
if formattedValue
= formattedValue
else
| {{_ 'edit'}}
i.fa.fa-pencil-square-o(aria-hidden="true")
else
if formattedValue
= formattedValue
template(name="cardCustomField-checkbox")
.js-checklist-item.checklist-item(class="{{#if data.value }}is-checked{{/if}}")
if canModifyCard
.check-box-container
.check-box.materialCheckBox(class="{{#if data.value}}is-checked{{/if}}")
//- This is a persisted card value, not a checklist item and not the
Custom Fields menu's visibility toggle. Keep its own event hook so no
generic checklist handler can interpret the click.
if canModifyCard
+inlinedForm(classNames="js-card-customfield-checkbox-editor")
+customFieldCopyButton(value=editValue)
label.check-box-container
input.js-card-customfield-checkbox-input(type="checkbox" checked=editValue)
.check-box.materialCheckBox(class="{{#if editValue}}is-checked{{/if}}")
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form(title="{{_ 'close'}}")
else
.materialCheckBox(class="{{#if data.value}}is-checked{{/if}}")
button.js-open-inlined-form.js-custom-field-edit-trigger(type="button" hidden)
.js-card-custom-field-checkbox.js-edit-card-custom-field-value.checklist-item(
class="{{#if value}}is-checked{{/if}}" title="{{_ 'edit'}}"
aria-label="{{_ 'edit'}}" role="button" tabindex="0")
.check-box-container
.check-box.materialCheckBox(class="{{#if value}}is-checked{{/if}}")
else
.materialCheckBox(class="{{#if value}}is-checked{{/if}}")
template(name="cardCustomField-currency")
if canModifyCard
+inlinedForm(classNames="js-card-customfield-currency")
input(type="text" value=data.value autofocus)
+customFieldCopyButton(value=editValue)
input(type="text" value=editValue autofocus)
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.js-close-inlined-form
a.fa.fa-times-thin.js-close-inlined-form(title="{{_ 'close'}}")
else
a.js-open-inlined-form
button.js-open-inlined-form.js-custom-field-edit-trigger(type="button" hidden)
a.js-edit-card-custom-field-value(title="{{_ 'edit'}}" aria-label="{{_ 'edit'}}")
if value
= formattedValue
else
| {{_ 'edit'}}
i.fa.fa-pencil-square-o(aria-hidden="true")
else
if value
= formattedValue
template(name="cardCustomField-date")
if canModifyCard
a.js-edit-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
a.js-edit-date.js-custom-field-edit-trigger(title="{{#if value}}{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}{{else}}{{_ 'edit'}}{{/if}}" aria-label="{{_ 'edit'}}" class="{{classes}}")
if value
div.card-date
time(datetime="{{showISODate}}")
@@ -88,7 +133,7 @@ template(name="cardCustomField-date")
b
| {{showWeek}}
else
| {{_ 'edit'}}
i.fa.fa-pencil-square-o(aria-hidden="true")
else
if value
div.card-date
@@ -99,26 +144,15 @@ template(name="cardCustomField-date")
| {{showWeek}}
template(name="cardCustomField-datePopup")
.datepicker-container
form.edit-date
.fields
.left
label(for="date") {{_ 'date'}}
input.js-date-field#date(type="date" name="date" value=showDate autofocus)
.right
label(for="time") {{_ 'time'}}
input.js-time-field#time(type="time" name="time" value=showTime)
if error.get
.warning {{_ error.get}}
button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}}
button.js-delete-date.negate.wide.right.js-delete-date {{_ 'delete'}}
+editDateForm(showDate=showDate showTime=showTime error=error datePicker=datePicker customFieldCopyValue=value customFieldControls=true)
template(name="cardCustomField-dropdown")
if canModifyCard
+inlinedForm(classNames="js-card-customfield-dropdown")
+customFieldCopyButton(value=selectedItem)
select.inline
each items
if($eq data.value this._id)
if isSelectedItem _id
option(value=_id selected="selected")
+viewer
= name
@@ -128,14 +162,15 @@ template(name="cardCustomField-dropdown")
= name
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.js-close-inlined-form
a.fa.fa-times-thin.js-close-inlined-form(title="{{_ 'close'}}")
else
a.js-open-inlined-form
button.js-open-inlined-form.js-custom-field-edit-trigger(type="button" hidden)
a.js-edit-card-custom-field-value(title="{{_ 'edit'}}" aria-label="{{_ 'edit'}}")
if value
+viewer
= selectedItem
else
| {{_ 'edit'}}
i.fa.fa-pencil-square-o(aria-hidden="true")
else
if value
+viewer
@@ -144,19 +179,21 @@ template(name="cardCustomField-dropdown")
template(name="cardCustomField-stringtemplate")
if canModifyCard
+inlinedForm(classNames="js-card-customfield-stringtemplate")
+customFieldCopyButton(value=formattedValue)
each item in stringtemplateItems.get
input.js-card-customfield-stringtemplate-item(type="text" value=item placeholder="")
input.js-card-customfield-stringtemplate-item.last(type="text" value="" placeholder="{{_ 'custom-field-stringtemplate-item-placeholder'}}" autofocus)
.edit-controls.clearfix
button.primary(type="submit") {{_ 'save'}}
a.js-close-inlined-form
a.fa.fa-times-thin.js-close-inlined-form(title="{{_ 'close'}}")
else
a.js-open-inlined-form
button.js-open-inlined-form.js-custom-field-edit-trigger(type="button" hidden)
a.js-edit-card-custom-field-value(title="{{_ 'edit'}}" aria-label="{{_ 'edit'}}")
if value
+viewer
= formattedValue
else
| {{_ 'edit'}}
i.fa.fa-pencil-square-o(aria-hidden="true")
else
if value
+viewer
+185 -35
View File
@@ -3,7 +3,6 @@ import {
setupDatePicker,
datePickerRendered,
datePickerHelpers,
datePickerEvents,
} from '/client/lib/datepicker';
import { ReactiveCache } from '/imports/reactiveCache';
import {
@@ -30,10 +29,56 @@ import {
import { CustomFieldStringTemplate } from '/client/lib/customFields'
import { getCurrentCardFromContext } from '/client/lib/currentCard';
import { formatNumberValue } from '/imports/lib/customNumberFormat';
import { EscapeActions } from '/client/lib/escapeActions';
import { getSidebarInstance } from '/client/features/sidebar/service';
import { Utils } from '/client/lib/utils';
import { subscribeDateNowTicker } from '/client/lib/dateNowTicker';
Template.customFieldCopyButton.events({
'click .js-copy-custom-field'(event, tpl) {
event.preventDefault();
event.stopPropagation();
const form = event.currentTarget.closest('form');
const checkbox = form?.querySelector('input[type="checkbox"]');
const select = form?.querySelector('select');
const date = form?.querySelector('input[type="date"]');
const time = form?.querySelector('input[type="time"]');
const stringItems = form?.querySelectorAll(
'.js-card-customfield-stringtemplate-item',
);
const editor = form?.querySelector(
'textarea, input[type="text"], input[type="number"]',
);
let rawValue = Template.currentData()?.value;
if (checkbox) {
rawValue = checkbox.checked;
} else if (select) {
rawValue = select.selectedOptions[0]?.textContent?.trim() ?? '';
} else if (date) {
rawValue = [date.value, time?.value].filter(Boolean).join(' ');
} else if (stringItems?.length) {
rawValue = Array.from(stringItems)
.map(input => input.value)
.filter(value => value.trim());
} else if (editor) {
rawValue = editor.value;
}
let value;
if (rawValue instanceof Date) {
value = rawValue.toISOString();
} else if (Array.isArray(rawValue)) {
value = rawValue.join('\n');
} else {
value = rawValue == null ? '' : String(rawValue);
}
const promise = Utils.copyTextToClipboard(value);
Utils.showCopied(promise, tpl.$('.copied-tooltip'));
},
});
Template.cardCustomFieldsPopup.helpers({
board() {
const card = getCurrentCardFromContext();
return card?.getRealBoard ? card.getRealBoard() : card?.board?.();
},
hasCustomField() {
const card = getCurrentCardFromContext();
if (!card) return false;
@@ -43,20 +88,32 @@ Template.cardCustomFieldsPopup.helpers({
});
Template.cardCustomFieldsPopup.events({
'click .js-select-field'(event) {
async 'click .js-select-field'(event) {
event.preventDefault();
const card = getCurrentCardFromContext();
if (!card) return;
const customFieldId = this._id;
card.toggleCustomField(customFieldId);
event.preventDefault();
},
'click .js-settings'(event) {
EscapeActions.executeUpTo('detailsPane');
const sidebar = getSidebarInstance();
if (sidebar) {
sidebar.setView('customFields');
const assigned = card.customFieldIndex(customFieldId) < 0;
try {
await Meteor.callAsync(
'setCardCustomFieldAssigned', card.getRealId(), customFieldId, assigned);
} catch (error) {
alert(error.reason || error.message || TAPi18n.__('server-error'));
}
event.preventDefault();
},
// Editing a field, and making one, open in this same pop-over on top of the
// list, so the back arrow returns to it and the card stays open behind. They
// are the board's own forms - the same templates the sidebar list opens - so
// there is one create form and one edit form, not a second pair for cards.
// client/components/sidebar/sidebarCustomFields.jade
'click .js-edit-custom-field': Popup.open('editCustomField'),
// "Add custom field" sits OUTSIDE the list, so its data context is this
// popup's own - which is the CARD. The form reads its context as the field
// being edited, and a card has an `_id`, so it was read as an edit of a
// custom field that does not exist and the new field was never inserted.
// A new field is made from nothing, so it is handed nothing.
'click .js-open-create-custom-field'(event) {
Popup.open('createCustomField').call({}, event);
},
});
@@ -72,12 +129,59 @@ Template.cardCustomField.onCreated(function () {
this.customFieldId = Template.currentData()._id;
});
function openCustomFieldValueEditor(event, tpl) {
// The Checkbox square is the direct on/off control. Only its surrounding row
// uses this shared editor route.
if (event.target.closest('.check-box-container')) return;
event.preventDefault();
event.stopPropagation();
const trigger = tpl.find('.js-custom-field-edit-trigger');
if (trigger) trigger.click();
}
function openCustomFieldValueEditorFromKeyboard(event) {
if (event.currentTarget.matches('a, button')) return;
if (event.key !== 'Enter' && event.key !== ' ') return;
event.preventDefault();
event.currentTarget.click();
}
Template.cardCustomField.events({
'click .js-edit-card-custom-field-value': openCustomFieldValueEditor,
'keydown .js-edit-card-custom-field-value': openCustomFieldValueEditorFromKeyboard,
});
function persistedEditValue() {
return Template.instance().data?.value;
}
// Titles belong to Template.cardCustomField, while displayed values belong to
// these nested type templates. Register on both sides of the Blaze boundary so
// every title, value and type-specific empty edit area behaves consistently.
[
'text',
'number',
'checkbox',
'currency',
'dropdown',
'stringtemplate',
].forEach(type => {
Template[`cardCustomField-${type}`].events({
'click .js-edit-card-custom-field-value': openCustomFieldValueEditor,
'keydown .js-edit-card-custom-field-value': openCustomFieldValueEditorFromKeyboard,
});
});
// cardCustomField-text
Template['cardCustomField-text'].onCreated(function () {
this.card = getCurrentCardFromContext();
this.customFieldId = Template.currentData()._id;
});
Template['cardCustomField-text'].helpers({
editValue: persistedEditValue,
});
Template['cardCustomField-text'].events({
'submit .js-card-customfield-text'(event, tpl) {
event.preventDefault();
@@ -93,6 +197,7 @@ Template['cardCustomField-number'].onCreated(function () {
});
Template['cardCustomField-number'].helpers({
editValue: persistedEditValue,
// Render blank / cleared / non-numeric values as empty instead of "NaN" (#2091).
formattedValue() {
return formatNumberValue(this.value);
@@ -117,9 +222,45 @@ Template['cardCustomField-checkbox'].onCreated(function () {
this.customFieldId = Template.currentData()._id;
});
Template['cardCustomField-checkbox'].helpers({
editValue: persistedEditValue,
});
Template['cardCustomField-checkbox'].events({
'click .js-checklist-item .check-box-container'(event, tpl) {
tpl.card.setCustomField(tpl.customFieldId, !Template.currentData().value);
'change .js-card-customfield-checkbox-input'(event, tpl) {
// Editing is staged until Save, but its square must still show the staged
// value immediately. The persisted `value` remains unchanged until submit,
// so update only the editor's visual checkbox here.
tpl.$('.js-card-customfield-checkbox-editor .materialCheckBox')
.toggleClass('is-checked', event.currentTarget.checked);
},
async 'submit .js-card-customfield-checkbox-editor'(event, tpl) {
event.preventDefault();
const value = Boolean(
tpl.find('.js-card-customfield-checkbox-input')?.checked,
);
try {
await Meteor.callAsync(
'setCardCustomFieldCheckbox', tpl.card.getRealId(), tpl.customFieldId, value);
} catch (error) {
alert(error.reason || error.message || TAPi18n.__('server-error'));
}
},
async 'click .js-card-custom-field-checkbox .check-box-container'(event, tpl) {
event.preventDefault();
event.stopPropagation();
// This template context is rebuilt from customFieldsWD whenever the saved
// value changes. Using the Card object captured at onCreated kept the old
// value after the first click, so a second click tried to save true again.
const currentField = Template.currentData();
if (!currentField || currentField._id !== tpl.customFieldId) return;
const value = !Boolean(currentField.value);
try {
await Meteor.callAsync(
'setCardCustomFieldCheckbox', tpl.card.getRealId(), tpl.customFieldId, value);
} catch (error) {
alert(error.reason || error.message || TAPi18n.__('server-error'));
}
},
});
@@ -131,6 +272,7 @@ Template['cardCustomField-currency'].onCreated(function () {
});
Template['cardCustomField-currency'].helpers({
editValue: persistedEditValue,
formattedValue() {
const locale = TAPi18n.getLanguage();
const tpl = Template.instance();
@@ -142,11 +284,21 @@ Template['cardCustomField-currency'].helpers({
});
Template['cardCustomField-currency'].events({
'submit .js-card-customfield-currency'(event, tpl) {
async 'submit .js-card-customfield-currency'(event, tpl) {
event.preventDefault();
// To allow input separated by comma, the comma is replaced by a period.
const value = Number(tpl.find('input').value.replace(/,/i, '.'), 10);
tpl.card.setCustomField(tpl.customFieldId, value);
const value = Number(tpl.find('input').value.trim().replace(/,/g, '.'));
if (!Number.isFinite(value)) return;
try {
await Meteor.callAsync(
'setCardCustomFieldCurrency',
tpl.card.getRealId(),
tpl.customFieldId,
value,
);
} catch (error) {
alert(error.reason || error.message || TAPi18n.__('server-error'));
}
},
});
@@ -156,10 +308,9 @@ Template['cardCustomField-date'].onCreated(function () {
this.customFieldId = Template.currentData()._id;
const self = this;
self.date = ReactiveVar();
self.now = ReactiveVar(now());
window.setInterval(() => {
self.now.set(now());
}, 60000);
const dateNowTicker = subscribeDateNowTicker();
self.now = dateNowTicker.now;
self.view.onViewDestroyed(dateNowTicker.unsubscribe);
self.autorun(() => {
self.date.set(new Date(Template.currentData().value));
@@ -200,6 +351,9 @@ Template['cardCustomField-date'].helpers({
},
});
// Date keeps its original direct popup opener. Clicking the title activates
// this same visible element through `.js-custom-field-edit-trigger`, while a
// click on the displayed datetime is handled here without a template hop.
Template['cardCustomField-date'].events({
'click .js-edit-date': Popup.open('cardCustomField-date'),
});
@@ -209,10 +363,11 @@ Template['cardCustomField-datePopup'].onCreated(function () {
const data = Template.currentData();
setupDatePicker(this, {
initialDate: data.value ? data.value : undefined,
storeDate: (date, card) => card.setCustomField(data._id, date),
deleteDate: card => card.setCustomField(data._id, ''),
});
// Override card and store customFieldId for store/delete callbacks
// A custom-field popup's data is the field definition, not the card.
this.datePicker.card = getCurrentCardFromContext();
this.customFieldId = data._id;
});
Template['cardCustomField-datePopup'].onRendered(function () {
@@ -221,20 +376,12 @@ Template['cardCustomField-datePopup'].onRendered(function () {
Template['cardCustomField-datePopup'].helpers(datePickerHelpers());
Template['cardCustomField-datePopup'].events(datePickerEvents({
storeDate(date) {
this.datePicker.card.setCustomField(this.customFieldId, date);
},
deleteDate() {
this.datePicker.card.setCustomField(this.customFieldId, '');
},
}));
// cardCustomField-dropdown
Template['cardCustomField-dropdown'].onCreated(function () {
const data = Template.currentData();
this.card = getCurrentCardFromContext();
this.customFieldId = Template.currentData()._id;
this._items = Template.currentData().definition.settings.dropdownItems;
this.customFieldId = data._id;
this._items = data.definition.settings.dropdownItems;
this.items = this._items.slice(0);
this.items.unshift({
_id: '',
@@ -246,6 +393,9 @@ Template['cardCustomField-dropdown'].helpers({
items() {
return Template.instance().items;
},
isSelectedItem(itemId) {
return (Template.instance().data.value ?? '') === itemId;
},
selectedItem() {
const tpl = Template.instance();
const selected = tpl._items.find(item => {
+41 -239
View File
@@ -1,289 +1,91 @@
template(name="dateBadge")
//- One date badge: the coloured date on a card or a minicard. Twelve templates
drew this, letter for letter - Received, Start, Due and End on the card and
again on the minicard, a vote's end, a poker's end and a custom field's date -
and each of them still has its own helpers and its own click, which is the
part that differs. The markup did not.
//-
//- It takes what it draws as ARGUMENTS: a helper is looked up on the template
it is written in, not on the one including it, so passing the values is what
lets one piece of markup serve twelve templates without moving any of their
state.
//-
//- `baseClass` is the class the caller's own markup carried - `card-date`, plus
`due-date` and friends on a minicard. A custom field's date passes NONE: it
was never a `.card-date` and must not become one now.
//- `canModifyCard` decides whether the badge is a link that opens the date
editor; a custom field's date is never one, and passes nothing.
template(name="dateBadgeBody")
if canModifyCard
a.js-edit-date.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
a.js-edit-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{baseClass}} {{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
a(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{baseClass}} {{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
template(name="dateBadge")
+dateBadgeBody(canModifyCard=canModifyCard baseClass="card-date" showTitle=showTitle classes=classes showISODate=showISODate showDate=showDate showWeekOfYear=showWeekOfYear showWeek=showWeek)
template(name="cardReceivedDate")
if canModifyCard
a.js-edit-date.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
+dateBadgeBody(canModifyCard=canModifyCard baseClass="card-date" showTitle=showTitle classes=classes showISODate=showISODate showDate=showDate showWeekOfYear=showWeekOfYear showWeek=showWeek)
template(name="cardStartDate")
if canModifyCard
a.js-edit-date.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
+dateBadgeBody(canModifyCard=canModifyCard baseClass="card-date" showTitle=showTitle classes=classes showISODate=showISODate showDate=showDate showWeekOfYear=showWeekOfYear showWeek=showWeek)
template(name="cardDueDate")
if canModifyCard
a.js-edit-date.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
+dateBadgeBody(canModifyCard=canModifyCard baseClass="card-date" showTitle=showTitle classes=classes showISODate=showISODate showDate=showDate showWeekOfYear=showWeekOfYear showWeek=showWeek)
template(name="cardEndDate")
if canModifyCard
a.js-edit-date.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
+dateBadgeBody(canModifyCard=canModifyCard baseClass="card-date" showTitle=showTitle classes=classes showISODate=showISODate showDate=showDate showWeekOfYear=showWeekOfYear showWeek=showWeek)
template(name="dateCustomField")
a(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
+dateBadgeBody(showTitle=showTitle classes=classes showISODate=showISODate showDate=showDate showWeekOfYear=showWeekOfYear showWeek=showWeek)
template(name="cardCustomFieldDate")
a(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
+dateBadgeBody(showTitle=showTitle classes=classes showISODate=showISODate showDate=showDate showWeekOfYear=showWeekOfYear showWeek=showWeek)
template(name="voteEndDate")
if canModifyCard
a.js-edit-date.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
+dateBadgeBody(canModifyCard=canModifyCard baseClass="card-date" showTitle=showTitle classes=classes showISODate=showISODate showDate=showDate showWeekOfYear=showWeekOfYear showWeek=showWeek)
template(name="pokerEndDate")
if canModifyCard
a.js-edit-date.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
+dateBadgeBody(canModifyCard=canModifyCard baseClass="card-date" showTitle=showTitle classes=classes showISODate=showISODate showDate=showDate showWeekOfYear=showWeekOfYear showWeek=showWeek)
template(name="minicardReceivedDate")
if canModifyCard
a.js-edit-date.card-date.received-date(title="{{_ 'card-received'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date.received-date(title="{{_ 'card-received'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
+dateBadgeBody(canModifyCard=canModifyCard showTitle="{{_ 'card-received'}}" baseClass="card-date received-date" classes=classes showISODate=showISODate showDate=showDate showWeekOfYear=showWeekOfYear showWeek=showWeek)
template(name="minicardStartDate")
if canModifyCard
a.js-edit-date.card-date.start-date(title="{{_ 'card-start'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date.start-date(title="{{_ 'card-start'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
+dateBadgeBody(canModifyCard=canModifyCard showTitle="{{_ 'card-start'}}" baseClass="card-date start-date" classes=classes showISODate=showISODate showDate=showDate showWeekOfYear=showWeekOfYear showWeek=showWeek)
template(name="minicardDueDate")
if canModifyCard
a.js-edit-date.card-date.due-date(title="{{_ 'card-due'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date.due-date(title="{{_ 'card-due'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
+dateBadgeBody(canModifyCard=canModifyCard showTitle="{{_ 'card-due'}}" baseClass="card-date due-date" classes=classes showISODate=showISODate showDate=showDate showWeekOfYear=showWeekOfYear showWeek=showWeek)
template(name="minicardEndDate")
if canModifyCard
a.js-edit-date.card-date.end-date(title="{{_ 'card-end'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
else
a.card-date.end-date(title="{{_ 'card-end'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
+dateBadgeBody(canModifyCard=canModifyCard showTitle="{{_ 'card-end'}}" baseClass="card-date end-date" classes=classes showISODate=showISODate showDate=showDate showWeekOfYear=showWeekOfYear showWeek=showWeek)
template(name="minicardCustomFieldDate")
a(title="{{_ 'date'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}")
time(datetime="{{showISODate}}")
| {{showDate}}
if showWeekOfYear
b
| {{showWeek}}
+dateBadgeBody(showTitle="{{_ 'date'}}" classes=classes showISODate=showISODate showDate=showDate showWeekOfYear=showWeekOfYear showWeek=showWeek)
template(name="editCardReceivedDatePopup")
.datepicker-container
form.edit-date
.fields
.left
label(for="date") {{_ 'date'}}
input.js-date-field#date(type="date" name="date" value=showDate autofocus)
.right
label(for="time") {{_ 'time'}}
input.js-time-field#time(type="time" name="time" value=showTime)
if error.get
.warning {{_ error.get}}
button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}}
button.js-delete-date.negate.wide.right.js-delete-date {{_ 'delete'}}
+editDateForm(showDate=showDate showTime=showTime error=error datePicker=datePicker)
template(name="editCardStartDatePopup")
.datepicker-container
form.edit-date
.fields
.left
label(for="date") {{_ 'date'}}
input.js-date-field#date(type="date" name="date" value=showDate autofocus)
.right
label(for="time") {{_ 'time'}}
input.js-time-field#time(type="time" name="time" value=showTime)
if error.get
.warning {{_ error.get}}
button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}}
button.js-delete-date.negate.wide.right.js-delete-date {{_ 'delete'}}
+editDateForm(showDate=showDate showTime=showTime error=error datePicker=datePicker)
template(name="editCardDueDatePopup")
.datepicker-container
form.edit-date
.fields
.left
label(for="date") {{_ 'date'}}
input.js-date-field#date(type="date" name="date" value=showDate autofocus)
.right
label(for="time") {{_ 'time'}}
input.js-time-field#time(type="time" name="time" value=showTime)
if error.get
.warning {{_ error.get}}
button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}}
button.js-delete-date.negate.wide.right.js-delete-date {{_ 'delete'}}
+editDateForm(showDate=showDate showTime=showTime error=error datePicker=datePicker)
template(name="editCardEndDatePopup")
.datepicker-container
form.edit-date
.fields
.left
label(for="date") {{_ 'date'}}
input.js-date-field#date(type="date" name="date" value=showDate autofocus)
.right
label(for="time") {{_ 'time'}}
input.js-time-field#time(type="time" name="time" value=showTime)
if error.get
.warning {{_ error.get}}
button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}}
button.js-delete-date.negate.wide.right.js-delete-date {{_ 'delete'}}
+editDateForm(showDate=showDate showTime=showTime error=error datePicker=datePicker)
template(name="editVoteEndDatePopup")
.datepicker-container
form.edit-date
.fields
.left
label(for="date") {{_ 'date'}}
input.js-date-field#date(type="date" name="date" value=showDate autofocus)
.right
label(for="time") {{_ 'time'}}
input.js-time-field#time(type="time" name="time" value=showTime)
if error.get
.warning {{_ error.get}}
button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}}
button.js-delete-date.negate.wide.right.js-delete-date {{_ 'delete'}}
+editDateForm(showDate=showDate showTime=showTime error=error datePicker=datePicker)
template(name="editPokerEndDatePopup")
.datepicker-container
form.edit-date
.fields
.left
label(for="date") {{_ 'date'}}
input.js-date-field#date(type="date" name="date" value=showDate autofocus)
.right
label(for="time") {{_ 'time'}}
input.js-time-field#time(type="time" name="time" value=showTime)
if error.get
.warning {{_ error.get}}
button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}}
button.js-delete-date.negate.wide.right.js-delete-date {{_ 'delete'}}
+editDateForm(showDate=showDate showTime=showTime error=error datePicker=datePicker)
+82 -69
View File
@@ -4,7 +4,6 @@ import {
setupDatePicker,
datePickerRendered,
datePickerHelpers,
datePickerEvents,
} from '/client/lib/datepicker';
import {
formatDateTime,
@@ -28,6 +27,7 @@ import {
calendar
} from '/imports/lib/dateUtils';
import { dueDateClass } from '/client/lib/dueDateColor';
import { subscribeDateNowTicker } from '/client/lib/dateNowTicker';
// --- DatePicker popups (edit date forms) ---
@@ -37,6 +37,12 @@ Template.editCardReceivedDatePopup.onCreated(function () {
setupDatePicker(this, {
defaultTime: formatDateTime(now()),
initialDate: card.getReceived() ? card.getReceived() : undefined,
storeDate(date, currentCard) {
return currentCard.setReceived(date);
},
deleteDate(currentCard) {
return currentCard.unsetReceived();
},
});
});
@@ -46,21 +52,18 @@ Template.editCardReceivedDatePopup.onRendered(function () {
Template.editCardReceivedDatePopup.helpers(datePickerHelpers());
Template.editCardReceivedDatePopup.events(datePickerEvents({
storeDate(date) {
this.datePicker.card.setReceived(date);
},
deleteDate() {
this.datePicker.card.unsetReceived();
},
}));
// editCardStartDatePopup
Template.editCardStartDatePopup.onCreated(function () {
const card = Template.currentData();
setupDatePicker(this, {
defaultTime: formatDateTime(now()),
initialDate: card.getStart() ? card.getStart() : undefined,
storeDate(date, currentCard) {
return currentCard.setStart(date);
},
deleteDate(currentCard) {
return currentCard.unsetStart();
},
});
});
@@ -70,21 +73,18 @@ Template.editCardStartDatePopup.onRendered(function () {
Template.editCardStartDatePopup.helpers(datePickerHelpers());
Template.editCardStartDatePopup.events(datePickerEvents({
storeDate(date) {
this.datePicker.card.setStart(date);
},
deleteDate() {
this.datePicker.card.unsetStart();
},
}));
// editCardDueDatePopup
Template.editCardDueDatePopup.onCreated(function () {
const card = Template.currentData();
setupDatePicker(this, {
defaultTime: '1970-01-01 17:00:00',
initialDate: card.getDue() ? card.getDue() : undefined,
storeDate(date, currentCard) {
return currentCard.setDue(date);
},
deleteDate(currentCard) {
return currentCard.unsetDue();
},
});
});
@@ -94,21 +94,18 @@ Template.editCardDueDatePopup.onRendered(function () {
Template.editCardDueDatePopup.helpers(datePickerHelpers());
Template.editCardDueDatePopup.events(datePickerEvents({
storeDate(date) {
this.datePicker.card.setDue(date);
},
deleteDate() {
this.datePicker.card.unsetDue();
},
}));
// editCardEndDatePopup
Template.editCardEndDatePopup.onCreated(function () {
const card = Template.currentData();
setupDatePicker(this, {
defaultTime: formatDateTime(now()),
initialDate: card.getEnd() ? card.getEnd() : undefined,
storeDate(date, currentCard) {
return currentCard.setEnd(date);
},
deleteDate(currentCard) {
return currentCard.unsetEnd();
},
});
});
@@ -118,24 +115,40 @@ Template.editCardEndDatePopup.onRendered(function () {
Template.editCardEndDatePopup.helpers(datePickerHelpers());
Template.editCardEndDatePopup.events(datePickerEvents({
storeDate(date) {
this.datePicker.card.setEnd(date);
},
deleteDate() {
this.datePicker.card.unsetEnd();
},
}));
// --- Card date badge display helpers ---
// Passing named arguments to a Blaze inclusion replaces the included
// template's data context with the argument object. Card details passes
// `canModifyCard`, while minicards and Table view inherit the Card directly.
// Keep both call shapes explicit so reactive reruns never try Card methods on
// `{ card, canModifyCard }` (#6615).
function cardFromDateContext(data = Template.currentData()) {
return data?.card || data;
}
// dateBadgeBody is a child template whose data is only display arguments. An
// event handled by the surrounding card/minicard date template therefore must
// open the popup with THAT surrounding template's Card, not with the event's
// `this`. The plus buttons do not need this because they already live directly
// in cardDetails with the Card as their data context.
function openDateEditor(name) {
return function (event, templateInstance) {
event.preventDefault();
event.stopPropagation();
Popup.open(name).call(
cardFromDateContext(templateInstance.data),
event,
templateInstance,
);
};
}
// Shared onCreated logic for card date badge templates
function cardDateOnCreated(tpl) {
tpl.date = new ReactiveVar();
tpl.now = new ReactiveVar(now());
window.setInterval(() => {
tpl.now.set(now());
}, 60000);
const dateNowTicker = subscribeDateNowTicker();
tpl.now = dateNowTicker.now;
tpl.view.onViewDestroyed(dateNowTicker.unsubscribe);
}
// Shared helpers for card date badge templates
@@ -166,7 +179,7 @@ Template.cardReceivedDate.onCreated(function () {
cardDateOnCreated(this);
const self = this;
self.autorun(() => {
self.date.set(new Date(Template.currentData().getReceived()));
self.date.set(new Date(cardFromDateContext().getReceived()));
});
});
@@ -174,7 +187,7 @@ Template.cardReceivedDate.helpers(cardDateHelpers({
classes() {
const tpl = Template.instance();
let classes = 'received-date ';
const data = Template.currentData();
const data = cardFromDateContext();
const dueAt = data.getDue();
const endAt = data.getEnd();
const startAt = data.getStart();
@@ -201,7 +214,7 @@ Template.cardReceivedDate.helpers(cardDateHelpers({
}));
Template.cardReceivedDate.events({
'click .js-edit-date': Popup.open('editCardReceivedDate'),
'click .js-edit-date': openDateEditor('editCardReceivedDate'),
});
// cardStartDate
@@ -209,7 +222,7 @@ Template.cardStartDate.onCreated(function () {
cardDateOnCreated(this);
const self = this;
self.autorun(() => {
self.date.set(new Date(Template.currentData().getStart()));
self.date.set(new Date(cardFromDateContext().getStart()));
});
});
@@ -217,7 +230,7 @@ Template.cardStartDate.helpers(cardDateHelpers({
classes() {
const tpl = Template.instance();
let classes = 'start-date ';
const data = Template.currentData();
const data = cardFromDateContext();
const dueAt = data.getDue();
const endAt = data.getEnd();
const theDate = tpl.date.get();
@@ -242,7 +255,7 @@ Template.cardStartDate.helpers(cardDateHelpers({
}));
Template.cardStartDate.events({
'click .js-edit-date': Popup.open('editCardStartDate'),
'click .js-edit-date': openDateEditor('editCardStartDate'),
});
// cardDueDate
@@ -250,14 +263,14 @@ Template.cardDueDate.onCreated(function () {
cardDateOnCreated(this);
const self = this;
self.autorun(() => {
self.date.set(new Date(Template.currentData().getDue()));
self.date.set(new Date(cardFromDateContext().getDue()));
});
});
Template.cardDueDate.helpers(cardDateHelpers({
classes() {
const tpl = Template.instance();
const data = Template.currentData();
const data = cardFromDateContext();
const endAt = data.getEnd();
const theDate = tpl.date.get();
const nowVal = tpl.now.get();
@@ -274,7 +287,7 @@ Template.cardDueDate.helpers(cardDateHelpers({
}));
Template.cardDueDate.events({
'click .js-edit-date': Popup.open('editCardDueDate'),
'click .js-edit-date': openDateEditor('editCardDueDate'),
});
// cardEndDate
@@ -282,7 +295,7 @@ Template.cardEndDate.onCreated(function () {
cardDateOnCreated(this);
const self = this;
self.autorun(() => {
self.date.set(new Date(Template.currentData().getEnd()));
self.date.set(new Date(cardFromDateContext().getEnd()));
});
});
@@ -290,7 +303,7 @@ Template.cardEndDate.helpers(cardDateHelpers({
classes() {
const tpl = Template.instance();
let classes = 'end-date ';
const data = Template.currentData();
const data = cardFromDateContext();
const dueAt = data.getDue();
const theDate = tpl.date.get();
@@ -312,7 +325,7 @@ Template.cardEndDate.helpers(cardDateHelpers({
}));
Template.cardEndDate.events({
'click .js-edit-date': Popup.open('editCardEndDate'),
'click .js-edit-date': openDateEditor('editCardEndDate'),
});
// cardCustomFieldDate
@@ -353,7 +366,7 @@ Template.minicardReceivedDate.onCreated(function () {
cardDateOnCreated(this);
const self = this;
self.autorun(() => {
self.date.set(new Date(Template.currentData().getReceived()));
self.date.set(new Date(cardFromDateContext().getReceived()));
});
});
@@ -361,7 +374,7 @@ Template.minicardReceivedDate.helpers(cardDateHelpers({
classes() {
const tpl = Template.instance();
let classes = 'received-date ';
const data = Template.currentData();
const data = cardFromDateContext();
const dueAt = data.getDue();
const endAt = data.getEnd();
const startAt = data.getStart();
@@ -393,7 +406,7 @@ Template.minicardReceivedDate.helpers(cardDateHelpers({
}));
Template.minicardReceivedDate.events({
'click .js-edit-date': Popup.open('editCardReceivedDate'),
'click .js-edit-date': openDateEditor('editCardReceivedDate'),
});
// minicardStartDate
@@ -401,7 +414,7 @@ Template.minicardStartDate.onCreated(function () {
cardDateOnCreated(this);
const self = this;
self.autorun(() => {
self.date.set(new Date(Template.currentData().getStart()));
self.date.set(new Date(cardFromDateContext().getStart()));
});
});
@@ -409,7 +422,7 @@ Template.minicardStartDate.helpers(cardDateHelpers({
classes() {
const tpl = Template.instance();
let classes = 'start-date ';
const data = Template.currentData();
const data = cardFromDateContext();
const dueAt = data.getDue();
const endAt = data.getEnd();
const theDate = tpl.date.get();
@@ -439,7 +452,7 @@ Template.minicardStartDate.helpers(cardDateHelpers({
}));
Template.minicardStartDate.events({
'click .js-edit-date': Popup.open('editCardStartDate'),
'click .js-edit-date': openDateEditor('editCardStartDate'),
});
// minicardDueDate
@@ -447,14 +460,14 @@ Template.minicardDueDate.onCreated(function () {
cardDateOnCreated(this);
const self = this;
self.autorun(() => {
self.date.set(new Date(Template.currentData().getDue()));
self.date.set(new Date(cardFromDateContext().getDue()));
});
});
Template.minicardDueDate.helpers(cardDateHelpers({
classes() {
const tpl = Template.instance();
const data = Template.currentData();
const data = cardFromDateContext();
const endAt = data.getEnd();
const theDate = tpl.date.get();
const nowVal = tpl.now.get();
@@ -476,7 +489,7 @@ Template.minicardDueDate.helpers(cardDateHelpers({
}));
Template.minicardDueDate.events({
'click .js-edit-date': Popup.open('editCardDueDate'),
'click .js-edit-date': openDateEditor('editCardDueDate'),
});
// minicardEndDate
@@ -484,7 +497,7 @@ Template.minicardEndDate.onCreated(function () {
cardDateOnCreated(this);
const self = this;
self.autorun(() => {
self.date.set(new Date(Template.currentData().getEnd()));
self.date.set(new Date(cardFromDateContext().getEnd()));
});
});
@@ -492,7 +505,7 @@ Template.minicardEndDate.helpers(cardDateHelpers({
classes() {
const tpl = Template.instance();
let classes = 'end-date ';
const data = Template.currentData();
const data = cardFromDateContext();
const dueAt = data.getDue();
const theDate = tpl.date.get();
@@ -519,7 +532,7 @@ Template.minicardEndDate.helpers(cardDateHelpers({
}));
Template.minicardEndDate.events({
'click .js-edit-date': Popup.open('editCardEndDate'),
'click .js-edit-date': openDateEditor('editCardEndDate'),
});
// minicardCustomFieldDate
@@ -556,7 +569,7 @@ Template.voteEndDate.onCreated(function () {
cardDateOnCreated(this);
const self = this;
self.autorun(() => {
self.date.set(new Date(Template.currentData().getVoteEnd()));
self.date.set(new Date(cardFromDateContext().getVoteEnd()));
});
});
@@ -576,7 +589,7 @@ Template.voteEndDate.helpers(cardDateHelpers({
}));
Template.voteEndDate.events({
'click .js-edit-date': Popup.open('editVoteEndDate'),
'click .js-edit-date': openDateEditor('editVoteEndDate'),
});
// pokerEndDate
@@ -584,7 +597,7 @@ Template.pokerEndDate.onCreated(function () {
cardDateOnCreated(this);
const self = this;
self.autorun(() => {
self.date.set(new Date(Template.currentData().getPokerEnd()));
self.date.set(new Date(cardFromDateContext().getPokerEnd()));
});
});
@@ -604,5 +617,5 @@ Template.pokerEndDate.helpers(cardDateHelpers({
}));
Template.pokerEndDate.events({
'click .js-edit-date': Popup.open('editPokerEndDate'),
'click .js-edit-date': openDateEditor('editPokerEndDate'),
});
+227 -35
View File
@@ -4,7 +4,7 @@
}
.card-details-item-date-format .card-details-item-title {
font-size: 15px;
font-size: calc(15px * var(--wekan-ui-font-scale, 1));
font-weight: bold;
margin-bottom: 6px;
color: #333;
@@ -17,7 +17,7 @@
border: 1px solid #ddd;
border-radius: 5px;
background-color: #fff;
font-size: 15px;
font-size: calc(15px * var(--wekan-ui-font-scale, 1));
cursor: pointer;
transition: border-color 0.15s, box-shadow 0.15s;
}
@@ -138,7 +138,10 @@
body.desktop-mode .card-details:not(.card-details-popup) {
position: fixed;
width: auto;
max-width: 520px; /* #6465: narrower dock (was 800px, ~1/4 screen) */
/* Open at the compact dock width below, but let the bottom-right native
resize handle grow the card up to the viewport edge. A fixed 520px cap
made the handle capable of shrinking only. */
max-width: calc(100vw - 16px);
flex-basis: auto;
border-radius: 3px;
z-index: 2001;
@@ -149,7 +152,7 @@ body.desktop-mode .card-details:not(.card-details-popup):not([style*="left"]):no
top: 8px;
inset-inline-start: auto;
inset-inline-end: 8px;
width: min(650px, 90vw);
width: min(520px, 90vw);
bottom: 8px;
}
@@ -207,7 +210,7 @@ body.desktop-mode .card-details:not(.card-details-popup):not(.card-details-colla
canvas (off-screen) instead of over the viewport.
All four insets are !important for two reasons:
1. The floating-window rules above (top: 8px / inset-inline-end: 8px /
width: min(650px, 90vw), specificity up to 0,5,1) otherwise override the
width: min(520px, 90vw), specificity up to 0,5,1) otherwise override the
lower-specificity .card-details-maximized geometry entirely, leaving the
Maximize button a no-op.
2. The drag handle writes inline left/top styles onto the card; a card that
@@ -243,6 +246,8 @@ body.desktop-mode .card-details.card-details-collapsed {
}
.card-details .card-details-canvas {
width: auto;
box-sizing: border-box;
min-width: 0;
/* #6465: fixed 20px as in 6.09 — 35px was ~48px of white border per side
on a FullHD screen. */
padding: 0 20px;
@@ -267,7 +272,7 @@ body.desktop-mode .card-details.card-details-collapsed {
/* Collapse toggle triangle */
.card-details .card-details-header .card-collapse-toggle {
float: inline-start;
font-size: 20px;
font-size: calc(20px * var(--wekan-ui-font-scale, 1));
padding: 7px 10px;
margin-inline-start: -10px;
margin-inline-end: 5px;
@@ -279,7 +284,7 @@ body.desktop-mode .card-details.card-details-collapsed {
}
.card-details .card-details-header .card-drag-handle {
font-size: 20px;
font-size: calc(20px * var(--wekan-ui-font-scale, 1));
padding: 8px 10px;
margin-inline-end: 10px;
cursor: move;
@@ -304,7 +309,7 @@ body.desktop-mode .card-details.card-details-collapsed {
.card-details .card-details-header .close-card-details,
.card-details .card-details-header .maximize-card-details,
.card-details .card-details-header .minimize-card-details {
font-size: 24px;
font-size: calc(24px * var(--wekan-ui-font-scale, 1));
padding: 5px 10px 5px 10px;
margin-inline-end: -8px;
cursor: pointer;
@@ -315,37 +320,37 @@ body.desktop-mode .card-details.card-details-collapsed {
}
.card-details .card-details-header .close-card-details-mobile-web,
.card-details .card-details-header .card-mobile-desktop-toggle {
font-size: 24px;
font-size: calc(24px * var(--wekan-ui-font-scale, 1));
padding: 5px;
margin-inline-end: 5px;
cursor: pointer;
user-select: none;
}
.card-details .card-details-header .card-copy-button {
font-size: 17px;
font-size: calc(17px * var(--wekan-ui-font-scale, 1));
padding: 10px;
margin-inline-end: 10px;
}
.card-details .card-details-header .card-copy-mobile-button {
font-size: 17px;
font-size: calc(17px * var(--wekan-ui-font-scale, 1));
padding: 10px;
margin-inline-end: 10px;
}
.card-details .card-details-header .card-details-menu {
font-size: 17px;
font-size: calc(17px * var(--wekan-ui-font-scale, 1));
padding: 10px;
vertical-align: middle;
line-height: 1.2;
}
.card-details .card-details-header .card-details-menu-mobile-web {
font-size: 17px;
font-size: calc(17px * var(--wekan-ui-font-scale, 1));
padding: 10px;
margin-inline-end: 30px;
}
.card-details .card-details-header .card-mobile-desktop-toggle,
.card-details .card-details-header .card-zoom-in,
.card-details .card-details-header .card-zoom-out {
font-size: 24px;
font-size: calc(24px * var(--wekan-ui-font-scale, 1));
padding: 5px 10px 5px 10px;
margin-inline-end: 5px;
cursor: pointer;
@@ -362,7 +367,7 @@ body.desktop-mode .card-details.card-details-collapsed {
font-size: 1em;
}
.card-details .card-details-header .card-details-watch {
font-size: 17px;
font-size: calc(17px * var(--wekan-ui-font-scale, 1));
padding-inline-start: 7px;
color: #a6a6a6;
}
@@ -375,6 +380,46 @@ body.desktop-mode .card-details.card-details-collapsed {
vertical-align: middle;
line-height: 1.3;
letter-spacing: 0.01em;
position: relative;
}
/* The title is two things at once, split like a minicard's: the LEADING half
opens the editor, the trailing half drags the window. With drag handles OFF
there is no handle, so the title bar is what the window is moved by - and a
heading that is entirely an edit target leaves nothing to take hold of.
An overlay rather than a split of the text, because the text is a `.viewer`
and cutting it in two would cut the sentence in two. `inset-inline-start`, so
the edit half is the left in English and the right in Arabic from one rule.
The drag handler steps aside for this zone (cardDetails.js). */
.card-details .card-details-header .card-details-title .card-details-title-edit-zone {
position: absolute;
top: 0;
bottom: 0;
inset-inline-start: 0;
/* Half of the heading - and the heading is stretched to the row below, so
this is half of the BAR however long the title happens to be. */
width: 50%;
cursor: text;
z-index: 1;
}
/* The heading fills the row rather than shrinking to its text, so "half the
title" and "half the bar" are the same half however long the title is. A
heading only as wide as its words leaves the empty space beside it belonging
to nothing - and that space is where anybody would grab to move the window.
It stays inline-block so the complete-toggle beside it keeps its place; the
buttons floating at the trailing edge take their room out of this width. */
.card-details .card-details-header .card-details-title {
min-width: 60%;
}
/* With drag handles ON the handle is the only drag source, so the whole title
is the edit target - the same rule the minicard follows, from the same
question: `card-details-with-handle` is set by the template from
isTouchScreenOrShowDesktopDragHandles, which is Utils.showDragHandles(). */
.card-details.card-details-with-handle .card-details-header .card-details-title .card-details-title-edit-zone {
width: 100%;
}
.card-details .card-details-header .linked-card-location {
font-style: italic;
@@ -454,10 +499,19 @@ body.desktop-mode .card-details.card-details-collapsed {
padding-inline-start: 10px;
}
.card-details .card-details-item-title {
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
font-weight: bold;
color: #4d4d4d;
}
.card-details .card-details-item-customfield .js-edit-card-custom-field-value {
cursor: pointer;
}
.card-details .card-details-person-controls {
align-items: flex-start;
display: flex;
flex-direction: column;
gap: 0.25em;
}
.card-details .activities {
padding-top: 10px;
}
@@ -920,7 +974,7 @@ body.mobile-mode .card-details .card-details-header .close-card-details-mobile-w
box-sizing: border-box;
padding: 5px;
margin: 3px;
font-size: 20px;
font-size: calc(20px * var(--wekan-ui-font-scale, 1));
font-weight: bold;
text-shadow: #2d2d2d 1px 1px 0;
box-shadow: 0 0 5px #aaa;
@@ -946,8 +1000,15 @@ body.mobile-mode .card-details .card-details-header .close-card-details-mobile-w
}
/* Sticker picker + card-detail stickers + due-complete toggle */
/* The picker fills the popup it is in rather than stopping at eight columns:
the popup is wide now (popup.css), and a fixed 320px there would have left
the extra width empty while the icons still ran off the bottom. A grid that
fits as many 40px cells as there is room for turns that width into rows the
reader does not have to scroll past. */
.card-stickers-picker {
max-width: 320px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));
justify-items: center;
}
.card-sticker-select {
display: inline-flex;
@@ -957,7 +1018,7 @@ body.mobile-mode .card-details .card-details-header .close-card-details-mobile-w
height: 36px;
margin: 2px;
border-radius: 3px;
font-size: 18px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
color: #4d4d4d;
cursor: pointer;
position: relative;
@@ -973,18 +1034,18 @@ body.mobile-mode .card-details .card-details-header .close-card-details-mobile-w
position: absolute;
bottom: 1px;
inset-inline-end: 2px;
font-size: 10px;
font-size: calc(10px * var(--wekan-ui-font-scale, 1));
color: #3cb500;
}
.card-stickers .card-sticker {
display: inline-flex;
align-items: center;
margin: 0 8px 4px 0;
font-size: 18px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
}
.card-sticker .card-sticker-remove {
margin-inline-start: 3px;
font-size: 11px;
font-size: calc(11px * var(--wekan-ui-font-scale, 1));
color: #999;
cursor: pointer;
}
@@ -1065,19 +1126,6 @@ body.mobile-mode .card-details .card-details-header .close-card-details-mobile-w
box-sizing: border-box;
}
/* "Open map links at" provider selector in the location popup */
.js-card-location-form .map-provider-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-bottom: 6px;
}
.js-card-location-form .map-provider-row .js-map-provider {
flex: 0 0 auto;
width: auto;
}
/* Opened card (card details popup) docks to the very top of the window and
overlays the global + board header bars instead of opening below them. The
base .pop-over is position:absolute with a small margin/top offset, which left
@@ -1146,3 +1194,147 @@ body.mobile-mode .card-details .card-details-header .close-card-details-mobile-w
user-select: none;
margin: 8px 0;
}
/* #1591 gave every FIELD of the opened card a caret of its own on its title,
and hid everything after that title when it was folded. The section carets
replaced it - one handle per section, on the heading - and the two ran at the
same time: clicking a section's caret also folded the field the heading was
drawn on, and with the field's own caret gone there was nothing left to open
it. That is where the Date Format select went. Both halves are gone from
here; client/components/cards/cardDetails.js says the same at the handler. */
/* Collapsible card sections.
Every section of an opened card - Labels, Date Format, Members, Dependencies,
Sort, Custom Fields, Description, Checklists, Subtasks, Attachments, Comments
and Activities - has one rule above it and a caret in its heading that
collapses it. The rule comes from the same template as the caret, so eleven
sections cannot end up with ten separators.
The heading is a button: it says so to a screen reader (role/tabindex in the
template) and it looks like one to a mouse. */
/* The GROUP is the full-width row of the card, and the fields inside it share
rows within it - which is what puts Assignee and Creator beside Members
rather than under it. The class that made an individual field full width is
gone with the grouping; a field that is its own section is a group of one. */
.card-details .card-details-section-rule {
width: 100%;
margin: 14px 0 6px 0;
}
.card-details h3.card-details-item-title.js-toggle-card-section {
cursor: pointer;
user-select: none;
display: flex;
align-items: center;
gap: 6px;
}
/* The hamburger that opens a section's settings, at the FAR end of its heading
- `margin-inline-start: auto` in a flex row, so it mirrors itself in a
right-to-left page. It is inside a heading that folds the section when
clicked, so its own handler stops the click there. */
.card-details h3.card-details-item-title .card-details-section-menu {
margin-inline-start: auto;
padding: 0 4px;
color: #808080;
cursor: pointer;
}
.card-details h3.card-details-item-title .card-details-section-menu:hover {
color: #000;
}
/* The caret is the widest part of the heading that must not move as the label
changes length, so it gets a fixed box. `text-align: center` rather than a
side, because the glyph itself differs between the open and closed states
(down, and right or left) and a fixed side would make it jump. */
.card-details h3.card-details-item-title.js-toggle-card-section > .fa:first-child {
width: 12px;
text-align: center;
flex: 0 0 auto;
}
.card-details h3.card-details-item-title.js-toggle-card-section:hover {
opacity: 0.85;
}
/* The checklist "3/7" line that used to be a second h3 under the Checklists
heading: a subtitle, so it is not read as another section. */
.card-details .card-details-item-subtitle {
margin: 0 0 4px 0;
font-size: calc(13px * var(--wekan-ui-font-scale, 1));
font-weight: 400;
opacity: 0.8;
}
/* A GROUP of card fields - Labels with Stickers and Location, Date Format with
the four dates, Members with Assignee and Creator, and so on.
The group's own title carries the caret that folds it, and the fields inside
it do NOT: #1591 gives every .card-details-item a caret of its own, drawn on
the title by the rule further up this file, and inside a group that is a
second caret on every row saying the same thing in a smaller way. One handle
per group is what was asked for, and it is the group's.
The fields keep sharing rows inside the group, which is how they were laid
out - the group is a full-width row, the fields inside it wrap within it. */
.card-details .card-details-items .card-details-group {
flex: 0 0 100%;
width: 100%;
}
.card-details .card-details-group-body {
display: flex;
flex-wrap: wrap;
gap: 0.5em;
}
.card-details .card-details-group-body .card-details-item {
flex-grow: 1;
}
/* The user's Custom Fields layout toggle has two deliberately distinct modes.
Off keeps the compact wrapping grid; on gives every field its own row. */
.card-details .card-details-group-custom-fields .custom-fields-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(160px, 100%), 1fr));
}
.card-details .card-details-group-custom-fields .custom-fields-grid > .card-details-item-customfield {
margin-inline-end: 0;
max-width: none;
min-width: 0;
}
.card-details .card-details-group-custom-fields .custom-fields-one-per-row > .card-details-item-customfield {
flex: 0 0 100%;
margin-inline-end: 0;
max-width: 100%;
}
/* The same paired-icon language as the header's Mobile / Desktop selector:
both choices remain visible and the active layout is the filled chip. */
.card-details .custom-fields-layout-toggle {
align-items: center;
background: #fff;
border: 1px solid #999;
border-radius: 4px;
color: #4d4d4d;
cursor: pointer;
display: inline-flex;
gap: 2px;
margin: 0;
margin-inline-start: auto;
padding: 2px;
width: auto;
}
.card-details h3.card-details-item-title .custom-fields-layout-toggle + .card-details-section-menu {
margin-inline-start: 0;
}
.card-details .custom-fields-layout-toggle .grid-icon,
.card-details .custom-fields-layout-toggle .list-icon {
align-items: center;
border-radius: 3px;
display: inline-flex;
justify-content: center;
min-width: 28px;
opacity: 0.35;
padding: 4px 7px;
transition: opacity 0.2s ease, background-color 0.2s ease;
}
.card-details .custom-fields-layout-toggle .grid-icon.active,
.card-details .custom-fields-layout-toggle .list-icon.active {
background: var(--theme-accent, #2980b9);
opacity: 1;
}
.card-details .custom-fields-layout-toggle .grid-icon.active .fa,
.card-details .custom-fields-layout-toggle .list-icon.active .fa {
color: #fff;
}
File diff suppressed because it is too large. Load diff
+370 -314
View File
@@ -1,5 +1,6 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
import { ReactiveDict } from 'meteor/reactive-dict';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import { ReactiveVar } from 'meteor/reactive-var';
import {
@@ -21,7 +22,6 @@ import {
setupDatePicker,
datePickerRendered,
datePickerHelpers,
datePickerEvents,
} from '/client/lib/datepicker';
import {
formatDateTime,
@@ -72,91 +72,20 @@ import { EscapeActions } from '/client/lib/escapeActions';
import { MultiSelection } from '/client/lib/multiSelection';
import { Utils } from '/client/lib/utils';
import autosize from 'autosize';
import { cardMenuSource, setCardMenuSource } from '/client/lib/cardMenuSource';
import { caretClassFor } from '/client/lib/sectionCaret';
const { openCardIsUnavailable } = require('/models/lib/openCardPresence');
// Id of the location currently being edited in the cardLocationsPopup; null
// when adding a new location.
const editingLocationId = new ReactiveVar(null);
// Chinese datum conversions ("eviltransform" algorithm). Baidu Maps uses BD-09
// and Amap (Gaode) uses GCJ-02; both are offset from the WGS-84 coordinates
// Wekan stores, so a raw lat/lon would land a few hundred metres off. Convert on
// the way out (mapLinkFor) and back (parseMapLink). Coordinates outside mainland
// China use no offset, so every non-Chinese provider is unaffected.
const GCJ = (() => {
const PI = Math.PI;
const A = 6378245.0; // Krasovsky 1940 semi-major axis
const EE = 0.00669342162296594323; // eccentricity squared
const XPI = (PI * 3000.0) / 180.0;
const outOfChina = (lat, lon) =>
lon < 72.004 || lon > 137.8347 || lat < 0.8293 || lat > 55.8271;
const tLat = (x, y) => {
let r = -100 + 2 * x + 3 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
r += ((20 * Math.sin(6 * x * PI) + 20 * Math.sin(2 * x * PI)) * 2) / 3;
r += ((20 * Math.sin(y * PI) + 40 * Math.sin((y / 3) * PI)) * 2) / 3;
r += ((160 * Math.sin((y / 12) * PI) + 320 * Math.sin((y * PI) / 30)) * 2) / 3;
return r;
};
const tLon = (x, y) => {
let r = 300 + x + 2 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
r += ((20 * Math.sin(6 * x * PI) + 20 * Math.sin(2 * x * PI)) * 2) / 3;
r += ((20 * Math.sin(x * PI) + 40 * Math.sin((x / 3) * PI)) * 2) / 3;
r += ((150 * Math.sin((x / 12) * PI) + 300 * Math.sin((x / 30) * PI)) * 2) / 3;
return r;
};
const wgs2gcj = (lat, lon) => {
if (outOfChina(lat, lon)) return [lat, lon];
let dLat = tLat(lon - 105, lat - 35);
let dLon = tLon(lon - 105, lat - 35);
const rad = (lat / 180) * PI;
let magic = Math.sin(rad);
magic = 1 - EE * magic * magic;
const sq = Math.sqrt(magic);
dLat = (dLat * 180) / (((A * (1 - EE)) / (magic * sq)) * PI);
dLon = (dLon * 180) / ((A / sq) * Math.cos(rad) * PI);
return [lat + dLat, lon + dLon];
};
// Approximate inverse by fixed-point iteration (converges to ~1e-9 deg).
const gcj2wgs = (lat, lon) => {
if (outOfChina(lat, lon)) return [lat, lon];
let wLat = lat;
let wLon = lon;
for (let i = 0; i < 10; i++) {
const [gLat, gLon] = wgs2gcj(wLat, wLon);
const dLat = gLat - lat;
const dLon = gLon - lon;
if (Math.abs(dLat) < 1e-9 && Math.abs(dLon) < 1e-9) break;
wLat -= dLat;
wLon -= dLon;
}
return [wLat, wLon];
};
const gcj2bd = (lat, lon) => {
const z = Math.sqrt(lon * lon + lat * lat) + 0.00002 * Math.sin(lat * XPI);
const theta = Math.atan2(lat, lon) + 0.000003 * Math.cos(lon * XPI);
return [z * Math.sin(theta) + 0.006, z * Math.cos(theta) + 0.0065];
};
const bd2gcj = (lat, lon) => {
const x = lon - 0.0065;
const y = lat - 0.006;
const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * XPI);
const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * XPI);
return [z * Math.sin(theta), z * Math.cos(theta)];
};
return {
wgs2gcj,
gcj2wgs,
wgs2bd: (lat, lon) => {
if (outOfChina(lat, lon)) return [lat, lon];
const [gLat, gLon] = wgs2gcj(lat, lon);
return gcj2bd(gLat, gLon);
},
bd2wgs: (lat, lon) => {
if (outOfChina(lat, lon)) return [lat, lon];
const [gLat, gLon] = bd2gcj(lat, lon);
return gcj2wgs(gLat, gLon);
},
};
})();
// The "open in map" link builder and the Chinese datum conversions it needs.
// Shared with Admin Panel, which links the places accounts log in from the same
// way - models/lib/mapLink.js.
const { mapLinkFor, GCJ } = require('/models/lib/mapLink');
// Parse coordinates (and, when present, a place name / address) out of a map
// link from common providers, grouped by region:
@@ -230,6 +159,12 @@ function parseMapLink(url) {
if ((m = sd.match(new RegExp(`[?&]q=${N},${N}`)))) setCoords(m[1], m[2]);
// Generic center=lat,lon
if ((m = sd.match(new RegExp(`[?&]center=${N},${N}`)))) setCoords(m[1], m[2]);
// A coordinate pair copied directly from a map or GPS application. Anchor
// the complete input so unrelated prose containing two numbers is not
// silently treated as a location.
if ((m = sd.match(new RegExp(`^\\s*${N}\\s*,\\s*${N}\\s*$`)))) {
setCoords(m[1], m[2]);
}
if (lat !== undefined) {
result.latitude = lat;
@@ -259,44 +194,6 @@ function parseMapLink(url) {
return result;
}
// Build an "open in map" link for the given provider and coordinates. Mirrors
// the providers offered in the location popup's "Open map links at" selector,
// grouped by region (USA, Europe, Asia). Note that several non-US providers
// expect the coordinates in lon,lat order rather than lat,lon.
function mapLinkFor(provider, lat, lon) {
switch (provider) {
// --- USA ---
case 'google':
return `https://www.google.com/maps/search/?api=1&query=${lat},${lon}`;
case 'bing':
return `https://www.bing.com/maps?cp=${lat}~${lon}&lvl=16`;
case 'apple':
return `https://maps.apple.com/?ll=${lat},${lon}`;
case 'waze':
return `https://waze.com/ul?ll=${lat},${lon}`;
// --- Europe ---
case 'here':
return `https://wego.here.com/?map=${lat},${lon},16`;
case 'yandex':
return `https://yandex.com/maps/?ll=${lon},${lat}&z=16`;
case 'mapy':
return `https://mapy.cz/zakladni?x=${lon}&y=${lat}&z=16`;
case '2gis':
return `https://2gis.ru/geo/${lon},${lat}`;
// --- Asia (coordinates converted from WGS-84 into the local datum) ---
case 'baidu': {
const [bLat, bLon] = GCJ.wgs2bd(parseFloat(lat), parseFloat(lon));
return `https://api.map.baidu.com/marker?location=${bLat},${bLon}&output=html`;
}
case 'amap': {
const [gLat, gLon] = GCJ.wgs2gcj(parseFloat(lat), parseFloat(lon));
return `https://uri.amap.com/marker?position=${gLon},${gLat}`;
}
case 'openstreetmap':
default:
return `https://www.openstreetmap.org/?mlat=${lat}&mlon=${lon}#map=16/${lat}/${lon}`;
}
}
// SubsManager removed for Meteor 3 migration
const { calculateIndexData } = Utils;
@@ -448,6 +345,36 @@ Template.cardDetails.onCreated(function () {
this.currentBoard = Utils.getCurrentBoard();
this.isLoaded = new ReactiveVar(false);
this.infiniteScrolling = new InfiniteScrolling();
const openedCardId = this.data?._id;
const openedBoardId = this.data?.boardId;
// #3114: another client can delete/archive this card or move it to another
// board. Minimongo then removed the data while the mobile details view kept
// rendering an empty shell. Observe the card that created this instance and
// close every way it can be open as soon as it no longer belongs here.
this.autorun(() => {
if (!openedCardId || !openedBoardId) return;
const card = ReactiveCache.getCard(openedCardId);
if (!openCardIsUnavailable(card, openedBoardId)) return;
Session.set(
'openCards',
(Session.get('openCards') || []).filter(id => id !== openedCardId),
);
if (Session.get('currentCard') === openedCardId) {
Session.set('currentCard', null);
}
if (Session.get('popupCardId') === openedCardId) {
Session.delete('popupCardId');
Session.delete('popupCardBoardId');
Popup.close();
}
const routeCardId = FlowRouter.current()?.params?.cardId;
if (routeCardId === openedCardId) {
Utils.goBoardId(openedBoardId);
}
});
const boardBody = getBoardBodyInstance();
if (boardBody !== null) {
@@ -654,10 +581,19 @@ Template.cardDetails.onDestroyed(function () {
});
Template.cardDetails.helpers({
canShowCustomFieldsOnCard() {
const board = this?.board?.();
return Utils.canModifyCard(this) && board?.allowsCustomFields !== false;
},
stickers() {
const card = Template.currentData();
return card && typeof card.getStickers === 'function' ? card.getStickers() : [];
},
isWatching() {
const card = Template.currentData();
if (!card || typeof card.findWatcher !== 'function') return false;
return card.findWatcher(Meteor.userId());
const realCard = typeof card.getRealCard === 'function' ? card.getRealCard() : card;
return realCard.findWatcher(Meteor.userId());
},
// #6081: number of times this card's due date has been changed, for
@@ -727,7 +663,10 @@ Template.cardDetails.helpers({
showActivities() {
const card = Template.currentData();
return card && card.showActivities;
const realCard = card && typeof card.getRealCard === 'function'
? card.getRealCard()
: card;
return realCard && realCard.showActivities;
},
cardCollapsed() {
@@ -804,12 +743,11 @@ Template.cardDetails.helpers({
const swimlaneId = card.swimlaneId;
const selector = { boardId: card.boardId, archived: false };
if (swimlaneId) {
const defaultSwimlane = board.getDefaultSwimline && board.getDefaultSwimline();
if (defaultSwimlane && defaultSwimlane._id === swimlaneId) {
selector.swimlaneId = { $in: [swimlaneId, null, ''] };
} else {
selector.swimlaneId = swimlaneId;
}
// Board-wide lists have no swimlaneId. They are shared by EVERY
// swimlane, not only by the first/default one. Restricting this fallback
// to the default swimlane made the List chooser (and move/copy chooser)
// empty for valid cards in every later swimlane (#6614/#6618).
selector.swimlaneId = { $in: [swimlaneId, null, ''] };
}
return ReactiveCache.getLists(selector, { sort: { sort: 1 } });
},
@@ -828,7 +766,109 @@ Template.cardDetails.helpers({
},
});
// WHICH SECTIONS OF A CARD ARE OPEN.
//
// Every section of an opened card - Labels, Date Format, Members, Dependencies,
// Sort, Custom Fields, Description, Checklists, Subtasks, Attachments, Comments
// and Activities - carries a caret that collapses it, and one rule above it.
// Before this there was one caret for the whole card and an EYE on Activities
// that showed and hid exactly what a caret would: two controls for one idea,
// able to disagree with each other.
//
// The state is module-level, so it survives closing and reopening a card in the
// same session: somebody who works with the description collapsed wants it
// collapsed on the next card too, not just on this one.
//
// ACTIVITIES starts CLOSED and everything else open. A card is opened to read
// the card; its history is the thing you go looking for.
const CARD_SECTIONS = [
'labels', 'date-format', 'members', 'dependencies', 'sort', 'custom-fields',
'description', 'checklists', 'subtasks', 'attachments', 'comments', 'activities',
];
const COLLAPSED_BY_DEFAULT = ['activities'];
const cardSectionState = new ReactiveDict();
CARD_SECTIONS.forEach(section => {
cardSectionState.set(section, !COLLAPSED_BY_DEFAULT.includes(section));
});
function isCardSectionOpen(section) {
const state = cardSectionState.get(section);
// An unknown section is open: a new one must show up, not hide silently.
return state === undefined ? true : state;
}
Template.registerHelper('isSectionOpen', isCardSectionOpen);
// "Attachments (3)" - the count the heading carried before it became a section,
// and only when the board asks for it.
Template.registerHelper('attachmentCount', function attachmentCount() {
const card = Utils.getCurrentCard();
const board = card && card.board && card.board();
if (!board || !board.allowsAttachmentCountOnCard) return '';
const attachments = card.attachments && card.attachments();
return attachments && attachments.length ? attachments.length : '';
});
// Which way the caret points. The rule - and the reason - is in
// client/lib/sectionCaret.js, shared with the board sidebar's Activities, which
// is the same control and must not point the other way in the same language.
Template.registerHelper('sectionCaret', section =>
caretClassFor(isCardSectionOpen(section)));
Template.cardSectionHeader.events({
// A hamburger at the end of a heading opens that section's settings. Neither
// may reach the heading itself: a click there folds the section, so opening
// the menu would close what you opened it from.
'click .js-open-subtasks-settings'(event) {
event.preventDefault();
event.stopPropagation();
Popup.open('boardSubtaskSettings')(event);
},
// Every custom field the board has, ticked when it is on this card, with a
// pencil each and "Add custom field" under a rule. The card is its data
// context, which is what the ticks and the toggle read.
// client/components/cards/cardCustomFields.jade
'click .js-open-custom-fields-settings'(event) {
event.preventDefault();
event.stopPropagation();
Popup.open('cardCustomFields').call(Utils.getCurrentCard(), event);
},
'click #toggleCustomFieldsGridButton'(event) {
event.preventDefault();
event.stopPropagation();
Meteor.call('toggleCustomFieldsGrid');
},
'click .js-toggle-card-section'(event) {
if (event.target.closest('#toggleCustomFieldsGridButton')) return;
event.preventDefault();
const section = event.currentTarget.dataset.section;
if (!section) return;
cardSectionState.set(section, !isCardSectionOpen(section));
},
// A heading that can be operated with the mouse can be operated with the
// keyboard: it carries role="button" and tabindex, so Enter and Space are
// what a screen reader user presses.
'keydown .js-toggle-card-section'(event) {
if (event.target !== event.currentTarget) return;
if (event.key !== 'Enter' && event.key !== ' ') return;
event.preventDefault();
const section = event.currentTarget.dataset.section;
if (section) cardSectionState.set(section, !isCardSectionOpen(section));
},
});
Template.cardDetails.events({
// #1591 folded each FIELD by its own title, storing a class per card in the
// profile. The section carets replaced it: one handle per section, on the
// heading, and every field lives in a section now. Both were still running,
// and they fought - clicking a section's caret also ran the old handler, so
// the field it was drawn on was left `is-collapsed` with nothing to open it
// again, and the Date Format select vanished for good. The old half is gone;
// the same store is still the CHECKLISTS' (client/components/cards/checklists.js),
// which is why it is not.
[`${CSSEvents.transitionend} .js-card-details`](event, tpl) {
tpl.isLoaded.set(true);
},
@@ -878,10 +918,29 @@ Template.cardDetails.events({
$(document).on('mouseup', onMouseUp);
},
'mousedown .js-card-title-drag-handle'(event) {
// Allow dragging from title for ReadOnly users
// Don't interfere with text selection
if (event.target.tagName === 'A' || $(event.target).closest('a').length > 0) {
return; // Don't drag if clicking on links
// The title bar drags the window. Don't interfere with text selection.
if (
$(event.target).closest(
'a, input, textarea, button, select, option, .js-card-details-title',
).length > 0
) {
// #6641: while the editor replaces the header, a textarea press still
// bubbles to this drag bar. Leave it to the browser so mouse dragging
// selects text instead of moving the card window.
return;
}
// ...and don't drag from the half that EDITS. The title is split like a
// minicard's: the leading half opens the editor, the trailing half is what
// you take hold of. With drag handles ON the zone covers the whole title
// (cardDetails.css), so the window is dragged by its handle alone - which is
// what turning handles on means.
if ($(event.target).closest('.card-details-title-edit-zone').length > 0) {
return;
}
// The drag handle has its own handler; without this both would run and the
// window would be moved twice by one press.
if ($(event.target).closest('.js-card-drag-handle').length > 0) {
return;
}
event.preventDefault();
@@ -969,7 +1028,12 @@ Template.cardDetails.events({
window.localStorage.setItem('dateFormat', dateFormat);
}
},
'click .js-open-card-details-menu': Popup.open('cardDetailsActions'),
// The opened card's own hamburger. It says so, so the menu's first entry is
// "Show on Card" rather than the minicard's "Show on Minicard".
'click .js-open-card-details-menu'(event) {
setCardMenuSource('card');
Popup.open('cardDetailsActions').call(this, event);
},
// Mobile: switch to desktop popup view (maximize)
'click .js-mobile-switch-to-desktop'(event) {
event.preventDefault();
@@ -1108,6 +1172,8 @@ Template.cardDetails.events({
},
'click .js-member': Popup.open('cardMember'),
'click .js-add-members': Popup.open('cardMembers'),
'click .js-select-requester': Popup.open('cardRequestedBy', { titleKey: 'requested-by' }),
'click .js-select-assigner': Popup.open('cardAssignedBy', { titleKey: 'assigned-by' }),
'click .js-assignee': Popup.open('cardAssignee'),
'click .js-add-assignees': Popup.open('cardAssignees'),
'click .js-add-labels': Popup.open('cardLabels'),
@@ -1142,14 +1208,16 @@ Template.cardDetails.events({
dataContextIfCurrentDataIsUndefined: card,
});
},
'click .js-remove-location'(event) {
async 'click .js-remove-location'(event) {
event.preventDefault();
event.stopPropagation();
if (!Utils.canModifyCard()) return;
const locationId = event.currentTarget.dataset.locationId;
const card = Template.currentData();
// The X is rendered inside `each getLocations`, so Template.currentData()
// is the location row, not the surrounding card (#6644).
const card = getCurrentCardFromContext();
if (card && locationId) {
card.removeLocation(locationId);
await card.removeLocation(locationId);
}
},
'click .js-received-date': Popup.open('editCardReceivedDate'),
@@ -1165,7 +1233,6 @@ Template.cardDetails.events({
'click .js-end-date': Popup.open('editCardEndDate'),
'click .js-show-positive-votes': Popup.open('positiveVoteMembers'),
'click .js-show-negative-votes': Popup.open('negativeVoteMembers'),
'click .js-custom-fields': Popup.open('cardCustomFields'),
'mouseenter .js-card-details'(event, tpl) {
const boardBody = getBoardBodyInstance(tpl);
if (boardBody === null) return;
@@ -1189,13 +1256,6 @@ Template.cardDetails.events({
const card = Template.currentData();
await card.toggleHideCheckedChecklistItems();
},
async 'click .js-toggle-show-activities-card'() {
const card = Template.currentData();
await card.toggleShowActivities();
},
'click #toggleCustomFieldsGridButton'() {
Meteor.call('toggleCustomFieldsGrid');
},
'click .js-maximize-card-details'() {
if (Meteor.userId()) {
Meteor.call('toggleCardMaximized');
@@ -1224,50 +1284,50 @@ Template.cardDetails.events({
newState = forIt;
}
// Use secure server method; direct client updates to vote are blocked
Meteor.call('cards.vote', card._id, newState);
Meteor.call('cards.vote', card.getRealId(), newState);
},
'click .js-poker'(e) {
const card = Template.currentData();
let newState = null;
if ($(e.target).hasClass('js-poker-vote-one')) {
newState = 'one';
Meteor.call('cards.pokerVote', card._id, newState);
Meteor.call('cards.pokerVote', card.getRealId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-two')) {
newState = 'two';
Meteor.call('cards.pokerVote', card._id, newState);
Meteor.call('cards.pokerVote', card.getRealId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-three')) {
newState = 'three';
Meteor.call('cards.pokerVote', card._id, newState);
Meteor.call('cards.pokerVote', card.getRealId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-five')) {
newState = 'five';
Meteor.call('cards.pokerVote', card._id, newState);
Meteor.call('cards.pokerVote', card.getRealId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-eight')) {
newState = 'eight';
Meteor.call('cards.pokerVote', card._id, newState);
Meteor.call('cards.pokerVote', card.getRealId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-thirteen')) {
newState = 'thirteen';
Meteor.call('cards.pokerVote', card._id, newState);
Meteor.call('cards.pokerVote', card.getRealId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-twenty')) {
newState = 'twenty';
Meteor.call('cards.pokerVote', card._id, newState);
Meteor.call('cards.pokerVote', card.getRealId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-forty')) {
newState = 'forty';
Meteor.call('cards.pokerVote', card._id, newState);
Meteor.call('cards.pokerVote', card.getRealId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-one-hundred')) {
newState = 'oneHundred';
Meteor.call('cards.pokerVote', card._id, newState);
Meteor.call('cards.pokerVote', card.getRealId(), newState);
}
if ($(e.target).hasClass('js-poker-vote-unsure')) {
newState = 'unsure';
Meteor.call('cards.pokerVote', card._id, newState);
Meteor.call('cards.pokerVote', card.getRealId(), newState);
}
},
'click .js-poker-finish'(e) {
@@ -1275,16 +1335,16 @@ Template.cardDetails.events({
e.preventDefault();
const card = Template.currentData();
const now = new Date();
Meteor.call('cards.setPokerEnd', card._id, now);
Meteor.call('cards.setPokerEnd', card.getRealId(), now);
}
},
'click .js-poker-replay'(e) {
if ($(e.target).hasClass('js-poker-replay')) {
e.preventDefault();
const currentCard = Template.currentData();
Meteor.call('cards.replayPoker', currentCard._id);
Meteor.call('cards.unsetPokerEnd', currentCard._id);
Meteor.call('cards.unsetPokerEstimation', currentCard._id);
Meteor.call('cards.replayPoker', currentCard.getRealId());
Meteor.call('cards.unsetPokerEnd', currentCard.getRealId());
Meteor.call('cards.unsetPokerEstimation', currentCard.getRealId());
}
},
'click .js-poker-estimation'(event, tpl) {
@@ -1295,9 +1355,9 @@ Template.cardDetails.events({
tpl.find('#pokerEstimation').value = '';
if (ruleTitle) {
Meteor.call('cards.setPokerEstimation', card._id, parseInt(ruleTitle, 10));
Meteor.call('cards.setPokerEstimation', card.getRealId(), parseInt(ruleTitle, 10));
} else {
Meteor.call('cards.unsetPokerEstimation', card._id);
Meteor.call('cards.unsetPokerEstimation', card.getRealId());
}
}
},
@@ -1394,90 +1454,9 @@ Template.cardDetailsPopup.helpers({
},
});
// Ordered list of Excel export field keys and their i18n label keys.
// Must match ALL_FIELDS in models/server/ExporterExcelCard.js.
const EXCEL_EXPORT_FIELDS = [
{ field: 'labels', label: 'labels' },
{ field: 'people', label: 'export-card-field-people' },
{ field: 'board-info', label: 'export-card-field-board-info' },
{ field: 'dates', label: 'export-card-field-dates' },
{ field: 'description', label: 'description' },
{ field: 'checklists', label: 'checklists' },
{ field: 'subtasks', label: 'export-card-subtasks' },
{ field: 'comments', label: 'comments' },
{ field: 'attachments', label: 'attachments' },
];
Template.exportCardPopup.onCreated(function () {
// Track which Excel sections the user wants to include (all on by default)
const initial = {};
EXCEL_EXPORT_FIELDS.forEach(({ field }) => { initial[field] = true; });
this.excelFields = new ReactiveDict(initial);
});
Template.exportCardPopup.helpers({
exportUrlCardPDF() {
const card = getCurrentCardFromContext({ ignorePopupCard: true }) || this;
const params = {
boardId: card.boardId || Session.get('currentBoard'),
listId: card.listId,
cardId: card._id || card.cardId,
};
return FlowRouter.path(
'/api/boards/:boardId/lists/:listId/cards/:cardId/exportPDF',
params,
{ authToken: Accounts._storedLoginToken() },
);
},
exportFilenameCardPDF() {
const card = getCurrentCardFromContext({ ignorePopupCard: true }) || this;
return `${String(card.title || 'export-card')
.replace(/[^a-z0-9._-]+/gi, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '') || 'export-card'}.pdf`;
},
// Returns the field list with current checked state — reactive
excelExportFields() {
const instance = Template.instance();
return EXCEL_EXPORT_FIELDS.map(f => ({
field: f.field,
label: f.label,
checked: instance.excelFields.get(f.field),
}));
},
exportUrlCardExcel() {
const instance = Template.instance();
const card = getCurrentCardFromContext({ ignorePopupCard: true }) || this;
const params = {
boardId: card.boardId || Session.get('currentBoard'),
listId: card.listId,
cardId: card._id || card.cardId,
};
const selectedFields = EXCEL_EXPORT_FIELDS
.map(f => f.field)
.filter(f => instance.excelFields.get(f));
return FlowRouter.path(
'/api/boards/:boardId/lists/:listId/cards/:cardId/exportExcel',
params,
{ authToken: Accounts._storedLoginToken(), fields: selectedFields.join(','), lang: TAPi18n.getLanguage() },
);
},
exportFilenameCardExcel() {
const card = getCurrentCardFromContext({ ignorePopupCard: true }) || this;
return `${String(card.title || 'export-card')
.replace(/[^a-z0-9._-]+/gi, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '') || 'export-card'}.xlsx`;
},
});
Template.exportCardPopup.events({
'click .js-excel-field-toggle'(event, instance) {
event.preventDefault();
const field = event.currentTarget.dataset.field;
instance.excelFields.set(field, !instance.excelFields.get(field));
},
});
// #1173: the card export popup is client/components/boards/exportScope.js now -
// one body, one field list and one url builder for the card, the list, the
// swimlane and the board. What was here was a second copy of all three.
// only allow number input
Template.editCardSortOrderForm.onRendered(function () {
@@ -1580,6 +1559,12 @@ Template.cardDetailsActionsPopup.helpers({
return ReactiveCache.getCurrentUser()?.isBoardAdmin();
},
// Which of the two hamburgers opened this menu, so its first entry can be
// about the thing the user is looking at. client/lib/cardMenuSource.js
isMinicardMenu() {
return cardMenuSource() === 'minicard';
},
showListOnMinicard() {
return this.showListOnMinicard;
},
@@ -1590,7 +1575,7 @@ Template.cardDetailsActionsPopup.events({
// tooltip to name it - the one place a name cannot be read without hovering -
// and it is here now beside the swimlane's and the list's, so all three are
// copied the same way from the same kind of menu.
// docs/Design/Page/Board-Item-Links.md
// docs/Features/Page/Board-Item-Links.md
'click .js-copy-card-link'(event, tpl) {
event.preventDefault();
const card = Template.currentData();
@@ -1599,13 +1584,24 @@ Template.cardDetailsActionsPopup.events({
if (!url) return;
Utils.showCopied(Utils.copyTextToClipboard(url), tpl.$('.copied-tooltip'));
},
// The board's Card Settings, one column of them, titled by the key the app
// already has for that column - so no `showOnCardPopup-title` has to be added
// to 147 language files to say a phrase they have already translated.
'click .js-show-on-card': Popup.open('showOnCard', { titleKey: 'show-on-card' }),
// "Custom Fields" is ONE entry: it opens the picker for which of the board's
// fields are on THIS card, and that popup's own Settings cog opens the
// board's list of fields, where one is created, renamed or deleted. Two
// entries for the two halves put the general one above the particular one and
// made the menu ask which you wanted before you had seen either.
// client/components/cards/cardCustomFields.jade
'click .js-show-on-minicard': Popup.open('showOnMinicard', { titleKey: 'show-on-minicard' }),
'click .js-export-card': Popup.open('exportCard'),
'click .js-import-card': Popup.open('importCard'),
'click .js-members': Popup.open('cardMembers'),
'click .js-assignees': Popup.open('cardAssignees'),
'click .js-attachments': Popup.open('cardAttachments'),
'click .js-start-voting': Popup.open('cardStartVoting'),
'click .js-start-planning-poker': Popup.open('cardStartPlanningPoker'),
'click .js-custom-fields': Popup.open('cardCustomFields'),
'click .js-received-date': Popup.open('editCardReceivedDate'),
'click .js-start-date': Popup.open('editCardStartDate'),
'click .js-due-date': Popup.open('editCardDueDate'),
@@ -1653,16 +1649,17 @@ Template.cardDetailsActionsPopup.events({
'click .js-toggle-watch-card'() {
const currentCard = Cards.findOne(getCardId());
if (!currentCard) return;
const level = currentCard.findWatcher(Meteor.userId()) ? null : 'watching';
Meteor.call('watch', 'card', currentCard._id, level, (err, ret) => {
const sourceCard = currentCard.getRealCard();
const level = sourceCard.findWatcher(Meteor.userId()) ? null : 'watching';
Meteor.call('watch', 'card', currentCard.getRealId(), level, (err, ret) => {
if (!err && ret) Popup.close();
});
},
'click .js-toggle-show-list-on-minicard'() {
const currentCard = Cards.findOne(getCardId());
if (!currentCard) return;
const newValue = !currentCard.showListOnMinicard;
Cards.update(currentCard._id, { $set: { showListOnMinicard: newValue } });
const newValue = !currentCard.getRealCard().showListOnMinicard;
Cards.update(currentCard.getRealId(), { $set: { showListOnMinicard: newValue } });
Popup.close();
},
});
@@ -1739,6 +1736,48 @@ Template.cardMembersPopup.helpers({
},
});
Template.cardIdentityPicker.onCreated(function () {
this.filterTerm = new ReactiveVar('');
});
Template.cardIdentityPicker.events({
'click .js-select-card-identity'(event, tpl) {
event.preventDefault();
const card = getCurrentCardFromContext();
const user = ReactiveCache.getUser(this.userId);
if (!card || !user) return;
if (tpl.data.field === 'requesters') card.toggleRequester(user._id);
if (tpl.data.field === 'assigners') card.toggleAssigner(user._id);
},
'keyup .card-identity-filter'(event) {
Template.instance().filterTerm.set(event.target.value);
},
});
Template.cardIdentityPicker.helpers({
members() {
return [...uniqBy(filterMembers(Template.instance().filterTerm.get()), 'userId')]
.sort((a, b) => {
const userA = ReactiveCache.getUser(a.userId);
const userB = ReactiveCache.getUser(b.userId);
const nameA = (userA && userA.profile && userA.profile.fullname) || '';
const nameB = (userB && userB.profile && userB.profile.fullname) || '';
return nameA.localeCompare(nameB);
});
},
userData() {
return ReactiveCache.getUser(this.userId);
},
isSelected() {
const card = getCurrentCardFromContext();
const user = ReactiveCache.getUser(this.userId);
if (!card || !user) return false;
const selected = Template.instance().data.field === 'requesters'
? card.getRequesters() : card.getAssigners();
return (selected || []).includes(user._id);
},
});
// Popup that adds or edits a single card location (name, address, latitude,
// longitude). Cards can hold multiple locations, like members.
Template.cardLocationsPopup.onCreated(function () {
@@ -1762,11 +1801,6 @@ Template.cardLocationsPopup.helpers({
detectMessage() {
return Template.instance().detectMsg.get();
},
isMapProvider(provider) {
const user = ReactiveCache.getCurrentUser();
const current = user ? user.getMapProvider() : 'openstreetmap';
return current === provider;
},
mapSavedMessage() {
return Template.instance().mapSavedMsg.get();
},
@@ -1911,47 +1945,75 @@ Template.editCardAssignerForm.events({
},
});
// The four selects of the move/copy dialogs, in one template. The dialog comes
// from the popup that includes it and is kept on THIS instance: inside
// `each boards` the data context is a board, so a helper reaching into the
// context for it would find nothing there.
Template.cardDestinationPicker.onCreated(function () {
this.autorun(() => {
const data = Template.currentData();
this.dialog = data && data.dialog;
});
});
Template.cardDestinationPicker.helpers({
boards() {
return Template.instance().dialog.boards();
},
swimlanes() {
return Template.instance().dialog.swimlanes();
},
lists() {
return Template.instance().dialog.lists();
},
cards() {
return Template.instance().dialog.cards();
},
isDialogOptionBoardId(boardId) {
return Template.instance().dialog.isDialogOptionBoardId(boardId);
},
isDialogOptionSwimlaneId(swimlaneId) {
return Template.instance().dialog.isDialogOptionSwimlaneId(swimlaneId);
},
isDialogOptionListId(listId) {
return Template.instance().dialog.isDialogOptionListId(listId);
},
isSelectedBoardId(boardId) {
return Template.instance().dialog.isSelectedBoardId(boardId);
},
isSelectedSwimlaneId(swimlaneId) {
return Template.instance().dialog.isSelectedSwimlaneId(swimlaneId);
},
isSelectedListId(listId) {
return Template.instance().dialog.isSelectedListId(listId);
},
isDialogOptionCardId(cardId) {
return Template.instance().dialog.isDialogOptionCardId(cardId);
},
isTitleDefault(title) {
return Template.instance().dialog.isTitleDefault(title);
},
});
/**
* Helper: register standard board/swimlane/list/card dialog helpers and events
* for a template that uses BoardSwimlaneListCardDialog.
*/
/**
* Helper: register standard board/swimlane/list/card dialog helpers and events
* for a template that uses BoardSwimlaneListCardDialog.
*
* The MARKUP those helpers feed is one template - `cardDestinationPicker` in
* cardDetails.jade, included by all four popups - so the helpers are registered
* on it, once, below. What each popup needs of its own is the `dialog` to hand
* that template, and the events: a change or a click inside the picker bubbles
* up to the popup that includes it, which is the one holding the dialog.
*/
function registerCardDialogTemplate(templateName) {
Template[templateName].helpers({
boards() {
return Template.instance().dialog.boards();
},
swimlanes() {
return Template.instance().dialog.swimlanes();
},
lists() {
return Template.instance().dialog.lists();
},
cards() {
return Template.instance().dialog.cards();
},
isDialogOptionBoardId(boardId) {
return Template.instance().dialog.isDialogOptionBoardId(boardId);
},
isDialogOptionSwimlaneId(swimlaneId) {
return Template.instance().dialog.isDialogOptionSwimlaneId(swimlaneId);
},
isDialogOptionListId(listId) {
return Template.instance().dialog.isDialogOptionListId(listId);
},
isSelectedBoardId(boardId) {
return Template.instance().dialog.isSelectedBoardId(boardId);
},
isSelectedSwimlaneId(swimlaneId) {
return Template.instance().dialog.isSelectedSwimlaneId(swimlaneId);
},
isSelectedListId(listId) {
return Template.instance().dialog.isSelectedListId(listId);
},
isDialogOptionCardId(cardId) {
return Template.instance().dialog.isDialogOptionCardId(cardId);
},
isTitleDefault(title) {
return Template.instance().dialog.isTitleDefault(title);
dialog() {
return Template.instance().dialog;
},
});
@@ -2481,16 +2543,16 @@ Template.cardStartVotingPopup.events({
'is-checked',
);
const endString = card.getVoteEnd();
Meteor.call('cards.setVoteQuestion', card._id, voteQuestion, publicVote, allowNonBoardMembers);
Meteor.call('cards.setVoteQuestion', card.getRealId(), voteQuestion, publicVote, allowNonBoardMembers);
if (endString) {
Meteor.call('cards.setVoteEnd', card._id, new Date(endString));
Meteor.call('cards.setVoteEnd', card.getRealId(), new Date(endString));
}
Popup.back();
},
'click .js-remove-vote': Popup.afterConfirm('deleteVote', function () {
const card = Cards.findOne(getCardId());
if (!card) return;
Meteor.call('cards.unsetVote', card._id);
Meteor.call('cards.unsetVote', card.getRealId());
Popup.back();
}),
'click a.js-toggle-vote-public'(event) {
@@ -2537,6 +2599,12 @@ Template.editVoteEndDatePopup.onCreated(function () {
setupDatePicker(this, {
defaultTime: formatDateTime(now()),
initialDate: card?.getVoteEnd ? (card.getVoteEnd() || undefined) : undefined,
async storeDate(date, currentCard) {
await Meteor.callAsync('cards.setVoteEnd', currentCard.getRealId(), date);
},
async deleteDate(currentCard) {
await Meteor.callAsync('cards.unsetVoteEnd', currentCard.getRealId());
},
});
});
@@ -2546,15 +2614,6 @@ Template.editVoteEndDatePopup.onRendered(function () {
Template.editVoteEndDatePopup.helpers(datePickerHelpers());
Template.editVoteEndDatePopup.events(datePickerEvents({
storeDate(date) {
Meteor.call('cards.setVoteEnd', this.datePicker.card._id, date);
},
deleteDate() {
Meteor.call('cards.unsetVoteEnd', this.datePicker.card._id);
},
}));
Template.cardStartPlanningPokerPopup.onCreated(function () {
const cardId = getCardId();
this.currentCard = Cards.findOne(cardId);
@@ -2588,16 +2647,16 @@ Template.cardStartPlanningPokerPopup.events({
);
const endString = card.getPokerEnd();
Meteor.call('cards.setPokerQuestion', card._id, pokerQuestion, allowNonBoardMembers);
Meteor.call('cards.setPokerQuestion', card.getRealId(), pokerQuestion, allowNonBoardMembers);
if (endString) {
Meteor.call('cards.setPokerEnd', card._id, new Date(endString));
Meteor.call('cards.setPokerEnd', card.getRealId(), new Date(endString));
}
Popup.back();
},
'click .js-remove-poker': Popup.afterConfirm('deletePoker', function () {
const card = Cards.findOne(getCardId());
if (!card) return;
Meteor.call('cards.unsetPoker', card._id);
Meteor.call('cards.unsetPoker', card.getRealId());
Popup.back();
}),
'click a.js-toggle-poker-allow-non-members'(event) {
@@ -2612,6 +2671,12 @@ Template.editPokerEndDatePopup.onCreated(function () {
setupDatePicker(this, {
defaultTime: formatDateTime(now()),
initialDate: card?.getPokerEnd ? (card.getPokerEnd() || undefined) : undefined,
async storeDate(date, currentCard) {
await Meteor.callAsync('cards.setPokerEnd', currentCard.getRealId(), date);
},
async deleteDate(currentCard) {
await Meteor.callAsync('cards.unsetPokerEnd', currentCard.getRealId());
},
});
});
@@ -2621,15 +2686,6 @@ Template.editPokerEndDatePopup.onRendered(function () {
Template.editPokerEndDatePopup.helpers(datePickerHelpers());
Template.editPokerEndDatePopup.events(datePickerEvents({
storeDate(date) {
Meteor.call('cards.setPokerEnd', this.datePicker.card._id, date);
},
deleteDate() {
Meteor.call('cards.unsetPokerEnd', this.datePicker.card._id);
},
}));
// Close the card details pane by pressing escape
EscapeActions.register(
'detailsPane',
+28 -5
View File
@@ -54,12 +54,12 @@ textarea.js-edit-checklist-item {
float: inline-start;
width: 30px;
height: 30px;
font-size: 18px;
line-height: 30px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
line-height: calc(30px * var(--wekan-ui-font-scale, 1));
}
.checklist-title .title {
font-size: 18px;
line-height: 25px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
line-height: calc(25px * var(--wekan-ui-font-scale, 1));
}
.checklist-title .checklist-stat {
margin: 0 0.5em;
@@ -93,7 +93,7 @@ textarea.js-edit-checklist-item {
}
.checklist-item {
margin: 0 0 0 0.1em;
line-height: 18px;
line-height: calc(18px * var(--wekan-ui-font-scale, 1));
font-size: 1.1em;
margin-top: 3px;
display: flex;
@@ -191,3 +191,26 @@ textarea.js-edit-checklist-item {
.edit-controls label.toggle-label {
margin-inline-start: 2px;
}
/* #1591: the fold caret on a checklist title. Sized and coloured like the
actions menu beside it so the two read as one row of controls, and given a
real hit area - it is a single glyph, and a caret you have to aim at is worse
than no caret. */
.checklist-title .checklist-collapse {
/* Logical, so the caret sits on the reading-start side under dir=rtl too
(tests/rtl.test.js enforces this across component CSS). */
float: inline-start;
margin-block-start: 2px;
margin-inline-end: 4px;
padding: 2px 4px;
color: #808080;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
line-height: calc(14px * var(--wekan-ui-font-scale, 1));
cursor: pointer;
text-decoration: none;
}
.checklist-title .checklist-collapse:hover,
.checklist-title .checklist-collapse:focus-visible {
color: #2980b9;
}
+29 -4
View File
@@ -1,8 +1,9 @@
template(name="checklists")
//- The NAME of this section is drawn by +cardSectionHeader in cardDetails.jade,
with the caret that collapses it and the rule above it. What stays here is
the control that belongs to the list itself: add a checklist.
.checklists-title
h3.card-details-item-title
i.fa.fa-check
| {{_ 'checklists'}}
if canModifyCard
+inlinedForm(autoclose=false classNames="js-add-checklist" cardId = cardId
position="top")
@@ -38,6 +39,15 @@ template(name="checklistDetail")
else
.checklist-title
span
//- #1591: fold a long checklist out of the way. A caret rather than
another entry in the actions menu, because the whole point is to do it
at a glance while reading the card - and it is a view preference of
this user, so it does not touch the card.
a.fa.checklist-collapse.js-collapse-checklist(
class="{{#if checklistCollapsed}}fa-caret-right{{else}}fa-caret-down{{/if}}"
title="{{#if checklistCollapsed}}{{_ 'expand-checklist'}}{{else}}{{_ 'collapse-checklist'}}{{/if}}"
role="button" tabindex="0"
aria-expanded="{{#if checklistCollapsed}}false{{else}}true{{/if}}")
if canModifyCard
a.fa.fa-navicon.checklist-details-menu.js-open-checklist-details-menu(title="{{_ 'checklistActionsPopup-title'}}")
@@ -52,12 +62,15 @@ template(name="checklistDetail")
+viewer
= checklist.title
//- The progress bar stays visible when folded: it is the summary of what was
folded away, so hiding it too would leave a title that says nothing.
if $gt finishedPercent 0
.checklist-progress-bar-container
.checklist-progress-text {{finishedPercent}}%
.checklist-progress-bar
.checklist-progress(style="width:{{finishedPercent}}%")
+checklistItems(checklist = checklist card = card)
unless checklistCollapsed
+checklistItems(checklist = checklist card = card)
template(name="checklistDeletePopup")
p {{_ 'confirm-checklist-delete-popup'}}
@@ -155,6 +168,14 @@ template(name='checklistItemDetail')
= item.title
template(name="checklistActionsPopup")
//- #1173: a checklist exports like everything else - the same popup body, with
the checklist as its scope. Reading, so it is above the rows that change it.
ul.pop-over-list
li
a.js-export-checklist
i.fa.fa-exchange
| {{_ 'export'}} / {{_ 'import'}} ...
hr
ul.pop-over-list
li
a.js-delete-checklist.delete-checklist
@@ -188,7 +209,11 @@ template(name="checklistActionsPopup")
i.fa.fa-th-list
| {{_ "show-on-minicard"}} ...
.material-toggle-switch(title="{{_ 'show-on-minicard'}}")
if checklist.showChecklistAtMinicard
//- The switch shows what is ON THE MINICARD, not the raw field. Drawn
from the field it read OFF beside a checklist that was plainly showing,
because the board default (allowsChecklistsOnMinicard, on out of the
box) put it there - reported by email.
if shownAtMinicard
input.toggle-switch(type="checkbox" id="toggleShowChecklistAtMinicard_{{checklist._id}}" checked="checked")
else
input.toggle-switch(type="checkbox" id="toggleShowChecklistAtMinicard_{{checklist._id}}")
+67 -1
View File
@@ -8,6 +8,7 @@ import { BoardSwimlaneListCardDialog } from '/client/lib/dialogWithBoardSwimlane
import { EscapeActions } from '/client/lib/escapeActions';
import { Utils } from '/client/lib/utils';
import autosize from 'autosize';
import { isChecklistShownAtMinicard } from '/models/lib/minicardChecklistVisibility';
// SubsManager removed for Meteor 3 migration
const { calculateIndexData } = Utils;
@@ -82,6 +83,47 @@ Template.checklistDetail.helpers({
const ret = this.checklist.finishedPercent();
return ret;
},
/** #1591: is this checklist folded for THIS user?
*
* Per-user, keyed by card and checklist, exactly like collapsed lists and
* swimlanes - not the checklist's own hideAllChecklistItems, which is a field
* on the checklist and so changes what everyone on the board sees. `null` from
* the getter means "never set", which is what makes the default (expanded)
* distinguishable from somebody having deliberately expanded it. */
checklistCollapsed() {
const checklist = this.checklist;
if (!checklist || !checklist._id) return false;
const user = ReactiveCache.getCurrentUser();
if (!user) return false;
const cardId = (this.card && this.card._id) || checklist.cardId;
const stored = user.getCollapsedCardSection(
cardId, user.checklistSectionKey(checklist._id));
return stored === true;
},
});
Template.checklistDetail.events({
'click .js-collapse-checklist'(event) {
// The caret sits inside the title, which opens the inline rename form when
// clicked - so this must not reach it.
event.preventDefault();
event.stopPropagation();
const checklist = this.checklist;
const user = ReactiveCache.getCurrentUser();
if (!checklist || !checklist._id || !user) return;
const cardId = (this.card && this.card._id) || checklist.cardId;
const key = user.checklistSectionKey(checklist._id);
const collapsed = user.getCollapsedCardSection(cardId, key) === true;
user.setCollapsedCardSection(cardId, key, !collapsed);
},
'keydown .js-collapse-checklist'(event) {
// It is a link acting as a button, so it has to answer the keys a button
// answers or it is unreachable without a mouse.
if (event.key !== 'Enter' && event.key !== ' ') return;
event.preventDefault();
event.stopPropagation();
$(event.currentTarget).trigger('click');
},
});
Template.checklists.helpers({
@@ -256,7 +298,26 @@ Template.addChecklistItemForm.events({
},
});
// The board default this checklist's own setting overrides. Read here rather than
// in the model helper, because the model runs on the server too, where looking a
// board up is asynchronous - and a Blaze helper has to answer now.
function boardAllowsChecklistsOnMinicard(checklist) {
if (!checklist) return false;
const board = ReactiveCache.getBoard(checklist.boardId);
return !!(board && board.allowsChecklistsOnMinicard);
}
Template.checklistActionsPopup.helpers({
// What the "Show on minicard" switch draws: whether the checklist is on the
// minicard right now, board default included.
shownAtMinicard() {
const checklist = this.checklist;
return isChecklistShownAtMinicard(checklist, boardAllowsChecklistsOnMinicard(checklist));
},
});
Template.checklistActionsPopup.events({
'click .js-export-checklist': Popup.open('exportChecklist'),
'click .js-delete-checklist': Popup.afterConfirm('checklistDelete', function () {
Popup.back(2);
const checklist = this.checklist;
@@ -279,7 +340,12 @@ Template.checklistActionsPopup.events({
},
'click .js-show-checklist-at-minicard'(event) {
event.preventDefault();
Template.currentData().checklist.toggleShowChecklistAtMinicard();
const checklist = Template.currentData().checklist;
// The board's setting is the default this one overrides, so the toggle has to
// flip what is ON SCREEN, not the raw field. Flipping the field is what made
// the first click do nothing while the board default was on (false -> true,
// still shown) - reported by email.
checklist.toggleShowChecklistAtMinicard(boardAllowsChecklistsOnMinicard(checklist));
Popup.back();
},
});
+4 -4
View File
@@ -17,7 +17,7 @@
background: #fff;
color: #333;
cursor: pointer;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
line-height: 1.4;
text-decoration: none;
}
@@ -41,7 +41,7 @@
background: #fff;
color: #333;
cursor: pointer;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
line-height: 1.4;
text-decoration: none;
}
@@ -82,7 +82,7 @@
color: #fff;
display: inline-block;
font-weight: 700;
font-size: 13px;
font-size: calc(13px * var(--wekan-ui-font-scale, 1));
margin-inline-end: 4px;
margin-bottom: 5px;
padding: 3px 8px;
@@ -238,7 +238,7 @@
width: 24px;
}
.edit-labels .labels-static .card-label {
line-height: 30px;
line-height: calc(30px * var(--wekan-ui-font-scale, 1));
margin-bottom: 4px;
position: relative;
top: auto;
+24 -6
View File
@@ -16,6 +16,12 @@ const getFallbackLabelColor = () => {
return 'green';
};
// Label popups opened from a linked card operate on the source card and its
// label catalogue. Do not infer this from the route: the route still names the
// board containing the linked representation.
const getCardLabelBoard = card =>
card?.getRealBoard?.() || card?.board?.() || Utils.getCurrentBoard();
Template.formLabel.onCreated(function () {
const initialColor = this.data?.color || getFallbackLabelColor();
this.currentColor = new ReactiveVar(initialColor);
@@ -56,7 +62,7 @@ Template.createLabelPopup.helpers({
// is not already used in the board (although it's not a problem if two
// labels have the same color).
defaultColor() {
const board = Utils.getCurrentBoard();
const board = getCardLabelBoard(Template.currentData());
const colors = Array.isArray(labelColors) ? labelColors : [getFallbackLabelColor()];
const labels = Array.isArray(board?.labels) ? board.labels : [];
const usedColors = labels.map(l => l.color);
@@ -111,17 +117,29 @@ Template.cardLabelsPopup.onRendered(function () {
});
Template.cardLabelsPopup.helpers({
board() {
const card = Template.currentData();
// The linked source board is preferred when it is published. Fall back to
// the card's placement/current board: a missing linked source must not turn
// the whole label picker into the lone "Create label" row (#6616).
return getCardLabelBoard(card);
},
isLabelSelected(cardId) {
return (ReactiveCache.getCard(cardId).labelIds || []).includes(this._id);
const card = ReactiveCache.getCard(cardId);
return (card?.getRealCard().labelIds || []).includes(this._id);
},
});
Template.cardLabelsPopup.events({
'click .js-select-label'(event) {
const card = Template.currentData();
async 'click .js-select-label'(event, templateInstance) {
// The event's `this` is the label row. Keep the popup template's card data
// explicitly so a linked card updates its source instead of trying to call
// toggleLabel on the row context.
const card = templateInstance.data;
const labelId = this._id;
card.toggleLabel(labelId);
event.preventDefault();
if (!card?.toggleLabel) return;
await card.toggleLabel(labelId);
},
'click .js-edit-label': Popup.open('editLabel'),
'click .js-add-label': Popup.open('createLabel'),
@@ -131,7 +149,7 @@ Template.createLabelPopup.events({
// Create the new label
'submit .create-label'(event, templateInstance) {
event.preventDefault();
const board = Utils.getCurrentBoard();
const board = getCardLabelBoard(templateInstance.data);
if (!board) {
return;
}
+46 -6
View File
@@ -9,6 +9,11 @@
background: #ccc;
border-radius: 17px;
}
.minicard-wrapper.card-drop-preview {
background: transparent;
border-radius: 0;
pointer-events: none;
}
.minicard-wrapper.ui-sortable-helper {
cursor: grabbing;
transform: rotate(4deg);
@@ -58,13 +63,13 @@
.minicard-details-menu-with-handle {
float: inline-end;
padding-inline-start: 10px;
font-size: 14px;
font-size: 0.875rem;
padding: 0;
z-index: 1;
}
.minicard-details-menu {
float: inline-end;
font-size: 14px;
font-size: 0.875rem;
padding-inline-start: 10px;
}
@media print {
@@ -237,12 +242,12 @@
}
body.mobile-mode .minicard .handle .drag-handle,
body.mobile-mode .minicard .handle .fa {
font-size: 22px;
font-size: 1.375rem;
}
}
.minicard .handle .drag-handle {
font-size: 16px;
font-size: 1rem;
color: #ccc;
display: inline-block;
width: 1.4em;
@@ -253,7 +258,7 @@
"Add card" affordance below (13px). It used to inherit the 16px document
size, which made a card title exactly as loud as the heading of the column
holding it. See the note on .list-header-name in lists/list.css. */
font-size: 14px;
font-size: 0.875rem;
margin-inline-end: 21px;
/* #6444: isolate each title's bidirectional context. The title is a
dir="auto" .viewer; under a global dir="rtl" document, non-isolated
@@ -263,6 +268,33 @@
reverts on refresh). Isolation makes each title a self-contained run. */
unicode-bidi: isolate;
}
/* #4990: the title text is the edit target. It is a block so that the whole
line, not only the words, is that target: a click on the empty space after a
short title is still a click on the title. */
.minicard .minicard-title .minicard-title-text {
display: block;
position: relative;
}
/* #6639: the title text itself is the edit target. A transparent overlay must
not sit above the viewer: it intercepts rendered markdown links, especially
short links whose entire text fits inside the old fixed-width overlay. */
.minicard .minicard-title .minicard-title-text.is-editable {
cursor: text;
}
/* The editor replaces the title in place and is the width of the card. */
.minicard .js-minicard-title-form textarea.minicard-title-editor {
font-size: 0.875rem;
width: 100%;
min-height: 40px;
max-height: 200px;
margin-bottom: 4px;
overflow-y: auto;
resize: none;
user-select: text;
/* Same bidi isolation as the title it stands in for. */
unicode-bidi: isolate;
}
.minicard .minicard-title .card-number {
color: #b3b3b3;
display: inline-block;
@@ -855,8 +887,16 @@
transition: all 0.2s ease;
}
/* List name display on minicard */
/* The name of the LIST this card is in, under the badges - "Show on Minicard /
List title", for reading a card away from the column it lives in (a
swimlane, search results, My Cards).
`clear` is the whole rule: `.minicard .badges` is `float: inline-start`, so
without it this block shares the badges' line and reads as one more badge
rather than as the line it is. Logical `inline-start`, so it clears the
floats on whichever side the page starts. */
.minicard-list-name {
clear: inline-start;
font-size: 0.75em;
color: #8c8c8c;
margin-top: 2px;
+77 -44
View File
@@ -72,33 +72,49 @@ template(name="minicard")
i.fa.fa-check
span {{_ 'upload-completed'}}
.minicard-title
if showDueComplete
if canModifyCard
a.minicard-complete-toggle.js-toggle-card-complete(title="{{#if getDueComplete}}{{_ 'card-mark-incomplete'}}{{else}}{{_ 'card-mark-complete'}}{{/if}}")
.materialCheckBox(class="{{#if getDueComplete}}is-checked{{/if}}")
if $eq 'prefix-with-full-path' currentBoard.presentParentTask
.parent-prefix
| {{ parentString ' > ' }}
if $eq 'prefix-with-parent' currentBoard.presentParentTask
.parent-prefix
| {{ parentCardName }}
if isLinkedBoard
a.js-linked-link
span.linked-icon
i.fa.fa-folder
else if isLinkedCard
a.js-linked-link
span.linked-icon
i.fa.fa-id-card
if getArchived
span.linked-icon.linked-archived
i.fa.fa-archive
+viewer
if showCardNumber
span.card-number
| ##{getCardNumber} &nbsp;
= getTitle
//- #4990: the title is edited where it is read - on the board - exactly
like a list's title is. Clicking the title text opens this form in its
place; everything else on the card still opens the card.
+inlinedForm(classNames="js-minicard-title-form")
+editMinicardTitleForm
else
.minicard-title
if showDueComplete
if canModifyCard
a.minicard-complete-toggle.js-toggle-card-complete(title="{{#if getDueComplete}}{{_ 'card-mark-incomplete'}}{{else}}{{_ 'card-mark-complete'}}{{/if}}")
.materialCheckBox(class="{{#if getDueComplete}}is-checked{{/if}}")
if $eq 'prefix-with-full-path' currentBoard.presentParentTask
.parent-prefix
| {{ parentString ' > ' }}
if $eq 'prefix-with-parent' currentBoard.presentParentTask
.parent-prefix
| {{ parentCardName }}
if isLinkedBoard
a.js-linked-link
span.linked-icon
i.fa.fa-folder
else if isLinkedCard
a.js-linked-link
span.linked-icon
i.fa.fa-id-card
if getArchived
span.linked-icon.linked-archived
i.fa.fa-archive
//- #6639: the title container opens the editor, but a rendered markdown
anchor inside its viewer handles and stops its own click first. The old
fixed 50% overlay sat above short links and made every visible pixel of
them an edit target. With no overlay, links remain links and clicking
ordinary title text still edits in place.
span.minicard-title-text(
class="{{#if canModifyCard}}is-editable js-open-inlined-form{{/if}}"
role="{{#if canModifyCard}}button{{/if}}"
tabindex="{{#if canModifyCard}}0{{/if}}"
title="{{#if canModifyCard}}{{_ 'edit'}}{{/if}}")
+viewer
if showCardNumber
span.card-number
| ##{getCardNumber} &nbsp;
= getTitle
if showLabels
if labels
.minicard-labels(class="{{#if hiddenMinicardLabelText}}minicard-labels-no-text{{/if}}")
@@ -110,10 +126,10 @@ template(name="minicard")
if hiddenMinicardLabelText
.minicard-label(class="{{labelColorClass color}}" style="{{labelColorStyle color}}" title="{{name}}")
.minicard-custom-fields
each customFieldsWD
if definition.showOnCard
if trueValue
if showCustomFieldsOnMinicard
.minicard-custom-fields
each customFieldsWD
if definition.showOnCard
.minicard-custom-field
// If there is custom field label, show label at left,
// and value at right
@@ -169,7 +185,7 @@ template(name="minicard")
if showCreatorOnMinicard
.minicard-creator
+userAvatar(userId=this.userId noRemove=true)
+userAvatar(userId=getCreatorId noRemove=true)
if shouldShowChecklistAtMinicard
each shouldShowChecklistAtMinicard
@@ -257,6 +273,16 @@ template(name="minicard")
.parent-subtext
| {{ parentCardName }}
//- The card's title editor on the board, the same shape as a list's: the text
as a textarea, Save, and an X that closes without saving.
template(name="editMinicardTitleForm")
textarea.minicard-title-editor.js-edit-minicard-title(rows='1' autofocus dir="auto")
= getTitle
.edit-controls.clearfix
button.primary.confirm.js-submit-edit-minicard-title(type="submit") {{_ 'save'}}
a.js-close-inlined-form(title="{{_ 'close'}}")
i.fa.fa-times-thin
template(name="editCardSortOrderPopup")
input.js-edit-card-sort-popup(type='text' autofocus value=sort dir="auto")
.edit-controls.clearfix
@@ -265,6 +291,13 @@ template(name="editCardSortOrderPopup")
template(name="minicardChecklist")
.minicard-checklist
.checklist-header
//- #1591: the same caret as on the opened card, and the same stored key,
so folding a checklist folds it in both places.
a.fa.checklist-collapse.js-collapse-checklist(
class="{{#if checklistCollapsed}}fa-caret-right{{else}}fa-caret-down{{/if}}"
title="{{#if checklistCollapsed}}{{_ 'expand-checklist'}}{{else}}{{_ 'collapse-checklist'}}{{/if}}"
role="button" tabindex="0"
aria-expanded="{{#if checklistCollapsed}}false{{else}}true{{/if}}")
if canModifyCard
+inlinedForm(classNames="js-edit-checklist-title" checklist=checklist)
+editChecklistForm(checklist=checklist)
@@ -275,16 +308,16 @@ template(name="minicardChecklist")
if canModifyCard
a.checklist-menu.js-open-checklist-menu(title="{{_ 'checklistActionsPopup-title'}}")
i.fa.fa-bars
.checklist-items.js-checklist-items
each visibleItems
+inlinedForm(classNames="js-edit-checklist-item" item=. checklist=checklist)
+editChecklistItemForm(type='item' item=. checklist=checklist)
unless checklistCollapsed
.checklist-items.js-checklist-items
each visibleItems
+inlinedForm(classNames="js-edit-checklist-item" item=. checklist=checklist)
+editChecklistItemForm(type='item' item=. checklist=checklist)
else
+checklistItemDetail(item=. checklist=checklist card=card)
if canModifyCard
+inlinedForm(autoclose=false classNames="js-add-checklist-item" checklist=checklist)
+addChecklistItemForm(checklist=checklist showNewlineBecomesNewChecklistItem=false)
else
+checklistItemDetail(item=. checklist=checklist card=card)
if canModifyCard
+inlinedForm(autoclose=false classNames="js-add-checklist-item" checklist=checklist)
+addChecklistItemForm(checklist=checklist showNewlineBecomesNewChecklistItem=false)
else
a.add-checklist-item.js-open-inlined-form(title="{{_ 'add-checklist-item'}}")
i.fa.fa-plus
a.add-checklist-item.js-open-inlined-form(title="{{_ 'add-checklist-item'}}")
i.fa.fa-plus
+155 -52
View File
@@ -7,11 +7,16 @@ import { Utils } from '/client/lib/utils';
import ChecklistItems from '/models/checklistItems';
import Cards from '/models/cards';
import { resolveCoverId } from '/models/lib/linkedCardCover';
import { isChecklistShownAtMinicard } from '/models/lib/minicardChecklistVisibility';
import {
parseChecklistItemTitles,
buildChecklistItemPayload,
} from '/models/lib/checklistItemTitles';
import { normalizeDependencies } from '/models/metadata/dependencies';
import { setCardMenuSource } from '/client/lib/cardMenuSource';
import {
hiddenMinicardLabelText,
toggleMinicardLabelText,
} from '/client/lib/minicardLabelText';
function getMinicardFlag(board, onMinicardField, legacyField, defaultValue) {
if (!board) return false;
@@ -29,6 +34,10 @@ function getMinicardFlag(board, onMinicardField, legacyField, defaultValue) {
// });
Template.minicard.helpers({
showCustomFieldsOnMinicard() {
const board = this.board();
return board?.allowsCustomFieldsOnMinicard === true;
},
// True exactly when the drag handle is rendered (see minicard.jade): the user
// may move the card AND drag handles are on. On a coarse pointer the handle is
// a 48px strip down the leading edge, so the card's own content needs matching
@@ -48,7 +57,7 @@ Template.minicard.helpers({
// when a card has dependencies: the first dependency's icon and color plus the
// total count.
dependencyBadge() {
const deps = normalizeDependencies(this.cardDependencies);
const deps = this.getDependencies();
if (deps.length === 0) return null;
return {
icon: deps[0].icon,
@@ -59,9 +68,10 @@ Template.minicard.helpers({
// #3984: visual card aging — fade cards that have not been touched recently,
// based on dateLastActivity, when the board has card aging enabled.
agingClass() {
const card = this.getRealCard();
const board = ReactiveCache.getBoard(this.boardId);
if (!board || !board.cardAging) return '';
const last = this.dateLastActivity || this.modifiedAt || this.createdAt;
const last = card.dateLastActivity || card.modifiedAt || card.createdAt;
if (!last) return '';
const days = (Date.now() - new Date(last).getTime()) / 86400000;
// #3984: thresholds are board-configurable (board settings / cardSettings API),
@@ -75,29 +85,44 @@ Template.minicard.helpers({
return '';
},
formattedCurrencyCustomFieldValue(definition) {
const customField = this
.customFieldsWD()
.find(f => f._id === definition._id);
const customFieldTrueValue =
customField && customField.trueValue ? customField.trueValue : '';
// This helper is called from `each customFieldsWD`, so `this` is already
// the rendered custom-field row, not the Card. Calling this.customFieldsWD
// threw and prevented minicards with Currency fields from rendering.
const field = this || {};
const fieldDefinition = definition || field.definition || {};
const customFieldTrueValue = field.trueValue;
if (customFieldTrueValue === '' || customFieldTrueValue == null) return '';
const currencyCode = fieldDefinition.settings?.currencyCode || 'USD';
const locale = TAPi18n.getLanguage();
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: definition.settings.currencyCode,
}).format(customFieldTrueValue);
try {
const number = typeof customFieldTrueValue === 'number'
? customFieldTrueValue
: Number(customFieldTrueValue);
if (!Number.isFinite(number)) return String(customFieldTrueValue);
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currencyCode,
}).format(number);
} catch (error) {
return `${currencyCode} ${customFieldTrueValue}`;
}
},
formattedStringtemplateCustomFieldValue(definition) {
const customField = this
.customFieldsWD()
.find(f => f._id === definition._id);
const customFieldTrueValue =
customField && customField.trueValue ? customField.trueValue : [];
const ret = new CustomFieldStringTemplate(definition).getFormattedValue(customFieldTrueValue);
return ret;
// As above, the current row already carries the resolved linked/source
// value and definition. Never reach back through a nonexistent Card API.
const field = this || {};
const fieldDefinition = definition || field.definition;
const customFieldTrueValue = Array.isArray(field.trueValue)
? field.trueValue
: [];
if (!fieldDefinition) return customFieldTrueValue.join(' ');
try {
return new CustomFieldStringTemplate(fieldDefinition)
.getFormattedValue(customFieldTrueValue);
} catch (error) {
return customFieldTrueValue.join(' ');
}
},
showCreatorOnMinicard() {
@@ -110,7 +135,7 @@ Template.minicard.helpers({
return ret;
},
isWatching() {
return this.findWatcher(Meteor.userId());
return this.getRealCard().findWatcher(Meteor.userId());
},
showMembers() {
@@ -166,15 +191,9 @@ Template.minicard.helpers({
return !!(board && board.allowsChecklistCountBadgeOnMinicard);
},
hiddenMinicardLabelText() {
const currentUser = ReactiveCache.getCurrentUser();
if (currentUser) {
return (currentUser.profile || {}).hiddenMinicardLabelText;
} else if (window.localStorage.getItem('hiddenMinicardLabelText')) {
return true;
} else {
return false;
}
hiddenMinicardLabelText,
stickers() {
return this.getStickers();
},
cover() {
// #5666: for a linked card the cover lives on the real card it points at, so
@@ -200,13 +219,13 @@ Template.minicard.helpers({
},
// Upload progress helpers
hasActiveUploads() {
return uploadProgressManager.hasActiveUploads(this._id);
return uploadProgressManager.hasActiveUploads(this.getRealId());
},
uploads() {
return uploadProgressManager.getUploadsForCard(this._id);
return uploadProgressManager.getUploadsForCard(this.getRealId());
},
uploadCount() {
return uploadProgressManager.getUploadCountForCard(this._id);
return uploadProgressManager.getUploadCountForCard(this.getRealId());
},
listName() {
const list = this.list();
@@ -219,7 +238,7 @@ Template.minicard.helpers({
// 2. This specific card has the setting enabled
const currentBoard = this.board();
if (!currentBoard) return false;
return currentBoard.allowsShowListsOnMinicard || this.showListOnMinicard;
return currentBoard.allowsShowListsOnMinicard || this.getRealCard().showListOnMinicard;
},
shouldShowChecklistAtMinicard() {
@@ -231,14 +250,13 @@ Template.minicard.helpers({
const visibleChecklists = [];
checklists.forEach(checklist => {
// Show checklist if either:
// 1. Board-wide setting is enabled, OR
// 2. This specific checklist has the setting enabled
// #5565: use the same board field the sidebar toggle writes
// (`allowsChecklistsOnMinicard`); this previously checked a different,
// UI-less field (`allowsChecklistAtMinicard`), so the board toggle to show
// checklists on minicards had no effect.
if (currentBoard.allowsChecklistsOnMinicard || checklist.showChecklistAtMinicard) {
// The board setting is the DEFAULT and the checklist's own setting is an
// OVERRIDE. This was an OR - board || checklist - and the board setting
// (`allowsChecklistsOnMinicard`, #5565) defaults to TRUE, so unchecking
// "Show on minicard" on a checklist could never hide it: reported by email
// with a screenshot of a checklist still on the minicard after the toggle
// was switched off. See models/lib/minicardChecklistVisibility.js.
if (isChecklistShownAtMinicard(checklist, currentBoard.allowsChecklistsOnMinicard)) {
visibleChecklists.push(checklist);
}
});
@@ -270,6 +288,47 @@ function moveCardBy(card, delta) {
}
Template.minicard.events({
'keydown .minicard-title-text.js-open-inlined-form'(event) {
// The title container replaced an anchor overlay in #6639 so markdown
// anchors can receive clicks. Retain the overlay's keyboard behavior on
// the container itself without turning nested rendered links into editors.
if (
event.target === event.currentTarget &&
(event.key === 'Enter' || event.key === ' ')
) {
event.preventDefault();
event.currentTarget.click();
}
},
// #4990: the title is edited in place. The minicard sits inside the wrapper
// LINK to the card, so every click inside the open form would otherwise
// navigate away mid-edit. Cancelling the click's default stops that - except
// on the save button, whose own default IS the submit (the innermost element
// with an activation behaviour is the one that runs, so the link does not
// follow when the button is clicked).
'click .js-minicard-title-form'(event) {
event.stopPropagation();
if (!$(event.target).closest('button[type=submit]').length) {
event.preventDefault();
}
},
async 'submit .js-minicard-title-form'(event, templateInstance) {
event.preventDefault();
event.stopPropagation();
// #6604: `this` is the nested inlinedForm's data context
// ({ classNames: 'js-minicard-title-form' }), not the Card. The enclosing
// minicard template instance keeps the Card it was created with.
const card = templateInstance.data;
const title = templateInstance
.$('.js-edit-minicard-title')
.val()
?.trim();
// An empty title would leave a card with nothing to click, so an empty
// save is a no-op rather than a way to lose the card - same as a list.
if (card && title && title !== card.getTitle()) {
await card.setTitle(title);
}
},
'click .js-linked-link'() {
if (this.isLinkedCard()) Utils.goCardId(this.linkedId);
else if (this.isLinkedBoard())
@@ -297,23 +356,35 @@ Template.minicard.events({
}
this.setDueComplete(!this.getDueComplete());
},
// The minicard's own copy of this only ever wrote localStorage, so a
// logged-in user toggling it here set something nothing reads. One module
// now, for reading and for writing. client/lib/minicardLabelText.js
'click .js-toggle-minicard-label-text'() {
if (window.localStorage.getItem('hiddenMinicardLabelText')) {
window.localStorage.removeItem('hiddenMinicardLabelText'); //true
} else {
window.localStorage.setItem('hiddenMinicardLabelText', 'true'); //true
}
toggleMinicardLabelText();
},
'click span.badge-icon.fa.fa-sort, click span.badge-text.check-list-sort' : Popup.open("editCardSortOrder"),
'click .minicard-labels'(event, tpl) {
if (tpl.find('.js-card-label:hover')) {
Popup.open("cardLabels")(event, {dataContextIfCurrentDataIsUndefined: Template.currentData()});
// A label on a minicard opens the card's labels, and ONLY that. The click used
// to reach the minicard as well, so the card details opened behind the popup:
// two things for one click, and the one you asked for on top of the one you
// did not.
//
// Which label was clicked is read from the EVENT rather than from `:hover`.
// `:hover` answers about the pointer, and on a touch screen it can still be
// true for whatever was tapped last - so a tap anywhere in the labels area
// could open the labels of a label nobody touched.
'click .minicard-labels'(event) {
if (!$(event.target).closest('.js-card-label').length) {
return; // not a label - let the click be the card's, as before
}
event.preventDefault();
event.stopPropagation();
Popup.open("cardLabels")(event, {dataContextIfCurrentDataIsUndefined: Template.currentData()});
},
'click .js-open-minicard-details-menu'(event, tpl) {
event.preventDefault();
event.stopPropagation();
const card = Template.currentData();
setCardMenuSource('minicard');
Popup.open('cardDetailsActions').call({currentData: () => card}, event);
},
// Drag and drop file upload handlers
@@ -375,6 +446,18 @@ Template.minicard.events({
});
Template.minicardChecklist.helpers({
/** #1591: folded for THIS user, under the same key the opened card uses, so
* the two agree. `this.checklist || this` because this template is called both
* with an explicit checklist and with one as the data context. */
checklistCollapsed() {
const checklist = this.checklist || this;
if (!checklist || !checklist._id) return false;
const user = ReactiveCache.getCurrentUser();
if (!user) return false;
const cardId = (this.card && this.card._id) || checklist.cardId;
return user.getCollapsedCardSection(
cardId, user.checklistSectionKey(checklist._id)) === true;
},
visibleItems() {
const checklist = this.checklist || this;
const items = checklist.items();
@@ -416,6 +499,26 @@ function addMinicardChecklistItems(textarea, checklist) {
}
Template.minicardChecklist.events({
'click .js-collapse-checklist'(event) {
// A minicard is a link to the card, and the title opens the rename form -
// neither must happen because somebody folded a checklist.
event.preventDefault();
event.stopPropagation();
const data = Template.currentData();
const checklist = (data && data.checklist) || data || this;
const user = ReactiveCache.getCurrentUser();
if (!checklist || !checklist._id || !user) return;
const cardId = (data && data.card && data.card._id) || checklist.cardId;
const key = user.checklistSectionKey(checklist._id);
const collapsed = user.getCollapsedCardSection(cardId, key) === true;
user.setCollapsedCardSection(cardId, key, !collapsed);
},
'keydown .js-collapse-checklist'(event) {
if (event.key !== 'Enter' && event.key !== ' ') return;
event.preventDefault();
event.stopPropagation();
$(event.currentTarget).trigger('click');
},
'click .js-convert-checklist-item-to-card'(event) {
event.stopPropagation();
const formData = Blaze.getData(event.currentTarget);
+5 -5
View File
@@ -33,12 +33,12 @@ textarea.js-edit-subtask-item {
flex-shrink: 0;
width: 30px;
height: 30px;
font-size: 18px;
line-height: 30px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
line-height: calc(30px * var(--wekan-ui-font-scale, 1));
}
.subtask-title .title {
font-size: 18px;
line-height: 25px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
line-height: calc(25px * var(--wekan-ui-font-scale, 1));
flex: 1;
order: 1;
}
@@ -121,7 +121,7 @@ textarea.js-edit-subtask-item {
}
.subtasks-item {
margin: 0 0 0 0.1em;
line-height: 18px;
line-height: calc(18px * var(--wekan-ui-font-scale, 1));
font-size: 1.1em;
margin-top: 3px;
display: flex;
+5 -3
View File
@@ -1,7 +1,6 @@
template(name="subtasks")
h3.card-details-item-title
i.fa.fa-globe
| {{_ 'subtasks'}}
//- No title here: +cardSectionHeader draws it, with the caret and the rule.
Two headings for one section is what a card showed before.
if currentUser.isBoardAdmin
if toggleDeleteDialog.get
.board-overlay#card-details-overlay
@@ -107,4 +106,7 @@ template(name="subtaskActionsPopup")
a.js-delete-subtask.delete-subtask
i.fa.fa-trash
| {{_ "delete"}} ...
//- The board's subtask SETTINGS are not here: they belong to the section, not
to one subtask, and they are behind the hamburger at the end of the
Subtasks heading. client/components/cards/cardDetails.jade
+12 -5
View File
@@ -22,7 +22,8 @@ Template.subtasks.events({
event.preventDefault();
const textarea = tpl.find('textarea.js-add-subtask-item');
const title = textarea.value.trim();
const cardId = Template.currentData().cardId;
const contextCard = ReactiveCache.getCard(this.cardId);
const cardId = contextCard?.getRealId ? contextCard.getRealId() : this.cardId;
if (title) {
// Subtask creation is performed server-side by the `addSubtaskCard` Meteor
@@ -32,9 +33,12 @@ Template.subtasks.events({
// (#3868 / #5788 / #2256) and lets multiple subtasks be created reliably
// (#4782), and the method applies the destination board's automatic
// custom fields to the new subtask (#4037 / #3562).
const _id = await Meteor.callAsync('addSubtaskCard', cardId, title);
try {
const _id = await Meteor.callAsync('addSubtaskCard', cardId, title);
if (_id) {
if (!_id) {
throw new Error('The server could not create the subtask.');
}
// In case the filter is active we need to add the newly inserted card in
// the list of exceptions -- cards that are not filtered. Otherwise the
// card will disappear instantly.
@@ -46,10 +50,13 @@ Template.subtasks.events({
.last()
.click();
}, 100);
textarea.value = '';
textarea.focus();
} catch (error) {
alert(error?.reason || error?.message || 'Could not create the subtask.');
textarea.focus();
}
}
textarea.value = '';
textarea.focus();
},
'submit .js-edit-subtask-title'(event, tpl) {
event.preventDefault();
@@ -3,7 +3,7 @@
margin: 5px 0;
padding: 8px;
border-radius: 4px;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
line-height: 1.4;
}
@@ -57,7 +57,7 @@
.original-title {
color: #6c757d;
font-size: 11px;
font-size: calc(11px * var(--wekan-ui-font-scale, 1));
margin-top: 4px;
padding-top: 4px;
border-top: 1px solid #e9ecef;
@@ -78,7 +78,7 @@
/* Responsive adjustments */
@media (max-width: 768px) {
.original-position-info {
font-size: 11px;
font-size: calc(11px * var(--wekan-ui-font-scale, 1));
padding: 6px;
}
+32 -2
View File
@@ -1,8 +1,38 @@
.datepicker-container .fields {
display: flex;
gap: 15px;
margin-bottom: 15px;
}
.datepicker-container .fields .left {
width: 56%;
flex: 1;
min-width: 0;
float: none;
order: 1;
width: auto;
}
.datepicker-container .fields .right {
width: 38%;
flex: 1;
min-width: 0;
float: none;
order: 2;
width: auto;
}
.datepicker-container .fields input[type='date'],
.datepicker-container .fields input[type='time'] {
box-sizing: border-box;
max-width: 100%;
width: 100%;
}
.datepicker-container .fields .custom-field-date-copy {
align-items: center;
display: flex;
flex: 0 0 auto;
order: 3;
padding-top: 24px;
}
.datepicker-container .fields .custom-field-date-copy .custom-field-copy-control {
inset: auto;
position: static;
}
.datepicker-container .datepicker {
width: 100%;
+29 -14
View File
@@ -1,14 +1,29 @@
template(name="datepicker")
.datepicker-container
form.edit-date
.fields
.left
label(for="date") {{_ 'date'}}
input.js-date-field#date(type="date" name="date" value=showDate autofocus)
.right
label(for="time") {{_ 'time'}}
input.js-time-field#time(type="time" name="time" value=showTime)
if error.get
.warning {{_ error.get}}
button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}}
button.js-delete-date.negate.wide.right.js-delete-date {{_ 'delete'}}
//- The date-and-time form every "edit a date" popup is made of: the card's
//- Received, Start, Due and End dates, a vote's and a planning poker's end
//- date, and a date custom field. Eight popups drew this same form, letter for
//- letter, in three different files.
//-
//- It takes what it shows as ARGUMENTS rather than reading helpers: a helper
//- inside a template is looked up on that template, not on the one including
//- it, and all eight popups keep their own state and handlers
//- (client/lib/datepicker.js, which they already shared - it was only the
//- markup that was copied). So this template has no code of its own at all;
//- it renders the popup's datePicker state and the buttons. Its events live on
//- this template too: Blaze events do not cross a child-template boundary.
template(name="editDateForm")
.datepicker-container
form.edit-date(class="{{#if customFieldControls}}custom-field-date-editor{{/if}}")
.fields
.left
label(for="date") {{_ 'date'}}
input.js-date-field#date(type="date" name="date" value=showDate autofocus)
.right
label(for="time") {{_ 'time'}}
input.js-time-field#time(type="time" name="time" value=showTime)
if customFieldControls
.custom-field-date-copy
+customFieldCopyButton(value=customFieldCopyValue)
if error.get
.warning {{_ error.get}}
button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}}
button.js-delete-date.negate.wide.right.js-delete-date {{_ 'delete'}}
-16
View File
@@ -1,16 +0,0 @@
import {
setupDatePicker,
datePickerRendered,
datePickerHelpers,
datePickerEvents,
} from '/client/lib/datepicker';
Template.datepicker.onCreated(function () {
setupDatePicker(this);
});
Template.datepicker.onRendered(function () {
datePickerRendered(this);
});
Template.datepicker.helpers(datePickerHelpers());
+50 -16
View File
@@ -177,7 +177,7 @@ button:active:active {
it in the same menu was the themed one. Two buttons, one menu, two looks.
It is named in the SAME rules rather than given a copy of them, so the theme
accent and the hover and active states have one home.
docs/Design/Page/Theme.md */
docs/Features/Page/Theme.md */
input[type="submit"].primary,
button.primary,
.pop-over .at-form button.at-btn {
@@ -307,9 +307,33 @@ textarea::-moz-placeholder {
}
.edit-controls .fa-times-thin,
.add-controls .fa-times-thin {
font-size: 20px;
font-size: calc(20px * var(--wekan-ui-font-scale, 1));
margin: 5px 21px;
}
/* Custom-field Copy is an editor action above the top-right corner of the
field controls, not part of the Save / Close row. */
.js-card-customfield-text,
.js-card-customfield-number,
.js-card-customfield-checkbox-editor,
.js-card-customfield-currency,
.js-card-customfield-dropdown,
.js-card-customfield-stringtemplate,
.custom-field-date-editor {
position: relative;
}
.custom-field-copy-control {
inset-inline-end: 0;
position: absolute;
top: -24px;
z-index: 1;
}
form.inlined-form .custom-field-copy-control > a.fa-copy,
.custom-field-date-editor .custom-field-copy-control > a.fa-copy {
float: none;
inset: auto;
position: static;
top: auto;
}
/* CJK (Japanese/Chinese/Korean) labels contain no spaces, so browsers may
insert a line break between any two characters. In the narrow add-card /
edit footers this made short labels such as "追加" (Add) and "リンク"
@@ -346,6 +370,16 @@ textarea::-moz-placeholder {
transition: 0.2s;
margin: 0;
cursor: pointer;
/* A CHECKBOX IS SQUARE, WHEREVER IT IS PUT.
Several of the rows that carry one are flex containers - the export popup's
"select what to include", Admin Panel's setting rows, the sidebar's filters
- and a flex item shrinks by default. Next to a long label that squeezed the
13px box into a thin vertical sliver: "Kortin tiedot (jokainen kortti kuten
kortin viennissä)" drew a sliver while "Taulu" beside it stayed square.
`flex: none` here rather than in each of those places, because the shape of
a checkbox is this rule's business and there are 92 of them across 19
templates. */
flex: none;
}
.materialCheckBox.is-checked {
top: -4px;
@@ -388,10 +422,10 @@ textarea::-moz-placeholder {
border-radius: 3px;
color: #fff;
display: none;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
font-weight: 700;
height: 17px;
line-height: 17px;
line-height: calc(17px * var(--wekan-ui-font-scale, 1));
margin: 0;
padding: 2px 4px;
position: absolute;
@@ -445,8 +479,8 @@ textarea::-moz-placeholder {
.button-link.setting .label {
color: #222;
display: block;
font-size: 12px;
line-height: 14px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
line-height: calc(14px * var(--wekan-ui-font-scale, 1));
margin-bottom: 0;
}
.button-link.setting:hover .label {
@@ -454,8 +488,8 @@ textarea::-moz-placeholder {
}
.button-link.setting .value {
display: block;
font-size: 18px;
line-height: 24px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
line-height: calc(24px * var(--wekan-ui-font-scale, 1));
overflow: hidden;
text-overflow: ellipsis;
}
@@ -598,8 +632,8 @@ button.loud-text-button:hover {
padding: 11px;
position: relative;
text-decoration: none;
font-size: 16px;
line-height: 20px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
line-height: calc(20px * var(--wekan-ui-font-scale, 1));
}
.big-link .text {
text-decoration: underline;
@@ -641,9 +675,9 @@ button.loud-text-button:hover {
width: 40px;
}
.big-link.avatar-changer .member .member-initials {
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
height: 40px;
line-height: 40px;
line-height: calc(40px * var(--wekan-ui-font-scale, 1));
max-height: 40px;
}
.show-more {
@@ -675,24 +709,24 @@ button.loud-text-button:hover {
.uploader .realfile {
cursor: pointer;
height: 34px;
line-height: 34px;
line-height: calc(34px * var(--wekan-ui-font-scale, 1));
position: absolute;
top: 0;
inset-inline-start: 0;
width: 100%;
z-index: 2;
font-size: 23px;
font-size: calc(23px * var(--wekan-ui-font-scale, 1));
}
.uploader .realfile input[type="file"] {
cursor: pointer;
height: 34px;
line-height: 34px;
line-height: calc(34px * var(--wekan-ui-font-scale, 1));
margin: 0;
opacity: 0;
padding: 0;
width: 100%;
z-index: 2;
font-size: 23px;
font-size: calc(23px * var(--wekan-ui-font-scale, 1));
}
.uploader:hover .fakefile {
background: #318ec4;
+11 -11
View File
@@ -2,25 +2,25 @@
.ganttview-received {
background-color: #dbdbdb !important;
color: #000 !important;
font-size: 18px !important;
font-size: calc(18px * var(--wekan-ui-font-scale, 1)) !important;
font-weight: bold !important;
}
.ganttview-start {
background-color: #90ee90 !important;
color: #000 !important;
font-size: 18px !important;
font-size: calc(18px * var(--wekan-ui-font-scale, 1)) !important;
font-weight: bold !important;
}
.ganttview-due {
background-color: #ffd700 !important;
color: #000 !important;
font-size: 18px !important;
font-size: calc(18px * var(--wekan-ui-font-scale, 1)) !important;
font-weight: bold !important;
}
.ganttview-end {
background-color: #ffb3b3 !important;
color: #000 !important;
font-size: 18px !important;
font-size: calc(18px * var(--wekan-ui-font-scale, 1)) !important;
font-weight: bold !important;
}
/* Gantt View Styles */
@@ -52,7 +52,7 @@
min-width: 800px;
border: 2px solid #666;
font-family: sans-serif;
font-size: 13px;
font-size: calc(13px * var(--wekan-ui-font-scale, 1));
background-color: #fff;
}
@@ -81,7 +81,7 @@
padding: 2px 1px; /* half */
text-align: center;
background-color: #f5f5f5;
font-size: 11px;
font-size: calc(11px * var(--wekan-ui-font-scale, 1));
min-width: 15px; /* half of 30px */
font-weight: bold;
height: auto;
@@ -110,9 +110,9 @@
min-width: 15px; /* half of 30px */
height: 32px;
vertical-align: middle;
line-height: 28px;
line-height: calc(28px * var(--wekan-ui-font-scale, 1));
background-color: #ffffff;
font-size: 18px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
font-weight: bold;
}
@@ -162,7 +162,7 @@
.gantt-container tbody td.ganttview-block {
background-color: #4CAF50 !important;
color: #fff !important;
font-size: 18px !important;
font-size: calc(18px * var(--wekan-ui-font-scale, 1)) !important;
font-weight: bold !important;
padding: 2px !important;
border-radius: 2px;
@@ -171,7 +171,7 @@
/* Responsive adjustments */
@media (max-width: 768px) {
.gantt-container table {
font-size: 11px;
font-size: calc(11px * var(--wekan-ui-font-scale, 1));
}
.gantt-container thead td {
@@ -187,7 +187,7 @@
.gantt-container tbody td:first-child {
width: 100px;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
}
}
+69 -1
View File
@@ -134,7 +134,7 @@ a.show-mapping {
}
.trello-progress-percent {
text-align: end;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
color: #666;
font-weight: 600;
margin-bottom: 8px;
@@ -165,3 +165,71 @@ a.show-mapping {
font-size: 0.85em;
margin: 2px 0 4px;
}
/* ── The import page's two questions ────────────────────────────────────────
WHICH SOURCE, and WHAT TO TAKE FROM IT. They are asked in that order and
answered in either, so on a wide page they are two columns: fourteen sources
in one and two dozen parts in the other, both on screen at once instead of
the parts living below a list you scroll past. `auto-fit` with a minimum, so
a narrow window stacks them without a second rule. */
.import-page .import-columns {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 24px;
align-items: start;
}
.import-page .import-columns h2 {
font-size: 1.1em;
margin: 0 0 8px;
}
.import-page .import-source-list,
.import-page .import-part-list {
list-style: none;
margin: 0;
padding: 0;
}
/* A row is EXACTLY the row Admin Panel / Announcement draws:
`a.flex > .materialCheckBox + span`, and no geometry of its own. That matters
more than it looks: `.materialCheckBox.is-checked` becomes the tick by
turning two borders transparent, rotating 40deg and shifting itself
(`top: -4px; inset-inline-start: -3px; margin-inline-end: 6px`) - offsets
written for the plain flow `.flex` gives it. An `align-items: center` and a
`gap` on top of that fight those offsets, and the tick lands on the first
word of the label, which is what this page did before.
So only the things that move nothing: something to click against, and a
pointer. forms.css does the rest, the same way it does on the Announcement
page. */
.import-page .import-source-list a.flex,
.import-page .import-part-list a.flex {
padding: 4px 4px;
border-radius: 3px;
color: inherit;
cursor: pointer;
}
.import-page .import-source-list a.flex:hover,
.import-page .import-part-list a.flex:hover {
background: rgba(0, 0, 0, 0.05);
}
/* A space between the box and its label, in BOTH states.
`.materialCheckBox` is 13px plus a 2px border on each side, and carries no
margin, so an unchecked row's text starts right against the box. Checked, it
is a different shape entirely - 7px wide, shifted `inset-inline-start: -3px`,
with `margin-inline-end: 6px` - so its text starts 8px + 6px in.
Lining the two up is arithmetic: 17 + 8 = 25 for the unchecked box, and
8 + 17 = 25 for the checked one, so a row's label sits in the same place
whether it is ticked or not and ticking one no longer nudges its text. */
.import-page .import-source-list a.flex .materialCheckBox,
.import-page .import-part-list a.flex .materialCheckBox {
margin-inline-end: 8px;
}
.import-page .import-source-list a.flex .materialCheckBox.is-checked,
.import-page .import-part-list a.flex .materialCheckBox.is-checked {
margin-inline-end: 17px;
}
+48 -4
View File
@@ -1,10 +1,46 @@
template(name="import")
.wrapper
.wrapper.import-page
if error.get
.warning {{_ error.get}}
if zipImporting
.warning {{_ 'import-trello-zip-progress'}}
+Template.dynamic(template=currentTemplate)
//- #1173: ONE import page. Every source used to be its own address reached
from its own link in the board sidebar, and the page never said which
others existed - so "where do I import a Jira export" was answered by a
menu somewhere else. The source is picked HERE, the parts are picked with
the same checkboxes every export uses, and the boards inside a file that
holds several are picked below that.
//- The two questions side by side when the page is wide enough: which
source, and what to take from it. They are asked in that order and
answered in either, so reading them as two columns beats scrolling past
fourteen sources to reach the parts. client/components/import/import.css
.import-columns
.import-sources
h2 {{_ 'import-source-heading'}}
ul.import-source-list
each importSources
li
//- The same green tick every checkbox in the app uses
(`.materialCheckBox`, forms.css) - the one Admin Panel /
Announcement has. Nothing is chosen to begin with, and choosing
one un-chooses the last: an import reads ONE file in one format,
so this is a radio wearing the app's own checkbox.
a.flex.js-select-import-source(data-source="{{key}}" href="#")
.materialCheckBox(class="{{#if selected}}is-checked{{/if}}")
span {{name}}
if hasImportSource
.import-parts
h2 {{_ 'export-select-what-to-include'}}
p.quiet {{_ 'import-parts-instruction'}}
ul.import-part-list
each importParts
li
a.flex.js-import-part-toggle(data-field="{{field}}" href="#")
.materialCheckBox(class="{{#if checked}}is-checked{{/if}}")
span {{_ label}}
if hasImportSource
hr
+Template.dynamic(template=currentTemplate)
template(name="importTextarea")
form
@@ -16,6 +52,14 @@ template(name="importTextarea")
else
textarea.js-import-json(id='import-textarea' placeholder="{{_ importPlaceHolder}}" autofocus)
| {{jsonText}}
if isWekanImport
//- #1173: a WeKan export can be a .json or a .zip - the .zip is the one to
use when a board's attachments are too large to sit inside one JSON
string. Both land in the same place; the .zip is unpacked in the browser
and its files are put back on their metadata rows.
p.import-file-row
label(for='import-wekan-file') {{_ 'import-wekan-file'}}
input.js-import-json-file(id='import-wekan-file' type="file" accept=".json,.zip,application/json,application/zip")
if isTrelloImport
p.import-file-row
label(for='import-json-file') {{_ 'import-trello-json-file'}}
@@ -29,7 +73,7 @@ template(name="importTextarea")
label(for='import-workspace-name') {{_ 'import-trello-workspace'}}
input.js-import-workspace-name(id='import-workspace-name' type="text" placeholder="{{_ 'import-trello-workspace-placeholder'}}")
input.primary.wide(type="submit" value="{{_ 'import'}}")
button.wide.js-import-without-mapping(type="button") {{_ 'import-without-mapping-members'}}
button.primary.wide.js-import-without-mapping(type="button") {{_ 'import-without-mapping-members'}}
if isTrelloImport
hr
+importTrelloApi
@@ -158,7 +202,7 @@ template(name="importMapMembers")
.mapping-item.ghost-item
form
input.primary.wide(type="submit" value="{{_ 'done'}}")
button.wide.js-import-skip-mapping(type="button") {{_ 'import-without-mapping-members'}}
button.primary.wide.js-import-skip-mapping(type="button") {{_ 'import-without-mapping-members'}}
else
+spinner
+87 -3
View File
@@ -8,6 +8,14 @@ import { kanboardGetMembersToMap } from './kanboardMembersMapper';
import getSlug from 'limax';
import { UserSearchIndex } from '/models/users';
import { Utils } from '/client/lib/utils';
import { productNameOrDefault } from '/models/lib/productName';
import { BOARD_EXPORT_FIELDS } from '/models/lib/exportFields';
import {
selection as importSelection,
selectedFields,
readExportFile,
} from '/client/components/boards/exportScope';
import { pruneImportDocument } from '/models/lib/importParts';
import { TAPi18n } from '/imports/i18n';
import TrelloImportJobs from '/models/trelloImportJobs';
@@ -233,10 +241,17 @@ Template.import.onCreated(function () {
// exports are awkward to paste) or from the textarea.
let input = this.find('.js-import-json').value;
const jsonFileEl = this.find('.js-import-json-file');
let dataObject = null;
if (jsonFileEl && jsonFileEl.files && jsonFileEl.files[0]) {
input = await jsonFileEl.files[0].text();
// #1173: a .zip is read the same way the per-menu import reads one -
// the document out of `wekan.json`, and every file under `attachments/`
// put back on the metadata row it belongs to, so an export that was
// taken as a .zip imports with its files rather than without them.
// readExportFile handles a .json unchanged.
dataObject = await readExportFile(jsonFileEl.files[0]);
} else {
dataObject = JSON.parse(input);
}
const dataObject = JSON.parse(input);
// Guard against importing a broken/old WeKan export that has no board
// content (see wekanExportIsEmpty): warn the user to re-export rather than
// silently creating an empty board with only a Default swimlane.
@@ -350,9 +365,13 @@ Template.import.onCreated(function () {
settled = true;
this.setError('import-timeout');
}, timeoutMs);
// #1173: the parts the page's checkboxes did not tick are taken OUT of the
// document here, before any creator sees it. A creator that never sees a
// comment cannot import one, which is how one selection works for five
// sources without teaching each of them a selection of its own.
Meteor.call(
'importBoard',
importedData,
pruneImportDocument(importedData, selectedFields()),
{ membersMapping: mappingById },
this.importSource,
Session.get('fromBoard'),
@@ -371,10 +390,56 @@ Template.import.onCreated(function () {
};
});
// #1173: every import source, in one list, so the page says what it can read
// instead of the answer living in a menu somewhere else. The order is the one
// the board sidebar's links were in - the familiar one - and the WeKan entry is
// named for the PRODUCT NAME this instance is branded with, because "a previous
// export of ..." should say the name the person sees at the top of their screen.
const IMPORT_SOURCES = [
{ key: 'wekan', product: true },
{ key: 'trello', name: 'Trello' },
{ key: 'csv', name: 'CSV / TSV' },
{ key: 'excel', name: 'Excel' },
{ key: 'jira', name: 'Jira' },
{ key: 'kanboard', name: 'Kanboard' },
{ key: 'deck', name: 'NextCloud Deck' },
{ key: 'openproject', name: 'OpenProject' },
{ key: 'github', name: 'GitHub' },
{ key: 'gitlab', name: 'GitLab' },
{ key: 'gitea', name: 'Gitea' },
{ key: 'forgejo', name: 'Forgejo' },
{ key: 'asana', name: 'Asana' },
{ key: 'zenkit', name: 'Zenkit' },
];
Template.import.helpers({
error() {
return Template.instance().error;
},
importSources() {
const setting = ReactiveCache.getCurrentSetting();
const product = productNameOrDefault(setting && setting.productName);
const current = Session.get('importSource');
return IMPORT_SOURCES.map(source => ({
key: source.key,
// "a previous export of <Product name>", which for an unbranded WeKan is
// "WeKan" and for a rebranded one is whatever it was rebranded to.
name: source.product ? `${product} (JSON, .zip)` : source.name,
selected: source.key === current,
}));
},
hasImportSource() {
return Boolean(Session.get('importSource'));
},
// The SAME selection the export popups use - one list, one meaning: on an
// export it is what goes out, on an import it is what comes in.
importParts() {
return BOARD_EXPORT_FIELDS.map(({ field, label }) => ({
field,
label,
checked: importSelection.get(field),
}));
},
currentTemplate() {
return Template.instance().steps[Template.instance()._currentStepIndex.get()];
},
@@ -383,6 +448,21 @@ Template.import.helpers({
},
});
Template.import.events({
'click .js-select-import-source'(event) {
event.preventDefault();
const source = event.currentTarget.dataset.source;
// The address still names the source, so an import page can be linked and
// the back button works - the picker sets it rather than replacing it.
FlowRouter.go(`/import/${source}`);
},
'click .js-import-part-toggle'(event) {
event.preventDefault();
const field = event.currentTarget.dataset.field;
importSelection.set(field, !importSelection.get(field));
},
});
Template.importTextarea.helpers({
instruction() {
const importSource = Session.get('importSource');
@@ -426,6 +506,10 @@ Template.importTextarea.helpers({
isTrelloImport() {
return Session.get('importSource') === 'trello';
},
// #1173: a previous export of this WeKan, as a .json or as a .zip.
isWekanImport() {
return Session.get('importSource') === 'wekan';
},
isExcelImport() {
return Session.get('importSource') === 'excel';
},
+16 -3
View File
@@ -1,11 +1,24 @@
template(name="basicTabs")
.basicTabs-container(class="{{name}}")
ul.tabs-list
ul.tabs-list(role="tablist")
each tabs
li.tab-item(class="{{isActiveTab slug}} {{class}}" data-tab="{{slug}}") {{name}}
li.tab-item(
id="{{tabDomId slug}}"
class="{{isActiveTab slug}} {{class}}"
data-tab="{{slug}}"
role="tab"
tabindex="{{tabIndex slug}}"
aria-selected="{{isSelectedTab slug}}"
aria-controls="{{panelDomId slug}}") {{name}}
.tabs-content-container
+Template.contentBlock
template(name="tabContent")
section.tabs-content(class="{{isActiveTab slug}}" data-tab="{{slug}}")
section.tabs-content(
id="{{panelDomId slug}}"
class="{{isActiveTab slug}}"
data-tab="{{slug}}"
role="tabpanel"
aria-labelledby="{{tabDomId slug}}"
tabindex="0")
+Template.contentBlock
+39
View File
@@ -1,4 +1,6 @@
import { ReactiveVar } from 'meteor/reactive-var';
import { Tracker } from 'meteor/tracker';
import { tabIndexForKey } from '/client/lib/accessibility';
// Creation-time stack tracking which basicTabs is currently being rendered.
// Blaze creates parent views before children, so when tabContent.onCreated fires,
@@ -7,12 +9,14 @@ import { ReactiveVar } from 'meteor/reactive-var';
// back to the calling context (e.g. membersWidget), not the basicTabs template —
// so walking the view tree cannot find the basicTabs instance.
const _creatingStack = [];
let nextTabsId = 1;
Template.basicTabs.onCreated(function () {
const activeTab = this.data.activeTab
? { slug: this.data.activeTab }
: this.data.tabs[0];
this._activeTab = new ReactiveVar(activeTab);
this._accessibilityId = `basic-tabs-${nextTabsId++}`;
this.isActiveSlug = (slug) => {
const current = this._activeTab.get();
@@ -33,12 +37,39 @@ Template.basicTabs.helpers({
return 'active';
}
},
tabDomId(slug) {
return `${Template.instance()._accessibilityId}-tab-${slug}`;
},
panelDomId(slug) {
return `${Template.instance()._accessibilityId}-panel-${slug}`;
},
tabIndex(slug) {
return Template.instance().isActiveSlug(slug) ? '0' : '-1';
},
isSelectedTab(slug) {
return Template.instance().isActiveSlug(slug) ? 'true' : 'false';
},
});
Template.basicTabs.events({
'click .tab-item'(e, t) {
t._activeTab.set(this);
},
'keydown .tab-item'(event, template) {
const tabs = template.data.tabs || [];
const currentIndex = tabs.findIndex(tab => tab.slug === this.slug);
if (currentIndex < 0) return;
const nextIndex = tabIndexForKey(event, currentIndex, tabs.length);
if (nextIndex === null) return;
event.preventDefault();
template._activeTab.set(tabs[nextIndex]);
Tracker.afterFlush(() => {
const target = template.find(`#${template._accessibilityId}-tab-${tabs[nextIndex].slug}`);
target?.focus();
});
},
});
Template.tabContent.onCreated(function () {
@@ -53,4 +84,12 @@ Template.tabContent.helpers({
const inst = Template.instance()._basicTabsInst;
if (inst && inst.isActiveSlug(slug)) return 'active';
},
tabDomId(slug) {
const inst = Template.instance()._basicTabsInst;
return inst ? `${inst._accessibilityId}-tab-${slug}` : undefined;
},
panelDomId(slug) {
const inst = Template.instance()._basicTabsInst;
return inst ? `${inst._accessibilityId}-panel-${slug}` : undefined;
},
});
+35 -31
View File
@@ -346,7 +346,7 @@ body.mobile-mode [id^="swimlane-"] .list[style*="--list-width"]:not(.list-collap
margin: 5px auto;
z-index: 30;
padding: 5px;
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
white-space: nowrap;
display: block;
width: auto;
@@ -403,7 +403,7 @@ body.mobile-mode [id^="swimlane-"] .list[style*="--list-width"]:not(.list-collap
overflow: hidden;
white-space: normal;
display: block !important;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
line-height: 1.2;
color: #333;
padding: 0;
@@ -476,8 +476,12 @@ form.inlined-form .list-composer {
the cards. The scale is now heading 16 > card title 14 >= add-card link 13,
kept in that order by tests/listTypeScale.test.cjs. Mobile keeps its own
larger sizes (the !important rules further down) - a phone is read at arm's
length. */
font-size: 16px;
length.
#6587: written in rem, not px. The three sizes are the same as before at the
default root size (1rem = 16px), but a px size cannot follow Member Settings
/ Font size, which sets the root font size - so a board's type was the one
part of the UI that ignored the setting entirely. */
font-size: 1rem;
line-height: 1.2;
margin: 0;
font-weight: bold;
@@ -497,7 +501,7 @@ form.inlined-form .list-composer {
background: transparent;
color: #8c8c8c;
font-weight: bold;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
vertical-align: middle;
}
.list-rotated {
@@ -550,7 +554,7 @@ form.inlined-form .list-composer {
border-radius: 0;
background-color: transparent;
cursor: pointer;
font-size: 18px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
line-height: 1.2;
min-width: 30px;
text-align: center;
@@ -621,7 +625,7 @@ form.inlined-form .list-composer {
}
.list.list-collapsed .list-header .list-rotated h2.list-header-name {
width: 100%;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
height: 100%;
line-height: 1.2;
padding: 0;
@@ -672,7 +676,7 @@ form.inlined-form .list-composer {
}
.list.list-collapsed .list-header .list-rotated h2.list-header-name {
width: 100%;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
height: 100%;
line-height: 1.2;
padding: 0;
@@ -723,7 +727,7 @@ form.inlined-form .list-composer {
}
.list.list-collapsed .list-header .list-rotated h2.list-header-name {
width: 100%;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
height: 100%;
line-height: 1.2;
padding: 0;
@@ -774,7 +778,7 @@ form.inlined-form .list-composer {
.list-header .cardCount {
color: #8c8c8c;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
font-weight: bold;
}
.list-header .list-header-plus-top,
@@ -784,7 +788,7 @@ form.inlined-form .list-composer {
padding-inline-start: 4px;
}
.js-open-list-menu {
font-size: 18px;
font-size: calc(18px * var(--wekan-ui-font-scale, 1));
vertical-align: middle;
line-height: 1.2;
}
@@ -828,7 +832,7 @@ form.inlined-form .list-composer {
"Add" button was the biggest thing on the board while typing. 34px is a
comfortable touch target that still reads as a control rather than a slab. */
min-height: 34px;
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
}
.list-body .open-minicard-composer {
border-radius: 2px;
@@ -837,7 +841,7 @@ form.inlined-form .list-composer {
/* Smaller than a card title, because it is not a card: it is the affordance
for making one. At the inherited 16px it was the largest text in the list
body, level with the board's real content. */
font-size: 13px;
font-size: 0.8125rem;
padding: 7px 10px;
position: relative;
text-decoration: none;
@@ -956,7 +960,7 @@ form.inlined-form .list-composer {
}
.list.mobile-view .list-header .list-header-menu-icon {
padding: 14px;
font-size: 40px !important;
font-size: calc(40px * var(--wekan-ui-font-scale, 1)) !important;
text-align: center;
/* Force positioning for iPhone */
position: absolute !important;
@@ -967,7 +971,7 @@ form.inlined-form .list-composer {
}
.list.mobile-view .list-header .list-header-handle {
padding: 14px;
font-size: 48px !important;
font-size: calc(48px * var(--wekan-ui-font-scale, 1)) !important;
text-align: center;
/* Force positioning for iPhone */
position: absolute !important;
@@ -985,7 +989,7 @@ form.inlined-form .list-composer {
grid-row: 1;
grid-column: 2;
align-self: end;
font-size: 20px !important;
font-size: calc(20px * var(--wekan-ui-font-scale, 1)) !important;
font-weight: bold;
line-height: 1.2;
padding-bottom: 2px;
@@ -997,7 +1001,7 @@ form.inlined-form .list-composer {
text-align: start;
padding-inline-start: 0;
margin-inline-start: 0;
font-size: 16px !important;
font-size: calc(16px * var(--wekan-ui-font-scale, 1)) !important;
line-height: 1.2;
}
.list.mobile-view .list-header .list-header-menu {
@@ -1097,7 +1101,7 @@ form.inlined-form .list-composer {
}
.list-header .list-header-menu-icon {
padding: 14px;
font-size: 40px;
font-size: calc(40px * var(--wekan-ui-font-scale, 1));
text-align: center;
/* iOS Safari fallback positioning */
position: absolute;
@@ -1107,7 +1111,7 @@ form.inlined-form .list-composer {
}
.list-header .list-header-handle {
padding: 14px;
font-size: 48px;
font-size: calc(48px * var(--wekan-ui-font-scale, 1));
text-align: center;
/* iOS Safari fallback positioning */
position: absolute;
@@ -1129,7 +1133,7 @@ form.inlined-form .list-composer {
grid-row: 1;
grid-column: 2;
align-self: end;
font-size: 20px;
font-size: calc(20px * var(--wekan-ui-font-scale, 1));
font-weight: bold;
line-height: 1.2;
padding-bottom: 2px;
@@ -1138,7 +1142,7 @@ form.inlined-form .list-composer {
grid-row: 2;
grid-column: 2;
align-self: start;
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
line-height: 1.2;
}
.list-header .list-header-menu {
@@ -1184,7 +1188,7 @@ form.inlined-form .list-composer {
grid-row: 1/3 !important;
grid-column: 3 !important;
padding: 14px !important;
font-size: 40px !important;
font-size: calc(40px * var(--wekan-ui-font-scale, 1)) !important;
text-align: center !important;
}
@@ -1198,7 +1202,7 @@ form.inlined-form .list-composer {
grid-row: 1/3 !important;
grid-column: 4 !important;
padding: 14px !important;
font-size: 48px !important;
font-size: calc(48px * var(--wekan-ui-font-scale, 1)) !important;
text-align: center !important;
}
@@ -1206,7 +1210,7 @@ form.inlined-form .list-composer {
grid-row: 1 !important;
grid-column: 2 !important;
align-self: end !important;
font-size: 20px !important;
font-size: calc(20px * var(--wekan-ui-font-scale, 1)) !important;
font-weight: bold !important;
line-height: 1.2 !important;
padding-bottom: 2px !important;
@@ -1219,7 +1223,7 @@ form.inlined-form .list-composer {
text-align: start !important;
padding-inline-start: 0 !important;
margin-inline-start: 0 !important;
font-size: 16px !important;
font-size: calc(16px * var(--wekan-ui-font-scale, 1)) !important;
line-height: 1.2 !important;
}
@@ -1249,7 +1253,7 @@ form.inlined-form .list-composer {
grid-row: 1/3 !important;
grid-column: 3 !important;
padding: 14px !important;
font-size: 40px !important;
font-size: calc(40px * var(--wekan-ui-font-scale, 1)) !important;
text-align: center !important;
}
@@ -1263,7 +1267,7 @@ form.inlined-form .list-composer {
grid-row: 1/3 !important;
grid-column: 4 !important;
padding: 14px !important;
font-size: 48px !important;
font-size: calc(48px * var(--wekan-ui-font-scale, 1)) !important;
text-align: center !important;
}
@@ -1271,7 +1275,7 @@ form.inlined-form .list-composer {
grid-row: 1 !important;
grid-column: 2 !important;
align-self: end !important;
font-size: 20px !important;
font-size: calc(20px * var(--wekan-ui-font-scale, 1)) !important;
font-weight: bold !important;
line-height: 1.2 !important;
padding-bottom: 2px !important;
@@ -1281,7 +1285,7 @@ form.inlined-form .list-composer {
grid-row: 2 !important;
grid-column: 2 !important;
align-self: start !important;
font-size: 16px !important;
font-size: calc(16px * var(--wekan-ui-font-scale, 1)) !important;
line-height: 1.2 !important;
}
@@ -1618,7 +1622,7 @@ form.inlined-form .list-composer {
.list.list-collapsed .list-header-name-collapsed {
writing-mode: vertical-rl;
text-align: center;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
color: #333;
margin: 0;
padding: 0;
@@ -1642,7 +1646,7 @@ form.inlined-form .list-composer {
display: block !important;
align-items: initial;
justify-content: initial;
font-size: 16px !important;
font-size: calc(16px * var(--wekan-ui-font-scale, 1)) !important;
box-shadow: none;
margin: 5px auto;
z-index: 10;
+113 -21
View File
@@ -7,12 +7,16 @@ import {
suspendBoardDragscroll,
resumeBoardDragscroll,
} from '/client/lib/boardDragscroll';
import {
createCardDropPreview,
retireCardDropPreview,
} from '/client/lib/cardDropPreview';
import Cards from '/models/cards';
import {
isDegenerateSortGap,
computeRepairedDropIndex,
} from '/models/lib/cardSortRepair';
require('/client/lib/jquery-ui.js')
require('/client/lib/jquery-ui.js');
const { calculateIndex } = Utils;
@@ -187,7 +191,7 @@ function saveListWidth(list, width) {
// the per-list width, so EVERY list re-renders at the new width.
if (isFixedListWidth(boardId)) {
if (user) {
Meteor.call('setFixedListWidth', boardId, width, error => {
Meteor.call('setFixedListWidth', boardId, width, (error) => {
if (error) console.error('Error saving fixed list width:', error);
});
} else {
@@ -211,7 +215,7 @@ function saveListWidth(list, width) {
}
} else if (user) {
// Shared width: server also enforces board membership.
Meteor.call('applyListWidth', boardId, listId, width, width, error => {
Meteor.call('applyListWidth', boardId, listId, width, width, (error) => {
if (error) console.error('Error saving shared list width:', error);
});
}
@@ -237,9 +241,11 @@ Template.list.onCreated(function () {
// callback, we basically solve all issues related to reactive updates. A
// comment below provides further details.
Template.list.onRendered(function () {
const boardBodyEl = this.firstNode?.parentElement?.closest?.('.board-body') ||
const boardBodyEl =
this.firstNode?.parentElement?.closest?.('.board-body') ||
document.querySelector('.board-body');
const boardView = boardBodyEl && Blaze.getView(boardBodyEl, 'Template.boardBody');
const boardView =
boardBodyEl && Blaze.getView(boardBodyEl, 'Template.boardBody');
const boardComponent = boardView?.templateInstance?.();
// Initialize list resize functionality immediately
@@ -259,6 +265,9 @@ Template.list.onRendered(function () {
$cards.sortable({
connectWith: '.js-minicards:not(.js-list-full)',
// #6641: an inline title editor lives inside the otherwise draggable card.
// Never let a mouse selection that starts in that form arm the sortable.
cancel: '.js-minicard-title-form, input, textarea, button, select, option',
tolerance: 'pointer',
appendTo: '.board-canvas',
helper(evt, item) {
@@ -329,11 +338,21 @@ Template.list.onRendered(function () {
.children(itemsSelector)
.not(ui.item)
.toArray()
.map(el => Blaze.getData(el))
.map((el) => Blaze.getData(el))
.filter(Boolean);
}
const listData = Blaze.getData(ui.item.parents('.list').get(0));
const listId = listData._id;
const targetContainer = ui.item.parent().get(0);
const cardDomElement = ui.item.get(0);
const droppedCard = Blaze.getData(cardDomElement);
// #6430: sortable('cancel') is still necessary to keep jQuery UI from
// fighting Blaze, but it visibly returns the card to its source until a
// large board finishes its reactive flush. Leave a presentation-only
// clone in the drop slot until the real reactive card arrives.
const dropPreview = !MultiSelection.isActive()
? createCardDropPreview(cardDomElement, targetContainer)
: null;
const currentBoard = Utils.getCurrentBoard();
const defaultSwimlaneId = currentBoard.getDefaultSwimline()._id;
let targetSwimlaneId = null;
@@ -343,8 +362,9 @@ Template.list.onRendered(function () {
Utils.boardView() === 'board-view-swimlanes' ||
currentBoard.isTemplatesBoard()
) {
targetSwimlaneId = Blaze.getData(ui.item.parents('.swimlane').get(0))
._id;
targetSwimlaneId = Blaze.getData(
ui.item.parents('.swimlane').get(0),
)._id;
} else if (listData.swimlaneId) {
targetSwimlaneId = listData.swimlaneId;
}
@@ -371,14 +391,19 @@ Template.list.onRendered(function () {
nextCardData,
nCards,
);
repairedDrop.updates.forEach(u => {
repairedDrop.updates.forEach((u) => {
Cards.update(u._id, { $set: { sort: u.sort } });
});
sortIndex = { base: repairedDrop.base, increment: repairedDrop.increment };
sortIndex = {
base: repairedDrop.base,
increment: repairedDrop.increment,
};
}
if (MultiSelection.isActive()) {
ReactiveCache.getCards(MultiSelection.getMongoSelector(), { sort: ['sort'] }).forEach((card, i) => {
ReactiveCache.getCards(MultiSelection.getMongoSelector(), {
sort: ['sort'],
}).forEach((card, i) => {
const newSwimlaneId = targetSwimlaneId
? targetSwimlaneId
: card.swimlaneId || defaultSwimlaneId;
@@ -390,12 +415,22 @@ Template.list.onRendered(function () {
);
});
} else {
const cardDomElement = ui.item.get(0);
const card = Blaze.getData(cardDomElement);
const card = droppedCard;
const newSwimlaneId = targetSwimlaneId
? targetSwimlaneId
: card.swimlaneId || defaultSwimlaneId;
card.move(currentBoard._id, newSwimlaneId, listId, sortIndex.base);
const moveResult = card.move(
currentBoard._id,
newSwimlaneId,
listId,
sortIndex.base,
);
retireCardDropPreview(
dropPreview,
card._id,
targetContainer,
moveResult,
);
}
if (boardComponent) boardComponent.setIsDragging(false);
},
@@ -404,11 +439,20 @@ Template.list.onRendered(function () {
// overflow: auto), NOT .board-canvas, which only overflows vertically —
// the old code compared against a scrollLeftMax of 0 and never fired, so
// dragging a card toward an off-screen list never auto-scrolled the board.
const { computeEdgeScroll, findLaneUnderPointer } = require('/imports/lib/boardAutoScroll');
const {
computeEdgeScroll,
findLaneUnderPointer,
} = require('/imports/lib/boardAutoScroll');
let scrolled = false;
const lanes = document.querySelectorAll('.js-lists');
const rects = Array.prototype.map.call(lanes, el => el.getBoundingClientRect());
const laneIndex = findLaneUnderPointer(rects, event.clientX, event.clientY);
const rects = Array.prototype.map.call(lanes, (el) =>
el.getBoundingClientRect(),
);
const laneIndex = findLaneUnderPointer(
rects,
event.clientX,
event.clientY,
);
if (laneIndex !== -1) {
const nextLeft = computeEdgeScroll({
pointer: event.clientX,
@@ -423,8 +467,53 @@ Template.list.onRendered(function () {
scrolled = true;
}
}
// vertical auto-scroll stays on the board canvas
const canvas = document.querySelector('.board-canvas');
// #6584: VERTICALLY, the thing to scroll is the LIST the pointer is over,
// not the board canvas. `.list-body` is `overflow-y: scroll` (list.css) and
// is what holds the cards; `.board-canvas` is `overflow-y: auto` and holds
// the swimlanes. Scrolling the canvas moves the whole board, which is what
// the report describes: "The whole Page/Site scrolls down and not the Line".
//
// It only showed up DOWNWARDS, and the asymmetry is the tell. Dragging up,
// the canvas is usually already at scrollTop 0, so computeEdgeScroll
// returned null, nothing here fired, and jQuery UI's own `scroll` option -
// which acts on the placeholder's scrollParent, i.e. the list body - scrolled
// the list, exactly as expected. Dragging down, the canvas nearly always HAS
// room, so this fired first and scrolled the board instead of the list.
//
// The canvas is still scrolled, but only once the list under the pointer
// cannot go further that way - so a drag down a long list scrolls the list,
// and a drag past the end of it moves on to the board. Same shape as the
// horizontal case above, which already picks the lane under the pointer.
let scrolledList = false;
const listBodies = document.querySelectorAll('.list-body');
const listRects = Array.prototype.map.call(listBodies, (el) =>
el.getBoundingClientRect(),
);
// findLaneUnderPointer is a plain rectangle hit-test; the name is about
// where it was first used, not what it can be given.
const listIndex = findLaneUnderPointer(
listRects,
event.clientX,
event.clientY,
);
if (listIndex !== -1) {
const nextListTop = computeEdgeScroll({
pointer: event.clientY,
lowEdge: listRects[listIndex].top,
highEdge: listRects[listIndex].bottom,
scrollPos: listBodies[listIndex].scrollTop,
scrollSize: listBodies[listIndex].scrollHeight,
clientSize: listBodies[listIndex].clientHeight,
});
if (nextListTop !== null) {
listBodies[listIndex].scrollTop = nextListTop;
scrolled = true;
scrolledList = true;
}
}
const canvas = scrolledList
? null
: document.querySelector('.board-canvas');
if (canvas) {
const crect = canvas.getBoundingClientRect();
const nextTop = computeEdgeScroll({
@@ -583,7 +672,8 @@ Template.list.onCreated(function () {
const getEventPageX = (e) => {
const oe = e.originalEvent || e;
if (oe.touches && oe.touches.length) return oe.touches[0].pageX;
if (oe.changedTouches && oe.changedTouches.length) return oe.changedTouches[0].pageX;
if (oe.changedTouches && oe.changedTouches.length)
return oe.changedTouches[0].pageX;
return e.pageX;
};
@@ -669,7 +759,9 @@ Template.list.onCreated(function () {
// as the handler, throwing "handler.apply is not a function" on every
// touch. Use the native API so { passive: false } actually applies and
// preventDefault() works during the resize.
$resizeHandle[0].addEventListener('touchstart', startResize, { passive: false });
$resizeHandle[0].addEventListener('touchstart', startResize, {
passive: false,
});
document.addEventListener('touchmove', doResize, { passive: false });
document.addEventListener('touchend', stopResize, { passive: false });
+2 -1
View File
@@ -1,5 +1,6 @@
template(name="listBody")
.list-body(class="{{#unless isVerticalScrollbars}}no-scrollbars{{/unless}}")
.list-body(class="{{#unless isVerticalScrollbars}}no-scrollbars{{/unless}}"
role="region" tabindex="0" aria-label="{{_ 'list'}}: {{title}}")
.minicards.clearfix.js-minicards(class="{{#if reachedWipLimit}}js-list-full{{/if}}")
+inlinedForm(autoclose=false position="top")
+addCardForm(listId=_id position="top")
+136 -40
View File
@@ -53,6 +53,45 @@ function openCardWindow(cardId) {
}
}
// #6465: "Clicking the mini card again to close the popout is still not
// possible."
//
// On a desktop-sized screen the card details are NOT the route: clicking a
// minicard only writes `openCards`, and boardBody renders one draggable window
// per id in that list. The URL stays on the board. So closing by navigating to
// the board - which is what the second click did - cleared `currentCard` and
// left the window on screen, because nothing had taken the card OUT of
// `openCards`. Only the window's own X button did that, which is why closing
// worked from the card and not from the minicard.
//
// This is the close button's logic (cardDetails.js, `click
// .js-close-card-details`), so both ways of closing a card do the same thing.
function cardWindowIsOpen(cardId) {
if (!Utils.isMiniScreen() && (Session.get('openCards') || []).includes(cardId)) {
return true;
}
return Session.equals('currentCard', cardId);
}
function closeCardWindow(cardId) {
Session.set(
'openCards',
(Session.get('openCards') || []).filter(id => id !== cardId),
);
if (Session.equals('currentCard', cardId)) {
Session.set('currentCard', null);
}
Session.delete('popupCardId');
Session.delete('popupCardBoardId');
// A card opened by its own URL IS the route, and then the board has to be
// navigated back to. A card opened by clicking a minicard is not, and
// navigating would reset the board view for nothing.
const boardId = Session.get('currentBoard');
if (FlowRouter.current()?.params?.cardId === cardId && boardId) {
Utils.goBoardId(boardId);
}
}
Template.listBody.onCreated(function () {
// for infinite scrolling
this.cardlimit = new ReactiveVar(InfiniteScrollIter);
@@ -87,11 +126,13 @@ Template.listBody.onCreated(function () {
}
};
this.cardFormComponent = () => {
// Find addCardForm template instance by DOM traversal
const formEl = this.find('.js-composer');
if (formEl) {
const view = Blaze.getView(formEl, 'Template.addCardForm');
this.cardFormComponent = (submittedForm) => {
// A list renders independent top and bottom composers. Resolve the
// addCardForm below the form that actually submitted; a template-wide
// first match points at the hidden top composer when the bottom one saves.
const composerEl = submittedForm?.querySelector('.js-composer');
if (composerEl) {
const view = Blaze.getView(composerEl, 'Template.addCardForm');
return view?.templateInstance?.() || null;
}
return null;
@@ -121,8 +162,12 @@ Template.listBody.onCreated(function () {
evt.preventDefault();
const firstCardDom = this.find('.js-minicard:first');
const lastCardDom = this.find('.js-minicard:last');
const textarea = $(evt.currentTarget).find('textarea');
const position = Blaze.getData(evt.currentTarget)?.position;
const submittedForm = evt.target?.closest('form');
if (!submittedForm) {
return;
}
const textarea = $(submittedForm).find('textarea.js-card-title');
const position = Blaze.getData(submittedForm)?.position;
const title = textarea.val().trim();
let sortIndex;
@@ -132,7 +177,10 @@ Template.listBody.onCreated(function () {
sortIndex = Utils.calculateIndex(lastCardDom, null).base;
}
const formComponent = this.cardFormComponent();
const formComponent = this.cardFormComponent(submittedForm);
if (!formComponent) {
return;
}
const members = formComponent.members.get();
const labelIds = formComponent.labels.get();
const customFields = formComponent.customFields.get();
@@ -252,10 +300,37 @@ Template.listBody.onCreated(function () {
return;
}
// #4990: clicking the title TEXT edits it on the board, the way a list's
// title is edited. The minicard's own inlinedForm has already opened by the
// time this runs (it is delegated deeper in the DOM), so all that is left
// here is to not open the card on top of the editor - and to stop the
// wrapper link navigating away from the card being renamed. The rest of the
// minicard still opens it, and so does a middle-click or Ctrl-click on the
// link, which never reaches this handler.
const clickedEditableTitle =
clickedTitle && $target.closest('.js-open-inlined-form').length > 0;
if (clickedEditableTitle || $target.closest('.js-minicard-title-form').length > 0) {
// Let the title's own inlinedForm click handler run. Both handlers are
// delegated by Blaze on the same event root, so either propagation stop
// can prevent that sibling handler from replacing the title.
evt.preventDefault();
return;
}
// Title clicks should open the regular board card details view.
if (clickedTitle && !clickedLinkedReference) {
evt.stopImmediatePropagation();
evt.preventDefault();
// #6465: clicking the OPEN card again closes it. The toggle below has
// always been here, but this branch returned before ever reaching it - and
// the title covers most of the minicard, so in practice the second click
// almost always lands here and just re-opened the card that was already
// open. That is why it read as a missing feature rather than a dead
// branch. Same call as below, so both click targets behave alike.
if (cardWindowIsOpen(card._id)) {
closeCardWindow(card._id);
return;
}
Session.delete('popupCardId');
Session.delete('popupCardBoardId');
Session.set('currentCard', card._id);
@@ -265,12 +340,18 @@ Template.listBody.onCreated(function () {
if (Utils.isMiniScreen()) {
evt.preventDefault();
// ...and on a phone, where the card is a popup rather than a pane, the
// second click closes that popup for the same reason.
if (Session.equals('popupCardId', card._id) && Popup.isOpen()) {
Popup.back();
return;
}
Session.set('popupCardId', card._id);
this.cardDetailsPopup(evt);
} else if (Session.equals('currentCard', card._id)) {
} else if (cardWindowIsOpen(card._id)) {
evt.stopImmediatePropagation();
evt.preventDefault();
Utils.goBoardId(Session.get('currentBoard'));
closeCardWindow(card._id);
} else {
// Allow normal href navigation, but if it's the same card URL,
// we'll handle it by directly setting the session
@@ -786,6 +867,10 @@ Template.linkCardPopup.onCreated(function () {
this.selectedBoardId = new ReactiveVar('');
this.selectedSwimlaneId = new ReactiveVar('');
this.selectedListId = new ReactiveVar('');
// Blaze only exposes Template.currentData() while a view is current. Store
// the add-card position now; both confirmation handlers cross an `await`, so
// asking Blaze for it later throws "There is no current view".
this.position = Template.currentData()?.position;
this.boardId = Session.get('currentBoard');
// Only when there IS a board. `currentBoard` is null on every page that is not
@@ -812,12 +897,11 @@ Template.linkCardPopup.onCreated(function () {
this.swimlaneId = listData.swimlaneId || ReactiveCache.getSwimlane({ boardId: this.boardId })._id;
this.getSortIndex = () => {
const position = Template.currentData().position;
let ret;
if (position === 'top') {
if (this.position === 'top') {
const firstCardDom = this.list.find('.js-minicard:first')[0];
ret = Utils.calculateIndex(null, firstCardDom).base;
} else if (position === 'bottom') {
} else if (this.position === 'bottom') {
const lastCardDom = this.list.find('.js-minicard:last')[0];
ret = Utils.calculateIndex(lastCardDom, null).base;
}
@@ -949,9 +1033,9 @@ Template.linkCardPopup.events({
// https://github.com/wekan/wekan/issues/5715
evt.stopPropagation();
evt.preventDefault();
const linkedId = $('.js-select-cards option:selected').val();
const linkedId = tpl.$('.js-select-cards').val();
if (!linkedId) {
const boardId = $('.js-select-boards option:selected').val();
const boardId = tpl.$('.js-select-boards').val();
// No board and no card selected: nothing to link.
if (!boardId) {
Popup.back();
@@ -989,26 +1073,31 @@ Template.linkCardPopup.events({
Popup.back();
return;
}
const nextCardNumber = await tpl.board.getNextCardNumber();
const sortIndex = tpl.getSortIndex();
const _id = Cards.insert({
title: $('.js-select-cards option:selected').text(), //dummy
listId: tpl.listId,
swimlaneId: tpl.swimlaneId,
boardId: tpl.boardId,
sort: sortIndex,
type: 'cardType-linkedCard',
linkedId,
cardNumber: nextCardNumber,
});
Filter.addException(_id);
Popup.back();
try {
const _id = await Meteor.callAsync(
'createLinkedCard',
linkedId,
tpl.boardId,
tpl.swimlaneId,
tpl.listId,
sortIndex,
);
Filter.addException(_id);
Session.set(
'boardSubscriptionGeneration',
(Session.get('boardSubscriptionGeneration') || 0) + 1,
);
Popup.back();
} catch (error) {
alert(error.reason || error.message);
}
},
async 'click .js-link-board'(evt, tpl) {
//LINK BOARD
evt.stopPropagation();
evt.preventDefault();
const impBoardId = $('.js-select-boards option:selected').val();
const impBoardId = tpl.$('.js-select-boards').val();
if (
!impBoardId ||
ReactiveCache.getCard({ linkedId: impBoardId, archived: false })
@@ -1019,7 +1108,7 @@ Template.linkCardPopup.events({
const nextCardNumber = await tpl.board.getNextCardNumber();
const sortIndex = tpl.getSortIndex();
const _id = Cards.insert({
title: $('.js-select-boards option:selected').text(), //dummy
title: tpl.$('.js-select-boards option:selected').text(), //dummy
listId: tpl.listId,
swimlaneId: tpl.swimlaneId,
boardId: tpl.boardId,
@@ -1043,30 +1132,30 @@ Template.searchElementPopup.onCreated(function () {
this.isSwimlaneTemplateSearch = $(
Popup._getTopStack().openerElement,
).hasClass('js-open-add-swimlane-menu');
this.isBoardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass(
'js-add-board',
);
const popupOpener = $(Popup._getTopStack().openerElement);
this.isBoardTemplateSearch =
popupOpener.hasClass('js-add-board') ||
popupOpener.hasClass('js-create-board');
this.isTemplateSearch =
this.isCardTemplateSearch ||
this.isListTemplateSearch ||
this.isSwimlaneTemplateSearch ||
this.isBoardTemplateSearch;
this.board = {};
let boardId = '';
if (this.isTemplateSearch) {
const boardId = (ReactiveCache.getCurrentUser().profile || {}).templatesBoardId;
boardId = (ReactiveCache.getCurrentUser().profile || {}).templatesBoardId;
if (boardId) {
Meteor.subscribe('board', boardId, false);
this.board = ReactiveCache.getBoard(boardId);
}
} else {
this.board = Utils.getCurrentBoard();
boardId = (Utils.getCurrentBoard() || {})._id;
}
if (!this.board) {
if (!boardId) {
Popup.back();
return;
}
this.boardId = this.board._id;
this.boardId = boardId;
// Subscribe to this board
Meteor.subscribe('board', this.boardId, false);
this.selectedBoardId = new ReactiveVar(this.boardId);
@@ -1123,6 +1212,11 @@ Template.searchElementPopup.helpers({
return [];
}
const board = ReactiveCache.getBoard(tpl.selectedBoardId.get());
// The template container is subscribed when this popup is created. Keep
// the popup open and reactively return results once Minimongo receives it;
// synchronously requiring the board here bounced a freshly opened picker
// back to Create Board and could leave only stale cached templates visible.
if (!board) return [];
if (!tpl.isTemplateSearch || tpl.isCardTemplateSearch) {
return board.searchCards(tpl.term.get(), false);
} else if (tpl.isListTemplateSearch) {
@@ -1160,6 +1254,8 @@ Template.searchElementPopup.events({
.trim();
if (!title) return;
const element = Blaze.getData(evt.currentTarget);
const sourceBoard = ReactiveCache.getBoard(tpl.boardId);
if (!sourceBoard) return;
element.title = title;
let _id = '';
if (!tpl.isTemplateSearch || tpl.isCardTemplateSearch) {
@@ -1171,7 +1267,7 @@ Template.searchElementPopup.events({
// no card was created from the template. Capture it while still in the
// synchronous event/view context.
const sortIndex = tpl.getSortIndex();
element.cardNumber = await tpl.board.getNextCardNumber();
element.cardNumber = await sourceBoard.getNextCardNumber();
element.sort = sortIndex;
// 1.A From template
if (tpl.isTemplateSearch) {
+30 -24
View File
@@ -130,7 +130,7 @@ template(name="editListTitleForm")
template(name="listActionPopup")
//- Copy link, FIRST and above every permission check - see the swimlane
popup: copying an address is reading, not editing.
models/lib/boardItemUrl.js, docs/Design/Page/Board-Item-Links.md
models/lib/boardItemUrl.js, docs/Features/Page/Board-Item-Links.md
ul.pop-over-list
li: a.js-copy-list-link
i.fa.fa-link
@@ -138,6 +138,22 @@ template(name="listActionPopup")
| {{_ 'copy-link-to-clipboard'}}
span.copied-tooltip {{_ 'copied'}}
hr
//- #1173: exporting a list is the board export restricted to it. Reading, like
the link above it, so it is not behind the edit permissions below. The menu
already says what it is about, so the rows are named "Export" and "Import"
and not "Export list" - the noun was in the heading above them either way.
Import WRITES, so it is offered only to somebody who may change the board;
`canImportIntoBoard` is the same question the popup itself asks
(client/components/boards/exportScope.js).
ul.pop-over-list
li: a.js-export-list
i.fa.fa-upload
span {{_ 'export'}}
if canImportIntoBoard
li: a.js-import-list
i.fa.fa-download
span {{_ 'import'}}
hr
unless currentUser.isReadOnly
unless currentUser.isReadAssignedOnly
ul.pop-over-list
@@ -319,9 +335,13 @@ template(name="setListColorPopup")
button.primary.confirm.js-submit {{_ 'save'}}
button.js-remove-color.negate.wide.right {{_ 'unset-color'}}
template(name="copyListPopup")
label(for='copy-list-title') {{_ 'title'}}:
input#copy-list-title.full-line(type="text" value="{{title}}" autofocus)
//- WHERE a list goes: the board, the swimlane, which list to put it left or
right of, and Done. Copy list and Move list asked the same question with the
same form; only the title field above it differs, so that stays with each.
The dialog comes from the popup that includes this and is read from the
template INSTANCE, because inside `each boards` the data context is a board.
client/components/lists/listHeader.js
template(name="listDestinationPicker")
label {{_ 'boards'}}:
select.js-select-boards
each boards
@@ -343,29 +363,15 @@ template(name="copyListPopup")
.edit-controls.clearfix
button.primary.confirm.js-done {{_ 'done'}}
template(name="copyListPopup")
label(for='copy-list-title') {{_ 'title'}}:
input#copy-list-title.full-line(type="text" value="{{title}}" autofocus)
+listDestinationPicker(dialog=dialog)
template(name="moveListPopup")
label(for='move-list-title') {{_ 'title'}}:
input#move-list-title.full-line(type="text" value="{{title}}" autofocus)
label {{_ 'boards'}}:
select.js-select-boards
each boards
option(value="{{_id}}" selected="{{#if isDialogOptionBoardId _id}}selected{{/if}}") {{add @index 1}}. {{title}}
label {{_ 'swimlanes'}}:
select.js-select-swimlanes
each swimlanes
option(value="{{_id}}" selected="{{#if isDialogOptionSwimlaneId _id}}selected{{/if}}") {{add @index 1}}. {{isTitleDefault title}}
label {{_ 'lists'}}:
select.js-select-lists
each lists
option(value="{{_id}}" selected="{{#if isDialogOptionListId _id}}selected{{/if}}") {{add @index 1}}. {{title}}
div
input(type="radio" name="list-position" value="left" checked id="list-position-left" style="display: inline")
label(for="list-position-left") {{_ 'left-of-list'}}
div
input(type="radio" name="list-position" value="right" id="list-position-right" style="display: inline")
label(for="list-position-right") {{_ 'right-of-list'}}
.edit-controls.clearfix
button.primary.confirm.js-done {{_ 'done'}}
+listDestinationPicker(dialog=dialog)
template(name="addListPopup")
form.js-add-list-form
+44 -20
View File
@@ -314,6 +314,8 @@ Template.listActionPopup.events({
}
Popup.back();
},
'click .js-export-list': Popup.open('exportList'),
'click .js-import-list': Popup.open('importList'),
'click .js-add-list': Popup.open('addList'),
'click .js-set-list-width': Popup.open('setListWidth'),
'click .js-set-color-list': Popup.open('setListColor'),
@@ -442,28 +444,50 @@ Template.listMorePopup.events({
}),
});
// The three selects of the copy/move list dialogs, in one template. The dialog
// is kept on THIS instance: inside `each boards` the data context is a board,
// so a helper reaching into the context for it would find nothing.
Template.listDestinationPicker.onCreated(function () {
this.autorun(() => {
const data = Template.currentData();
this.dialog = data && data.dialog;
});
});
Template.listDestinationPicker.helpers({
boards() {
return Template.instance().dialog.boards();
},
swimlanes() {
return Template.instance().dialog.swimlanes();
},
lists() {
return Template.instance().dialog.lists();
},
isDialogOptionBoardId(boardId) {
return Template.instance().dialog.isDialogOptionBoardId(boardId);
},
isDialogOptionSwimlaneId(swimlaneId) {
return Template.instance().dialog.isDialogOptionSwimlaneId(swimlaneId);
},
isDialogOptionListId(listId) {
return Template.instance().dialog.isDialogOptionListId(listId);
},
isTitleDefault(title) {
return Template.instance().dialog.isTitleDefault(title);
},
});
function registerListDialogTemplate(templateName) {
// The markup those helpers feed is one template - `listDestinationPicker` in
// listHeader.jade, included by both popups - so the helpers are registered on
// it, once, below. Each popup provides the `dialog` and keeps the events: a
// change or a click inside the picker bubbles up to the popup that includes
// it, which is the one holding the dialog.
Template[templateName].helpers({
boards() {
return Template.instance().dialog.boards();
},
swimlanes() {
return Template.instance().dialog.swimlanes();
},
lists() {
return Template.instance().dialog.lists();
},
isDialogOptionBoardId(boardId) {
return Template.instance().dialog.isDialogOptionBoardId(boardId);
},
isDialogOptionSwimlaneId(swimlaneId) {
return Template.instance().dialog.isDialogOptionSwimlaneId(swimlaneId);
},
isDialogOptionListId(listId) {
return Template.instance().dialog.isDialogOptionListId(listId);
},
isTitleDefault(title) {
return Template.instance().dialog.isTitleDefault(title);
dialog() {
return Template.instance().dialog;
},
});
+1 -1
View File
@@ -81,7 +81,7 @@
}
.accessibility-page h2 {
font-size: 24px;
font-size: calc(24px * var(--wekan-ui-font-scale, 1));
margin-bottom: 20px;
color: #4d4d4d;
}
+32
View File
@@ -47,6 +47,38 @@
margin: 0 0 4px;
padding: 0;
}
/* Two switches, one row: what the theme IS on the start side ("Default (no
override)"), and where it ALSO applies on the end side ("All Boards", which
paints the overview's tiles in the theme's lighter colour). They are one
decision about the same theme, so they are one row rather than two stacked
menus, and each takes half the width so neither reads as the heading of the
other. On a narrow popup the row wraps and they stack, which is the same
layout one item at a time. */
.theme-color-picker ul.pop-over-list.theme-scope-row {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.theme-color-picker ul.pop-over-list.theme-scope-row > li {
flex: 1 1 12em;
min-width: 0;
}
/* The chosen one is filled, not merely ticked: the tick alone is easy to miss on
a row that is otherwise identical to the one beside it. Same fill as the
selected row of the All Boards left menu and of the Admin Panel - one
selected-thing look everywhere.
`--theme-accent-fill`, not `--theme-accent`: the accent is one colour - the
solid end of a colour-slide theme - so on clearorange this button was flat
orange while the menu row behind the popup slid, which is two looks for one
selected thing. The fill is the slide where the theme has one. */
.theme-color-picker ul.pop-over-list.theme-scope-row > li.active > a,
.theme-color-picker ul.pop-over-list.theme-scope-row > li.active > a:hover {
background: var(--theme-accent-fill, var(--theme-accent, #2980b9));
color: #fff !important;
}
.theme-color-picker ul.pop-over-list.theme-scope-row > li.active > a i {
color: #fff !important;
}
/* As many colour swatches visible at once as the width allows.
*
+8 -1
View File
@@ -1,7 +1,7 @@
template(name="dueCardsControls")
//- The controls of this page. They were a group in its second header bar,
beside a title the first bar now carries; they are a view of the shared
page sidebar. models/lib/pageSidebar.js, docs/Design/Page/Header.md
page sidebar. models/lib/pageSidebar.js, docs/Features/Page/Header.md
a.board-header-btn.js-due-cards-view-change(title="{{_ 'dueCardsViewChange-title'}}")
i.fa.fa-caret-down
if $eq dueCardsView 'me'
@@ -35,6 +35,13 @@ template(name="dueCards")
.due-cards-list
each card in dueCardsList
+resultCard(card)
.global-search-pager
if hasPreviousPage
button.js-due-cards-previous-page
| {{_ 'previous-page' }}
if hasNextPage
button.js-due-cards-next-page
| {{_ 'next-page' }}
else
.global-search-results-list-wrapper
.no-results
+31 -1
View File
@@ -18,12 +18,15 @@ Template.dueCardsControls.events({
});
Template.dueCards.onCreated(function () {
const PAGE_SIZE = 200;
this._cachedCards = null;
this._cachedTimestamp = null;
this.subscriptionHandle = null;
this.isLoading = new ReactiveVar(true);
this.hasResults = new ReactiveVar(false);
this.searching = new ReactiveVar(false);
this.page = new ReactiveVar(0);
this.pageSize = PAGE_SIZE;
const tpl = this;
@@ -106,10 +109,16 @@ Template.dueCards.onCreated(function () {
// Subscribe to the optimized due cards publication
this.autorun(() => {
const allUsers = dueCardsView() === 'all';
const page = tpl.page.get();
if (tpl.subscriptionHandle) {
tpl.subscriptionHandle.stop();
}
tpl.subscriptionHandle = Meteor.subscribe('dueCards', allUsers);
tpl.subscriptionHandle = Meteor.subscribe(
'dueCards',
allUsers,
PAGE_SIZE,
page * PAGE_SIZE,
);
// Update loading state based on subscription
tpl.autorun(() => {
@@ -175,6 +184,27 @@ Template.dueCards.helpers({
return result;
}
},
hasPreviousPage() {
return Template.instance().page.get() > 0;
},
hasNextPage() {
const tpl = Template.instance();
const cards = tpl.dueCardsList ? tpl.dueCardsList() : [];
return cards.length === tpl.pageSize;
},
});
Template.dueCards.events({
'click .js-due-cards-previous-page'(event, tpl) {
event.preventDefault();
tpl._cachedCards = null;
tpl.page.set(Math.max(0, tpl.page.get() - 1));
},
'click .js-due-cards-next-page'(event, tpl) {
event.preventDefault();
tpl._cachedCards = null;
tpl.page.set(tpl.page.get() + 1);
},
});
Template.dueCardsViewChangePopup.events({
+5
View File
@@ -5,6 +5,11 @@
top: -10px;
inset-inline-end: 60px;
}
.inlined-form.js-card-customfield-text > a.fa.fa-brands.fa-markdown {
float: none;
inset-inline-end: 28px;
top: -24px;
}
.new-comment a.fa.fa-copy,
.inlined-form a.fa.fa-copy {
float: inline-end;
+3 -2
View File
@@ -1,7 +1,8 @@
template(name="editor")
a.fa.fa-brands.fa-markdown(title="{{_ 'convert-to-markdown'}}")
a.fa.fa-copy(title="{{_ 'copy-text-to-clipboard'}}")
span.copied-tooltip {{_ 'copied'}}
unless hideCopy
a.fa.fa-copy(title="{{_ 'copy-text-to-clipboard'}}")
span.copied-tooltip {{_ 'copied'}}
textarea.editor(
dir="auto"
class="{{class}}"
+1 -1
View File
@@ -1,7 +1,7 @@
template(name="globalSearchControls")
//- The controls of this page. They were a group in its second header bar,
beside a title the first bar now carries; they are a view of the shared
page sidebar. models/lib/pageSidebar.js, docs/Design/Page/Header.md
page sidebar. models/lib/pageSidebar.js, docs/Features/Page/Header.md
a.board-header-btn.js-global-search-view-change(title="{{_ 'globalSearchViewChange-title'}}")
i.fa.fa-caret-down
if $eq globalSearchView 'me'
+2
View File
@@ -372,6 +372,7 @@ Template.globalSearch.helpers({
operator_debug: TAPi18n.__('operator-debug'),
operator_org: TAPi18n.__('operator-org'),
operator_team: TAPi18n.__('operator-team'),
operator_number: TAPi18n.__('operator-number'),
predicate_overdue: TAPi18n.__('predicate-overdue'),
predicate_archived: TAPi18n.__('predicate-archived'),
predicate_all: TAPi18n.__('predicate-all'),
@@ -414,6 +415,7 @@ Template.globalSearch.helpers({
['\n- ', 'globalSearch-instructions-operator-creator'],
['\n- ', 'globalSearch-instructions-operator-org'],
['\n- ', 'globalSearch-instructions-operator-team'],
['\n- ', 'globalSearch-instructions-operator-number'],
['\n- ', 'globalSearch-instructions-operator-due'],
['\n- ', 'globalSearch-instructions-operator-created'],
['\n- ', 'globalSearch-instructions-operator-modified'],
+15 -1
View File
@@ -28,7 +28,7 @@ import { activeAccent, activeFill } from '/models/lib/themeAccents';
// The class the CHROME (the two top bars, the board wrapper) must carry, from the
// same order of themes the <body> autorun below applies:
// the user's own override, then the board's own colour on a board page, then the
// site theme (docs/Design/Page/Theme.md). Registered as a Blaze helper so the
// site theme (docs/Features/Page/Theme.md). Registered as a Blaze helper so the
// templates read ONE answer instead of each re-deriving part of the order - which is
// what left the top bars on the default colour while the buttons took the site theme.
Template.registerHelper('themeColorClass', () => {
@@ -110,6 +110,20 @@ Meteor.startup(() => {
}
}
// Member Settings / Change color, beside "Default (no override)": paint the All
// Boards tiles in the theme's lighter colour instead of each board's own. It is
// a class on <body> rather than one on the list, because the preference is the
// user's and follows them to every page that shows board tiles.
Tracker.autorun(() => {
const user = Meteor.user();
const on = !!(user && user.profile && user.profile.allBoardsThemeTiles);
try {
document.body.classList.toggle('has-theme-board-tiles', on);
} catch (_) {
// document.body may not exist yet in exotic embeddings; ignore.
}
});
Tracker.autorun(() => {
const user = Meteor.user();
const globalColor = user && user.profile && user.profile.globalThemeColor;
+53 -30
View File
@@ -40,7 +40,7 @@
row-gap: 4px;
}
#header #header-main-bar h1 {
font-size: 20px;
font-size: calc(20px * var(--wekan-ui-font-scale, 1));
/* A flex box with its own centred line, so the title sits on the bar's middle with
everything else instead of on the middle of a line box far taller than its text. */
display: flex;
@@ -127,11 +127,11 @@
color: #f2f2f2;
padding: 0 6px;
height: 28px;
font-size: 13px;
font-size: calc(13px * var(--wekan-ui-font-scale, 1));
display: inline-flex;
align-items: center;
gap: 4px;
line-height: 28px;
line-height: calc(28px * var(--wekan-ui-font-scale, 1));
margin: 0 4px;
white-space: nowrap;
}
@@ -209,10 +209,10 @@
color: #f2f2f2;
padding: 0;
height: 28px;
font-size: 13px;
font-size: calc(13px * var(--wekan-ui-font-scale, 1));
float: inline-start;
overflow: hidden;
line-height: 28px;
line-height: calc(28px * var(--wekan-ui-font-scale, 1));
margin: 0 12px;
}
/* The sidebar hamburger sits at the far right; drop its wide 12px side margins so
@@ -228,7 +228,7 @@
#header #header-main-bar .board-header-btn i.fa {
float: inline-start;
display: block;
line-height: 28px;
line-height: calc(28px * var(--wekan-ui-font-scale, 1));
color: #f2f2f2;
margin: 0 10px;
}
@@ -246,7 +246,7 @@
#header #header-main-bar .board-header-btn .board-header-btn-icon {
float: inline-start;
display: block;
line-height: 28px;
line-height: calc(28px * var(--wekan-ui-font-scale, 1));
color: #27ae60;
margin: 0 10px;
cursor: pointer;
@@ -299,7 +299,7 @@ body:not(.board-view) #header #header-main-bar h1 {
the same, by naming this bar in the selector list the main bar already has
(boardColors.css) rather than by restating each hex a second time. */
background: #2980b9;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
display: flex;
z-index: 1000;
padding: 10px var(--wekan-header-gutter);
@@ -348,6 +348,29 @@ body:not(.board-view) #header #header-main-bar h1 {
margin-block: 0;
}
/* The page title beside the house. On a board, and for a board admin, it is
also the rename button (client/components/main/header.js) - so it says so on
hover, the way a list's heading does.
`min-width` is the part that is easy to miss: a board whose title is EMPTY
renders no text at all, and an element with no content is zero pixels wide
and cannot be clicked. The minimum keeps a strip of the bar clickable, so an
empty title can still be given one. `min-height` does the same for the other
axis, and the padding makes the target bigger than the glyphs. */
#header-quick-access .header-page-title.is-editable {
cursor: text;
display: inline-block;
min-width: 4em;
min-height: 1.3em;
padding: 2px 4px;
border-radius: 4px;
transition: background-color 0.2s ease;
}
#header-quick-access .header-page-title.is-editable:hover {
background-color: rgba(255, 255, 255, 0.1);
}
#header-quick-access .home-icon {
display: flex;
align-items: center;
@@ -382,7 +405,7 @@ body:not(.board-view) #header #header-main-bar h1 {
}
#header-quick-access .home-icon .fa-home {
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
margin-inline-end: 4px;
}
@@ -395,9 +418,9 @@ body:not(.board-view) #header #header-main-bar h1 {
- the same 22px the left menu's rows below it are indented by (4px of margin
plus 18px of padding), so the two line up down the page.
The gap to the logo beside it is the 4 + 4 of the two margins, like every
other pair of neighbours in this bar. docs/Design/Page/Header.md */
other pair of neighbours in this bar. docs/Features/Page/Header.md */
#header-quick-access .allBoards {
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
padding-block: 4px;
padding-inline: 0;
}
@@ -421,14 +444,14 @@ body.mobile-mode #header-quick-access .home-icon .header-home-link {
body.mobile-mode #header-quick-access .home-icon .header-home-link .fa-home,
body.mobile-mode #header-quick-access .home-icon .fa-home {
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
margin-inline-end: 0;
}
/* No inline padding in mobile mode either. This rule existed to cut the 15px
the desktop `.allBoards` carried down to 6px; that padding is gone, so
matching it here would only put the house back off the line every other width
starts on. docs/Design/Page/Header.md */
starts on. docs/Features/Page/Header.md */
body.mobile-mode #header-quick-access .allBoards {
padding-block: 4px;
padding-inline: 0;
@@ -649,7 +672,7 @@ body.mobile-mode #header-quick-access .allBoards {
line-height: normal !important;
margin: 0 !important;
transition: all 0.2s ease !important;
font-size: 14px !important;
font-size: calc(14px * var(--wekan-ui-font-scale, 1)) !important;
}
#header-quick-access .mobile-mode-toggle .board-header-btn i.active {
@@ -762,11 +785,11 @@ body.mobile-mode #header-quick-access .allBoards {
readable-font minimum (~44px / 16px). Enlarged vertically + bigger font;
width is unchanged so this does not worsen the horizontal icon crowding. */
height: 44px;
line-height: 44px;
font-size: 16px;
line-height: calc(44px * var(--wekan-ui-font-scale, 1));
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
}
#header #header-main-bar .board-header-btn i.fa {
line-height: 32px;
line-height: calc(32px * var(--wekan-ui-font-scale, 1));
}
#header #header-main-bar .board-header-btn i.fa + span {
display: none;
@@ -786,17 +809,17 @@ body.mobile-mode #header-quick-access .allBoards {
}
#header-quick-access .home-icon .fa-home {
font-size: 16px;
font-size: calc(16px * var(--wekan-ui-font-scale, 1));
margin-inline-end: 4px;
}
/* 6px at the START, the same as at every other width, so the house begins on
one line down the page no matter how wide the window is. It was 8px, which
moved it 2px in on a phone alone. The end stays 8px - that is a gap between
two things, not the inset. docs/Design/Page/Header.md */
two things, not the inset. docs/Features/Page/Header.md */
#header-quick-access .home-icon .header-home-link {
padding: 4px 8px 4px 6px;
font-size: 12px;
font-size: calc(12px * var(--wekan-ui-font-scale, 1));
}
/* Ensure All Boards text is visible on mobile */
@@ -808,12 +831,12 @@ body.mobile-mode #header-quick-access .allBoards {
/* Adjust for very small screens */
@media screen and (max-width: 480px) {
#header-quick-access .home-icon .header-home-link {
font-size: 11px;
font-size: calc(11px * var(--wekan-ui-font-scale, 1));
padding: 3px 6px;
}
#header-quick-access .home-icon .fa-home {
font-size: 14px;
font-size: calc(14px * var(--wekan-ui-font-scale, 1));
margin-inline-end: 3px;
}
}
@@ -1079,15 +1102,15 @@ body.mobile-mode #header-quick-access .allBoards {
}
#header-quick-access .home-icon .header-home-link {
font-size: 16px !important; /* Much larger text */
font-size: calc(16px * var(--wekan-ui-font-scale, 1)) !important; /* Much larger text */
/* 6px at the START here too - the tap target grows, the INSET does not.
At 12px the house began 6px further in on a small phone than at every
other width. docs/Design/Page/Header.md */
other width. docs/Features/Page/Header.md */
padding: 8px 12px 8px 6px !important;
}
#header-quick-access .home-icon .fa-home {
font-size: 20px !important; /* Much larger icon */
font-size: calc(20px * var(--wekan-ui-font-scale, 1)) !important; /* Much larger icon */
margin-inline-end: 6px !important;
}
@@ -1098,11 +1121,11 @@ body.mobile-mode #header-quick-access .allBoards {
/* Make mobile mode toggle larger */
#header-quick-access .mobile-mode-toggle .board-header-btn {
padding: 7px 17px !important;
font-size: 16px !important;
font-size: calc(16px * var(--wekan-ui-font-scale, 1)) !important;
}
#header-quick-access .mobile-mode-toggle .board-header-btn i {
font-size: 18px !important;
font-size: calc(18px * var(--wekan-ui-font-scale, 1)) !important;
}
}
@@ -1419,7 +1442,7 @@ body.mobile-mode #header-quick-access .allBoards {
}
.iphone-device #header-quick-access .home-icon .header-home-link .fa-home,
.iphone-device #header-quick-access .home-icon .fa-home {
font-size: 16px !important;
font-size: calc(16px * var(--wekan-ui-font-scale, 1)) !important;
margin-inline-end: 0 !important;
}
/* No auto margin: everything in this bar packs from the START and wraps
@@ -1554,7 +1577,7 @@ body.mobile-mode #header-quick-access .allBoards {
.iphone-device #header-quick-access .home-icon .fa-home,
.iphone-device .wrapper ~ #header-quick-access .home-icon .fa-home,
.iphone-device body:not(.board-view) #header-quick-access .home-icon .fa-home {
font-size: 16px !important;
font-size: calc(16px * var(--wekan-ui-font-scale, 1)) !important;
margin-inline-end: 0 !important;
padding: 0 !important;
}
@@ -1731,7 +1754,7 @@ body.mobile-mode #header-quick-access .allBoards {
padding: 0 !important;
min-width: 44px !important;
height: 44px !important;
line-height: 44px !important;
line-height: calc(44px * var(--wekan-ui-font-scale, 1)) !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
Loaded 100 of 1553 files, more files were not shown because too many files have changed in this diff. Show more